ms-obj.c 34.7 KB
Newer Older
1 2 3
/* vim: set sw=8: */

/*
4 5
 * ms-obj.c: MS Excel Object support for Gnumeric
 *
6 7 8 9 10 11
 * Authors:
 *    Jody Goldberg (jody@gnome.org)
 *    Michael Meeks (michael@ximian.com)
 *
 * (C) 1998-2001 Michael Meeks
 * (C) 2002-2005 Jody Goldberg
12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
22
 *
23 24
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
26 27
 * USA
 */
28

29 30
#include <gnumeric-config.h>
#include <gnumeric.h>
31
#include <string.h>
32

33
#include "boot.h"
34
#include "ms-obj.h"
Michael Meeks's avatar
Michael Meeks committed
35
#include "ms-chart.h"
36
#include "ms-escher.h"
37 38 39 40 41 42

#include <expr.h>
#include <parse-util.h>
#include <sheet-object-widget.h>

#include <gsf/gsf-utils.h>
43
#include <stdio.h>
44 45 46

#define GR_END                0x00
#define GR_MACRO              0x04
47
#define GR_COMMAND_BUTTON     0x05
48
#define GR_GROUP	      0x06
49 50 51
#define GR_CLIPBOARD_FORMAT   0x07
#define GR_PICTURE_OPTIONS    0x08
#define GR_PICTURE_FORMULA    0x09
52 53 54 55 56 57 58 59 60 61 62
#define GR_CHECKBOX_LINK      0x0A
#define GR_RADIO_BUTTON       0x0B
#define GR_SCROLLBAR          0x0C
#define GR_NOTE_STRUCTURE     0x0D
#define GR_SCROLLBAR_FORMULA  0x0E
#define GR_GROUP_BOX_DATA     0x0F
#define GR_EDIT_CONTROL_DATA  0x10
#define GR_RADIO_BUTTON_DATA  0x11
#define GR_CHECKBOX_DATA      0x12
#define GR_LISTBOX_DATA       0x13
#define GR_CHECKBOX_FORMULA   0x14
63 64
#define GR_COMMON_OBJ_DATA    0x15

65
MSObjAttr *
66
ms_obj_attr_new_flag (MSObjAttrID id)
67 68 69
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

70 71
	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == 0, NULL);

72 73 74 75 76 77 78
	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
	res->v.v_ptr = NULL;
	return res;
}

MSObjAttr *
79
ms_obj_attr_new_uint (MSObjAttrID id, guint32 val)
80 81 82
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

83
	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_INT_MASK, NULL);
84 85 86

	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
87
	res->v.v_uint = val;
88 89 90
	return res;
}
MSObjAttr *
91
ms_obj_attr_new_ptr (MSObjAttrID id, gpointer val)
92 93 94
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

95
	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_PTR_MASK, NULL);
96 97 98 99 100 101 102

	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
	res->v.v_ptr = val;
	return res;
}

103
MSObjAttr *
104
ms_obj_attr_new_array (MSObjAttrID id, GArray *array)
105 106 107 108 109 110 111 112 113 114 115
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_GARRAY_MASK, NULL);

	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
	res->v.v_array = array;
	return res;
}

116
MSObjAttr *
117
ms_obj_attr_new_expr (MSObjAttrID id, GnmExpr const *expr)
118 119 120
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

121
	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_EXPR_MASK, NULL);
122 123 124 125 126 127 128

	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
	res->v.v_expr = expr;
	return res;
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142
MSObjAttr *
ms_obj_attr_new_markup (MSObjAttrID id, PangoAttrList *markup)
{
	MSObjAttr *res = g_new (MSObjAttr, 1);

	g_return_val_if_fail ((id & MS_OBJ_ATTR_MASK) == MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK, NULL);

	/* be anal about constness */
	*((MSObjAttrID *)&(res->id)) = id;
	res->v.v_markup = markup;
	pango_attr_list_ref (markup);
	return res;
}

143
guint32
144
ms_obj_attr_get_uint (MSObjAttrBag *attrs, MSObjAttrID id, guint32 default_value)
145 146 147
{
	MSObjAttr *attr;

148
	g_return_val_if_fail (attrs != NULL, default_value);
149 150
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_INT_MASK, default_value);

151
	attr = ms_obj_attr_bag_lookup (attrs, id);
152 153 154 155 156 157
	if (attr == NULL)
		return default_value;
	return attr->v.v_uint;
}

gint32
158
ms_obj_attr_get_int  (MSObjAttrBag *attrs, MSObjAttrID id, gint32 default_value)
159 160 161
{
	MSObjAttr *attr;

162
	g_return_val_if_fail (attrs != NULL, default_value);
163 164
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_INT_MASK, default_value);

165
	attr = ms_obj_attr_bag_lookup (attrs, id);
166 167 168 169 170 171
	if (attr == NULL)
		return default_value;
	return attr->v.v_int;
}

gpointer
172
ms_obj_attr_get_ptr  (MSObjAttrBag *attrs, MSObjAttrID id, gpointer default_value)
173 174 175
{
	MSObjAttr *attr;

176
	g_return_val_if_fail (attrs != NULL, default_value);
177 178
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_PTR_MASK, default_value);

179
	attr = ms_obj_attr_bag_lookup (attrs, id);
180 181 182 183 184
	if (attr == NULL)
		return default_value;
	return attr->v.v_ptr;
}

185
GArray *
186
ms_obj_attr_get_array (MSObjAttrBag *attrs, MSObjAttrID id, GArray *default_value)
187 188 189
{
	MSObjAttr *attr;

190
	g_return_val_if_fail (attrs != NULL, default_value);
191 192
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_GARRAY_MASK, default_value);

193
	attr = ms_obj_attr_bag_lookup (attrs, id);
194 195 196 197 198
	if (attr == NULL)
		return default_value;
	return attr->v.v_array;
}

199
GnmExpr const *
200
ms_obj_attr_get_expr (MSObjAttrBag *attrs, MSObjAttrID id, GnmExpr const *default_value)
201 202 203
{
	MSObjAttr *attr;

204
	g_return_val_if_fail (attrs != NULL, default_value);
205 206
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_EXPR_MASK, default_value);

207
	attr = ms_obj_attr_bag_lookup (attrs, id);
208 209 210 211 212
	if (attr == NULL)
		return default_value;
	return attr->v.v_expr;
}

213
PangoAttrList *
214
ms_obj_attr_get_markup (MSObjAttrBag *attrs, MSObjAttrID id, PangoAttrList *default_value)
215 216 217
{
	MSObjAttr *attr;

218
	g_return_val_if_fail (attrs != NULL, default_value);
219 220
	g_return_val_if_fail (id & MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK, default_value);

221
	attr = ms_obj_attr_bag_lookup (attrs, id);
222 223 224 225 226
	if (attr == NULL)
		return default_value;
	return attr->v.v_markup;
}

227
static void
228
ms_obj_attr_destroy (MSObjAttr *attr)
229 230
{
	if (attr != NULL) {
231
		if ((attr->id & MS_OBJ_ATTR_IS_PTR_MASK) &&
232 233
		    attr->v.v_ptr != NULL) {
			g_free (attr->v.v_ptr);
234 235 236 237 238
			attr->v.v_ptr = NULL;
		} else if ((attr->id & MS_OBJ_ATTR_IS_GARRAY_MASK) &&
			   attr->v.v_array != NULL) {
			g_array_free (attr->v.v_array, TRUE);
			attr->v.v_array = NULL;
239
		} else if ((attr->id & MS_OBJ_ATTR_IS_EXPR_MASK) &&
240
			   attr->v.v_expr != NULL) {
241
			gnm_expr_unref (attr->v.v_expr);
242
			attr->v.v_expr = NULL;
243 244 245 246
		} else if ((attr->id & MS_OBJ_ATTR_IS_PANGO_ATTR_LIST_MASK) &&
			   attr->v.v_markup != NULL) {
			pango_attr_list_unref (attr->v.v_markup);
			attr->v.v_markup = NULL;
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
		}
		g_free (attr);
	}
}

static guint
cb_ms_obj_attr_hash (gconstpointer key)
{
	return ((MSObjAttr const *)key)->id;
}

static gint
cb_ms_obj_attr_cmp (gconstpointer a, gconstpointer b)
{
	return ((MSObjAttr const *)a)->id == ((MSObjAttr const *)b)->id;
}

MSObjAttrBag *
265
ms_obj_attr_bag_new (void)
266 267 268 269 270
{
	return g_hash_table_new (cb_ms_obj_attr_hash, cb_ms_obj_attr_cmp);
}

static void
271
cb_ms_obj_attr_destroy (gpointer key, gpointer value, gpointer ignored)
272
{
273
	ms_obj_attr_destroy (value);
274 275
}
void
276
ms_obj_attr_bag_destroy (MSObjAttrBag *attrs)
277 278
{
	if (attrs != NULL) {
279
		g_hash_table_foreach (attrs, cb_ms_obj_attr_destroy, NULL);
280 281 282 283 284 285
		g_hash_table_destroy (attrs);
	}
}


void
286
ms_obj_attr_bag_insert (MSObjAttrBag *attrs, MSObjAttr *attr)
287 288 289 290 291 292
{
	g_return_if_fail (!g_hash_table_lookup (attrs, attr));
	g_hash_table_insert (attrs, attr, attr);
}

MSObjAttr *
293
ms_obj_attr_bag_lookup (MSObjAttrBag *attrs, MSObjAttrID id)
294
{
Jody Goldberg's avatar
Jody Goldberg committed
295
	if (attrs != NULL) {
296
		MSObjAttr attr = {0, {0}};
Jody Goldberg's avatar
Jody Goldberg committed
297 298 299 300
		*((MSObjAttrID *)&(attr.id)) = id;
		return g_hash_table_lookup (attrs, &attr);
	}
	return NULL;
301 302 303 304
}

/********************************************************************************/

305 306 307 308 309 310 311 312 313 314
MSObj *
ms_obj_new (MSObjAttrBag *attrs)
{
	MSObj *obj = g_new0 (MSObj, 1);

	obj->excel_type = (unsigned)-1; /* Set to undefined */
	obj->excel_type_name = NULL;
	obj->id = -1;
	obj->gnum_obj = NULL;
	obj->attrs = (attrs != NULL) ? attrs : ms_obj_attr_bag_new ();
315
	obj->combo_in_autofilter	= FALSE;
316
	obj->is_linked			= FALSE;
317
	obj->comment_pos.col = obj->comment_pos.row = -1;
318 319 320 321

	return obj;
}

322 323
void
ms_obj_delete (MSObj *obj)
324
{
Morten Welinder's avatar
Morten Welinder committed
325 326
	if (obj) {
		if (obj->gnum_obj) {
Jody Goldberg's avatar
Jody Goldberg committed
327
			g_object_unref (obj->gnum_obj);
328
			obj->gnum_obj = NULL;
Morten Welinder's avatar
Morten Welinder committed
329
		}
330
		if (obj->attrs) {
331
			ms_obj_attr_bag_destroy (obj->attrs);
332 333
			obj->attrs = NULL;
		}
334
		g_free (obj);
Morten Welinder's avatar
Morten Welinder committed
335
	}
336
}
337

338
char *
339
ms_read_TXO (BiffQuery *q, MSContainer *c, PangoAttrList **markup)
340
{
341
	static char const * const orientations [] = {
342 343 344 345
		"Left to right",
		"Top to Bottom",
		"Bottom to Top on Side",
		"Top to Bottom on Side"
346
	};
347
	static char const * const haligns [] = {
348 349
		"At left", "Horizontaly centered",
		"At right", "Horizontaly justified"
350
	};
351
	static char const * const valigns [] = {
352 353
		"At top", "Verticaly centered",
		"At bottom", "Verticaly justified"
354
	};
355

356 357
	guint16 const options     = GSF_LE_GET_GUINT16 (q->data);
	guint16 const orient      = GSF_LE_GET_GUINT16 (q->data + 2);
358
	guint16	      text_len    = GSF_LE_GET_GUINT16 (q->data + 10);
359
/*	guint16 const num_formats = GSF_LE_GET_GUINT16 (q->data + 12);*/
360 361
	int const halign = (options >> 1) & 0x7;
	int const valign = (options >> 4) & 0x7;
362
	char         *text;
363
	guint16       op;
364

365
	*markup = NULL;
366 367 368
	if (text_len == 0)
		return NULL;

369 370 371
	g_return_val_if_fail (orient <= 3, NULL);
	g_return_val_if_fail (1 <= halign && halign <= 4, NULL);
	g_return_val_if_fail (1 <= valign && valign <= 4, NULL);
372

373
	if (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
374 375
		gboolean use_utf16;

376
		ms_biff_query_next (q);
Jody Goldberg's avatar
Jody Goldberg committed
377

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
		use_utf16 = q->data[0] != 0;
		text = excel_get_chars (c->importer,
			q->data + 1, MIN (text_len, q->length-1), use_utf16);
		if (q->length < text_len) {
			GString *accum = g_string_new (text);
			g_free (text);
			text_len -= q->length - 1;
			while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
				ms_biff_query_next (q);
				text = excel_get_chars (c->importer, q->data,
					MIN (q->length, text_len), use_utf16);
				g_string_append (accum, text);
				g_free (text);
				if (text_len <= q->length)
					break;
				text_len -= q->length;
			}
			text = g_string_free (accum, FALSE);
		}
397

398
		if (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
399
			ms_biff_query_next (q);
400
			*markup = ms_container_read_markup (c, q->data, q->length, text);
401 402
		} else
			g_warning ("Unusual, TXO text with no formatting has 0x%x @ 0x%x", op, q->streamPos);
Jody Goldberg's avatar
Jody Goldberg committed
403 404 405 406 407
	} else {
		if (text_len > 0)
			g_warning ("TXO len of %d but no continue", text_len);
		text = g_strdup ("");
	}
408

409
#ifndef NO_DEBUG_EXCEL
Jody Goldberg's avatar
Jody Goldberg committed
410
	if (ms_excel_object_debug > 0) {
Jody Goldberg's avatar
Jody Goldberg committed
411 412 413 414 415 416
		printf ("{ TextObject\n");
		printf ("Text '%s'\n", text);
		printf ("is %s, %s & %s;\n",
			orientations[orient], haligns[halign], valigns[valign]);
		printf ("}; /* TextObject */\n");
	}
417
#endif
418
	return text;
419 420
}

421 422
#ifndef NO_DEBUG_EXCEL
#define ms_obj_dump(data, len, data_left, name) ms_obj_dump_impl (data, len, data_left, name)
423
static void
424
ms_obj_dump_impl (guint8 const *data, int len, int data_left, char const *name)
425
{
Jody Goldberg's avatar
Jody Goldberg committed
426
	if (ms_excel_object_debug < 2)
427 428 429
		return;

	printf ("{ %s \n", name);
430 431 432 433 434
	if (len+4 > data_left) {
		printf ("/* invalid length %d (0x%x) > %d(0x%x)*/\n",
			len+4, len+4, data_left, data_left);
		len = data_left - 4;
	}
435
	if (ms_excel_object_debug > 2)
436
		gsf_mem_dump (data, len+4);
437 438
	printf ("}; /* %s */\n", name);
}
439 440 441
#else
#define ms_obj_dump (data, len, data_left, name)
#endif
442

443
static gboolean
444 445 446
read_pre_biff8_read_text (BiffQuery *q, MSContainer *c, MSObj *obj,
			  guint8 const *first,
			  unsigned len, unsigned txo_len)
447
{
448 449
	PangoAttrList *markup = NULL;
	GByteArray    *markup_data = NULL;
450
	char *str;
451 452
	unsigned remaining;
	guint16  op;
453

454 455
	if (first == NULL)
		return TRUE;
456

457
	remaining = q->data + q->length - first;
458

459 460 461 462 463 464 465 466
	/* CONTINUE handling here is very odd.
	 * If the text needs CONTINUEs but the markup does not, the markup is
	 * stored at the end of the OBJ rather than after the text.  */
	if (txo_len > 0 && txo_len < remaining) {
		markup_data = g_byte_array_new ();
		g_byte_array_append (markup_data, q->data + q->length - txo_len, txo_len);
		remaining -= txo_len;
	}
467

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
	str = excel_get_chars (c->importer, first, MIN (remaining, len), FALSE);
	if (len > remaining) {
		GString *accum = g_string_new (str);
		g_free (str);
		len -= remaining;
		while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
			ms_biff_query_next (q);
			str = excel_get_chars (c->importer, q->data,
				MIN (q->length, len), FALSE);
			g_string_append (accum, str);
			g_free (str);
			if (len < q->length)
				break;
			len -= q->length;
		}
		str = g_string_free (accum, FALSE);
		first = q->data + len;
	} else
		first += len;
	if (((first - q->data) & 1))
		first++; /* pad to word bound */
489

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
	ms_obj_attr_bag_insert (obj->attrs,
		ms_obj_attr_new_ptr (MS_OBJ_ATTR_TEXT, str));

	if (NULL != markup_data) {
		markup = ms_container_read_markup (c, markup_data->data, markup_data->len, str);
		g_byte_array_free (markup_data, TRUE);
	} else if (txo_len > 0) {
		remaining = q->data + q->length - first;
		if (txo_len > remaining) {
			GByteArray *accum = g_byte_array_new ();
			g_byte_array_append (accum, first, remaining);
			txo_len -= remaining;
			while (ms_biff_query_peek_next (q, &op) && op == BIFF_CONTINUE) {
				ms_biff_query_next (q);
				g_byte_array_append (accum, q->data, MIN (q->length, txo_len));
				if (txo_len <= q->length)
					break;
				txo_len -= q->length;
			}
			first = q->data + txo_len;
			markup = ms_container_read_markup (c, accum->data, accum->len, str);
			g_byte_array_free (accum, TRUE);
		} else {
			markup = ms_container_read_markup (c, first, txo_len, str);
			first += txo_len;
		}
	}
	if (NULL != markup) {
518 519 520 521 522
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_markup (MS_OBJ_ATTR_MARKUP, markup));
		pango_attr_list_unref (markup);
	}

523 524
	return FALSE;
}
525 526

static gboolean
527 528
read_pre_biff8_read_expr (BiffQuery *q, MSContainer *c, MSObj *obj,
			  guint8 const **first, unsigned total_len) /* including extras */
529
{
530 531 532 533 534 535 536 537 538 539 540 541
	guint8 const *ptr  = *first;
	guint8 const *last = q->data + q->length;
	GnmExpr const *ref;
	unsigned len;

	if (total_len <= 0)
		return FALSE;

	g_return_val_if_fail (ptr + 2 <= last, TRUE);

	len = GSF_LE_GET_GUINT16 (ptr);

542
	g_return_val_if_fail (ptr + 6 + len <= last, TRUE);
543

544
	ref = ms_container_parse_expr (c, ptr + 6, len);
545 546 547
	if (ref != NULL)
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
548 549 550 551 552

	*first = ptr + total_len;
	if (((*first - q->data) & 1))
		(*first)++; /* pad to word bound */

553 554 555
	return FALSE;
}

556
static guint8 const *
557
read_pre_biff8_read_name_and_fmla (BiffQuery *q, MSContainer *c, MSObj *obj,
558 559 560 561 562
				   gboolean has_name, unsigned offset)
{
	guint8 const *data = q->data + offset;
	gboolean const fmla_len = GSF_LE_GET_GUINT16 (q->data+26);

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
	if (has_name) {
		guint8 const *last = q->data + q->length;
		unsigned len = *data++;
		char *str;

		g_return_val_if_fail (data + len <= last, NULL);

		str = excel_get_chars (c->importer, data, len, FALSE);
		data += len;
		if (((data - q->data) & 1))
			data++; /* pad to word bound */

		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_ptr (MS_OBJ_ATTR_OBJ_NAME, str));
	}
	if (read_pre_biff8_read_expr (q, c, obj, &data, fmla_len))
579 580 581 582 583 584 585 586
		return NULL;
	return data;
}

static gboolean
ms_obj_read_pre_biff8_obj (BiffQuery *q, MSContainer *c, MSObj *obj)
{
	guint8 const *last = q->data + q->length;
587
	guint16 peek_op, tmp, len;
588
	unsigned txo_len, if_empty;
Jody Goldberg's avatar
Jody Goldberg committed
589
	guint8 const *data;
590
	gboolean const has_name = GSF_LE_GET_GUINT16 (q->data+30) != 0; /* undocumented */
591

592
#if 0
593
	guint16 const flags = GSF_LE_GET_GUINT16(q->data+8);
594
#endif
595 596
	guint8 *anchor = g_malloc (MS_ANCHOR_SIZE);
	memcpy (anchor, q->data+8, MS_ANCHOR_SIZE);
597 598
	ms_obj_attr_bag_insert (obj->attrs,
		ms_obj_attr_new_ptr (MS_OBJ_ATTR_ANCHOR, anchor));
599

600 601
	obj->excel_type = GSF_LE_GET_GUINT16(q->data + 4);
	obj->id         = GSF_LE_GET_GUINT32(q->data + 6);
602

603
	switch (obj->excel_type) {
Jody Goldberg's avatar
Jody Goldberg committed
604 605
	case 0: /* group */
		break;
606
	case 1: /* line */
607 608 609
		g_return_val_if_fail (q->data + 41 <= last, TRUE);
		tmp = GSF_LE_GET_GUINT8 (q->data+38) & 0x0F;
		if (tmp > 0)
Jody Goldberg's avatar
Jody Goldberg committed
610
			ms_obj_attr_bag_insert (obj->attrs,
611
				ms_obj_attr_new_uint (MS_OBJ_ATTR_ARROW_END, tmp));
Jody Goldberg's avatar
Jody Goldberg committed
612
		ms_obj_attr_bag_insert (obj->attrs,
613
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
Jody Goldberg's avatar
Jody Goldberg committed
614
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
615 616 617 618
		tmp = GSF_LE_GET_GUINT8 (q->data+35);
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_STYLE,
					      ((tmp == 0xff) ? 0 : tmp+1)));
Jody Goldberg's avatar
Jody Goldberg committed
619

620
		tmp = GSF_LE_GET_GUINT8 (q->data+40);
621
		if (tmp == 1 || tmp == 2)
Jody Goldberg's avatar
Jody Goldberg committed
622 623
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_H));
624
		if (tmp >= 2)
Jody Goldberg's avatar
Jody Goldberg committed
625 626
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_V));
627 628
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name,
			(obj->excel_type == 1) ? 42 : 44);
Jody Goldberg's avatar
Jody Goldberg committed
629
		break;
630

Jody Goldberg's avatar
Jody Goldberg committed
631 632 633 634
	case 2: /* rectangle */
	case 3: /* oval */
	case 4: /* arc */
	case 6: /* textbox */
635 636 637 638 639 640 641 642
		g_return_val_if_fail (q->data + 36 <= last, TRUE);
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_BACKGROUND,
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
		if (GSF_LE_GET_GUINT8 (q->data+36) == 0)
643 644
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_flag (MS_OBJ_ATTR_UNFILLED));
645

646 647 648 649
		tmp = GSF_LE_GET_GUINT8 (q->data+39);
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_STYLE,
					      ((tmp == 0xff) ? 0 : tmp+1)));
Jody Goldberg's avatar
Jody Goldberg committed
650 651 652
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
653 654
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_WIDTH,
655 656 657 658 659 660 661 662 663
					      GSF_LE_GET_GUINT8 (q->data+40) * 256));

		if (obj->excel_type == 6) {
			g_return_val_if_fail (q->data + 52 <= last, TRUE);
			len = GSF_LE_GET_GUINT16 (q->data + 44);
			txo_len = GSF_LE_GET_GUINT16 (q->data + 48);
			if_empty = GSF_LE_GET_GUINT16 (q->data + 50);

			data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
664
			if (read_pre_biff8_read_text (q, c, obj, data, len, txo_len))
665 666 667 668 669 670 671 672
				return TRUE;
			if (txo_len == 0)
				ms_obj_attr_bag_insert (obj->attrs,
					ms_obj_attr_new_markup (MS_OBJ_ATTR_MARKUP,
						ms_container_get_markup (c, if_empty)));
		} else
			data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 44);
		break;
673

674 675
	case 5: /* chart */
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 62);
Jody Goldberg's avatar
Jody Goldberg committed
676 677 678
		break;

	case 7: /* button */
679
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
Jody Goldberg's avatar
Jody Goldberg committed
680 681
		break;
	case 8: /* picture */
682 683
/* 50 uint16 cbPictFmla, 60 name len, name, fmla (respect cbMacro), fmla (cbPictFmla) */
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 60);
Jody Goldberg's avatar
Jody Goldberg committed
684
		break;
685

686 687
	case 9: /* polygon */
/* 66 name len, name, fmla (respect cbMacro) */
688 689
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
690
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
691 692
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
693 694
				0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));

695
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 66);
696

Jody Goldberg's avatar
Jody Goldberg committed
697 698 699 700 701
		if (ms_biff_query_peek_next (q, &peek_op) &&
		    peek_op == BIFF_COORDLIST) {
			unsigned i, n;
			guint tmp;
			GArray *array;
702

Jody Goldberg's avatar
Jody Goldberg committed
703 704 705 706 707 708 709 710 711 712 713 714 715
			ms_biff_query_next (q);
			n = q->length / 2;
			array = g_array_set_size (
				g_array_new (FALSE, FALSE, sizeof (double)), n + 2);

			for (i = 0; i < n ; i++) {
				tmp = GSF_LE_GET_GUINT16 (q->data + 2*i);
				g_array_index (array, double, i) = (double)tmp/ 16384.;
			}
			g_array_index (array, double, i)   = g_array_index (array, double, 0);
			g_array_index (array, double, i+1) = g_array_index (array, double, 1);
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_array (MS_OBJ_ATTR_POLYGON_COORDS, array));
716
		}
Jody Goldberg's avatar
Jody Goldberg committed
717 718 719
		break;

	case 0xB  : /* check box */
720
/* 76 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbtext, text */
Jody Goldberg's avatar
Jody Goldberg committed
721 722
		break;
	case 0xC  : /* option button */
723
/* 88 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbtext, text */
Jody Goldberg's avatar
Jody Goldberg committed
724 725
		break;
	case 0xD  : /* edit box */
726 727
/* 70 name len, name, fmla (respect cbMacro), cbtext, text */
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
Jody Goldberg's avatar
Jody Goldberg committed
728 729
		break;
	case 0xE  : /* label */
730 731 732
/* 70 name len, name, fmla (respect cbMacro), cbtext, text */
		len = GSF_LE_GET_GUINT16 (q->data + 44);
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
733
		if (read_pre_biff8_read_text (q, c, obj, data, len, 16))
734
			return TRUE;
Jody Goldberg's avatar
Jody Goldberg committed
735 736
		break;
	case 0xF  : /* dialog frame */
737 738
/* 70 name len, name, fmla (respect cbMacro) */
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 70);
Jody Goldberg's avatar
Jody Goldberg committed
739
		break;
740 741
	case 0x10 : /* spinner & scrollbar (layout is the same) */
	case 0x11 :
742
/* 68 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2 */
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
				GSF_LE_GET_GUINT16 (q->data+48)));
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
				GSF_LE_GET_GUINT16 (q->data+50)));
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
				GSF_LE_GET_GUINT16 (q->data+52)));
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
				GSF_LE_GET_GUINT16 (q->data+54)));
		ms_obj_attr_bag_insert (obj->attrs,
			ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
				GSF_LE_GET_GUINT16 (q->data+56)));

		{
			GnmExpr const *ref;
			guint16 len;
			guint8 const *last = q->data + q->length;
			guint8 const *ptr = q->data + 64;

			ptr += 1 + *ptr;		/* object name */
			if ((ptr - q->data) & 1) ptr++;	/* align on word */
			if (ptr >= last) break;

			ptr += 2 + GSF_LE_GET_GUINT16 (ptr); /* the macro */
			if ((ptr - q->data) & 1) ptr++;	/* align on word */
			if (ptr >= last) break;

			len = GSF_LE_GET_GUINT16 (ptr+2); /* the assigned macro */
774
			ref = ms_container_parse_expr (c, ptr + 8, len);
775 776
			if (ref != NULL)
				ms_obj_attr_bag_insert (obj->attrs,
777
					ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
778
		}
Jody Goldberg's avatar
Jody Goldberg committed
779 780
		break;
	case 0x12 : /* list box */
781
/* 88 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbfmla3, fmla3 */
Jody Goldberg's avatar
Jody Goldberg committed
782 783
		break;
	case 0x13 : /* group box */
784 785
/* 82 name len, name, fmla (respect cbMacro), cbtext, text */
		data = read_pre_biff8_read_name_and_fmla (q, c, obj, has_name, 82);
Jody Goldberg's avatar
Jody Goldberg committed
786 787
		break;
	case 0x14 : /* drop down */
788
/* 110 name len, name, cbfmla1 (IGNORE cbMacro), fmla1, cbfmla2, fmla2, cbfmla3, fmla3 */
789
		obj->combo_in_autofilter =
790
			(GSF_LE_GET_GUINT16 (q->data + 8) & 0x8000) ? TRUE : FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
791
		break;
792
	default :
793
		break;
794
	}
795

796 797 798
	if (obj->excel_type == 8) { /* picture */
		guint16 op;
		if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
799 800
			GdkPixbuf *pixbuf; 

801
			ms_biff_query_next (q);
802 803 804
			pixbuf = excel_read_IMDATA (q, FALSE);
			if (pixbuf)
				g_object_unref (pixbuf);
805 806
		}
	}
807
	return FALSE;
808 809
}

810 811

static void
812
ms_obj_map_forms_obj (MSObj *obj, MSContainer *c, guint8 const *data, gint32 len)
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
{
	static struct {
		char const	*key;
		unsigned	 excel_type;
		unsigned	 offset_to_link;
	} const map_forms [] = {
		{ "ScrollBar",		0x11, 16 },
		{ "CheckBox",		0x0B, 17 },
		{ "TextBox",		0x06 },
		{ "CommandButton",	0x07 },
		{ "OptionButton",	0x0C, 17 },
		{ "ListBox",		0x12, 16 },
		{ "ComboBox",		0x14, 17 },
		{ "ToggleButton",	0x70, 17 },
		{ "SpinButton",		0x10, 17 },
		{ "Label",		0x0E },
		{ "Image",		0x08 }
	};
	int i = G_N_ELEMENTS (map_forms);
	char const *key;
833
	int key_len = 0;
834 835

	if (obj->excel_type != 8 || len <= 27 ||
836
	    strncmp ((char *)(data+21), "Forms.", 6))
837 838 839 840 841 842 843
		return;

	while (i-- > 0) {
		key	= map_forms[i].key;
		key_len = strlen (key);
		if (map_forms [i].excel_type > 0 &&
		    len >= (27+key_len) &&
844
		    0 == strncmp ((char *)(data+27), map_forms [i].key, key_len))
845 846 847 848 849 850 851 852 853 854 855 856
			break;
	}

	if (i >= 0) {
		obj->excel_type = map_forms [i].excel_type;
		if (map_forms [i].offset_to_link != 0) {
			guint8 const *ptr = data + 27 + key_len + map_forms [i].offset_to_link;
			guint16 expr_len;
			GnmExpr const *ref;
			g_return_if_fail (ptr + 2 <= (data + len));
			expr_len = GSF_LE_GET_GUINT16 (ptr);
			g_return_if_fail (ptr + 2 + expr_len <= (data + len));
857
			ref = ms_container_parse_expr (c, ptr + 6, expr_len);
858 859 860 861 862 863
			if (ref != NULL)
				ms_obj_attr_bag_insert (obj->attrs,
					ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
		}
	}
}
864

865
static gboolean
866
ms_obj_read_biff8_obj (BiffQuery *q, MSContainer *c, MSObj *obj)
867 868 869
{
	guint8 *data;
	gint32 data_len_left;
870
	gboolean hit_end = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
871
	gboolean next_biff_record_maybe_imdata = FALSE;
872

873
	g_return_val_if_fail (q, TRUE);
874
	g_return_val_if_fail (q->opcode == BIFF_OBJ, TRUE);
875 876 877 878

	data = q->data;
	data_len_left = q->length;

879
#if 0
880
	ms_biff_query_dump (q);
881 882
#endif

883
	/* Scan through the pseudo BIFF substream */
884
	while (data_len_left > 0 && !hit_end) {
885
		guint16 const record_type = GSF_LE_GET_GUINT16(data);
886

887 888 889 890 891
		/* All the sub-records seem to have this layout
		 * 2001/Mar/29 JEG : liars.  Ok not all records have this
		 * layout.  Create a list box.  It seems to do something
		 * unique.  It acts like an end, and has no length specified.
		 */
892
		guint16 len = GSF_LE_GET_GUINT16(data+2);
893 894

		/* 1st record must be COMMON_OBJ*/
895 896 897
		g_return_val_if_fail (obj->excel_type >= 0 ||
				      record_type == GR_COMMON_OBJ_DATA,
				      TRUE);
898 899

		switch (record_type) {
900
		case GR_END:
901
			g_return_val_if_fail (len == 0, TRUE);
902
			/* ms_obj_dump (data, len, data_len_left, "ObjEnd"); */
903
			hit_end = TRUE;
904
			break;
905 906

		case GR_MACRO :
907
			ms_obj_dump (data, len, data_len_left, "MacroObject");
908 909 910
			break;

		case GR_COMMAND_BUTTON :
911
			ms_obj_dump (data, len, data_len_left, "CommandButton");
912
			break;
913

914 915
		case GR_GROUP :
			ms_obj_dump (data, len, data_len_left, "Group");
916 917
			break;

Jody Goldberg's avatar
Jody Goldberg committed
918
		case GR_CLIPBOARD_FORMAT :
919
			ms_obj_dump (data, len, data_len_left, "ClipboardFmt");
920
			break;
921

Jody Goldberg's avatar
Jody Goldberg committed
922
		case GR_PICTURE_OPTIONS :
923 924
			if (len == 2) {
				guint16 opt = GSF_LE_GET_GUINT16 (data + 4);
925

926
				obj->is_linked = (opt & 0x2) ? TRUE : FALSE;
927
#ifndef NO_DEBUG_EXCEL
928 929 930 931 932
				if (ms_excel_object_debug >= 1) {
					printf ("{ /* PictOpt */\n");
					printf ("value = %x;\n", opt);
					printf ("}; /* PictOpt */\n");
				}
933
#endif
934 935 936 937
			} else {
				/* no docs on this so be careful */
				g_warning ("PictOpt record with size other than 2");
			}
938

Jody Goldberg's avatar
Jody Goldberg committed
939
			next_biff_record_maybe_imdata = TRUE;
940
			break;
941

Jody Goldberg's avatar
Jody Goldberg committed
942
		case GR_PICTURE_FORMULA :
943
			/* Check for form objects stored here for no apparent reason */
944
			ms_obj_map_forms_obj (obj, c, data, len);
945
			break;
946

947
		case GR_CHECKBOX_LINK :
948
			ms_obj_dump (data, len, data_len_left, "CheckboxLink");
949
			break;
950

951
		case GR_RADIO_BUTTON :
952
			ms_obj_dump (data, len, data_len_left, "RadioButton");
953
			break;
954

955
		case GR_SCROLLBAR :
956 957
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
958
					GSF_LE_GET_GUINT16 (data+8)));
959 960
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
961
					GSF_LE_GET_GUINT16 (data+10)));
962 963
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
964
					GSF_LE_GET_GUINT16 (data+12)));
965 966
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
967
					GSF_LE_GET_GUINT16 (data+14)));
968 969
			ms_obj_attr_bag_insert (obj->attrs,
				ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
970
					GSF_LE_GET_GUINT16 (data+16)));
971
			ms_obj_dump (data, len, data_len_left, "ScrollBar");
972
			break;
973

974
		case GR_NOTE_STRUCTURE :
975
			ms_obj_dump (data, len, data_len_left, "Note");
976
			break;
977

978
		case GR_SCROLLBAR_FORMULA : {
979
			guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
980
			GnmExpr const *ref = ms_container_parse_expr (c, data+10, expr_len);
981
			if (ref != NULL)
982
				ms_obj_attr_bag_insert (obj->attrs,
983
					ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
984
			ms_obj_dump (data, len, data_len_left, "ScrollbarFmla");
985
			break;
986
		}
987

988
		case GR_GROUP_BOX_DATA :
989
			ms_obj_dump (data, len, data_len_left, "GroupBoxData");
990
			break;
991

992
		case GR_EDIT_CONTROL_DATA :
993
			ms_obj_dump (data, len, data_len_left, "EditCtrlData");
994
			break;
995

996
		case GR_RADIO_BUTTON_DATA :
997
			ms_obj_dump (data, len, data_len_left, "RadioData");
998
			break;
999

1000
		case GR_CHECKBOX_DATA :
1001
			ms_obj_dump (data, len, data_len_left, "CheckBoxData");
1002
			break;
1003

1004
		case GR_LISTBOX_DATA : {
1005 1006 1007 1008 1009 1010 1011 1012
			/* FIXME : find some docs for this
			 * It seems as if list box data does not conform to
			 * the docs.  It acts like an end and has no size.
			 */
			hit_end = TRUE;
			len = data_len_left - 4;

			ms_obj_dump (data, len, data_len_left, "ListBoxData");
1013
			break;
1014
		}
1015

1016
		case GR_CHECKBOX_FORMULA : {
1017
			guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
1018
			GnmExpr const *ref = ms_container_parse_expr (c, data+10, expr_len);
1019
			if (ref != NULL)
1020
				ms_obj_attr_bag_insert (obj->attrs,
1021
					ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
1022
			ms_obj_dump (data, len, data_len_left, "CheckBoxFmla");
1023
			break;
1024
		}
1025

1026
		case GR_COMMON_OBJ_DATA : {
1027
			guint16 const options =GSF_LE_GET_GUINT16 (data+8);
1028 1029

			/* Multiple objects in 1 record ?? */
1030
			g_return_val_if_fail (obj->excel_type == -1, TRUE);
1031

1032 1033
			obj->excel_type = GSF_LE_GET_GUINT16(data+4);
			obj->id = GSF_LE_GET_GUINT16(data+6);
1034

1035 1036 1037
			/* Undocumented.  It appears that combos for filters are marked
			 * with flag 0x100
			 */
1038
			obj->combo_in_autofilter =
1039 1040
				(obj->excel_type == 0x14) && (options & 0x100);

1041
#ifndef NO_DEBUG_EXCEL
1042
			/* only print when debug is enabled */
Jody Goldberg's avatar
Jody Goldberg committed
1043
			if (ms_excel_object_debug == 0)
1044 1045
				break;

1046
			printf ("OBJECT TYPE = %d\n", obj->excel_type);
1047
			if (options&0x0001)
1048
				printf ("Locked;\n");
1049
			if (options&0x0010)
1050
				printf ("Printable;\n");
1051
			if (options&0x2000)
1052
				printf ("AutoFilled;\n");
1053
			if (options&0x4000)
1054
				printf ("AutoLines;\n");
1055

1056 1057 1058
			if (ms_excel_object_debug > 4) {
				/* According to the docs this should not fail
				 * but there appears to be a flag at 0x200 for
1059 1060
				 * scrollbars and 0x100 for combos associated
				 * with filters.
1061 1062 1063 1064 1065
				 */
				if ((options & 0x9fee) != 0)
					printf ("WARNING : Why is option not 0 (%x)\n",
						options & 0x9fee);
			}
1066
#endif
1067
		}
1068 1069
		break;

1070
		default:
1071
			printf ("ERROR : Unknown Obj record 0x%x len 0x%x dll %d;\n",
1072
				record_type, len, data_len_left);
1073
		}
1074 1075 1076

		if (data_len_left < len+4)
			printf ("record len %d (0x%x) > %d\n", len+4, len+4, data_len_left);
1077 1078

		/* FIXME : We need a structure akin to the escher code to do this properly */
1079
		for (data_len_left -= len+4; data_len_left < 0; ) {
1080 1081
			guint16 peek_op;

1082 1083
			printf ("deficit of %d\n", data_len_left);

1084 1085 1086 1087
			/* FIXME : what do we expect here ??
			 * I've seen what seem to be embedded drawings
			 * but I am not sure what is embedding what.
			 */
1088 1089 1090 1091 1092 1093 1094 1095 1096
			if (!ms_biff_query_peek_next (q, &peek_op) ||
			    (peek_op != BIFF_CONTINUE &&
			     peek_op != BIFF_MS_O_DRAWING &&
			     peek_op != BIFF_TXO &&
			     peek_op != BIFF_OBJ)) {
				printf ("0x%x vs 0x%x\n", q->opcode, peek_op);
				return TRUE;
			}

1097 1098
			ms_biff_query_next (q);
			data_len_left += q->length;
1099
			printf ("merged in 0x%x with len %d\n", q->opcode, q->length);
1100
		}
1101
		data = q->data + q->length - data_len_left;
1102 1103 1104
	}

	/* The ftEnd record should have been the last */
Jody Goldberg's avatar
Jody Goldberg committed
1105
	if (data_len_left > 0) {
Jody Goldberg's avatar
Jody Goldberg committed
1106
		printf("OBJ : unexpected extra data after Object End record;\n");
1107
		gsf_mem_dump (data, data_len_left);
Jody Goldberg's avatar
Jody Goldberg committed
1108
		return TRUE;
1109
	}
Jody Goldberg's avatar
Jody Goldberg committed
1110 1111

	/* Catch underflow too */
1112
	g_return_val_if_fail (data_len_left == 0, TRUE);
1113

Jody Goldberg's avatar
Jody Goldberg committed
1114 1115
	/* FIXME : Throw away the IMDATA that may follow.
	 * I am not sure when the IMDATA does follow, or how to display it,
1116
	 * but very careful in case it is not there. */
Jody Goldberg's avatar
Jody Goldberg committed
1117
	if (next_biff_record_maybe_imdata) {
1118
		guint16 op;
1119

1120
		if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
1121 1122
			GdkPixbuf *pixbuf; 

1123
			printf ("Reading trailing IMDATA;\n");
1124
			ms_biff_query_next (q);
1125 1126 1127
			pixbuf = excel_read_IMDATA (q, FALSE);
			if (pixbuf)
				g_object_unref (pixbuf);
Jody Goldberg's avatar
Jody Goldberg committed
1128
		}
1129 1130
	}

1131
	return FALSE;
1132 1133
}

1134 1135 1136
/**
 * ms_read_OBJ :
 * @q : The biff record to start with.
1137
 * @c : The object's container
1138 1139 1140
 * @attrs : an OPTIONAL hash of object attributes.
 */
void
1141
ms_read_OBJ (BiffQuery *q, MSContainer *c, MSObjAttrBag *attrs)
1142
{
1143
	static char const * const object_type_names[] = {
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
		"Group", 	/* 0x00 */
		"Line",		/* 0x01 */
		"Rectangle",	/* 0x02 */
		"Oval",		/* 0x03 */
		"Arc",		/* 0x04 */
		"Chart",	/* 0x05 */
		"TextBox",	/* 0x06 */
		"Button",	/* 0x07 */
		"Picture",	/* 0x08 */
		"Polygon",	/* 0x09 */
		NULL,		/* 0x0A */
		"CheckBox",	/* 0x0B */
		"Option",	/* 0x0C */
		"Edit",		/* 0x0D */
		"Label",	/* 0x0E */
		"Dialog",	/* 0x0F */
		"Spinner",	/* 0x10 */
		"Scroll",	/* 0x11 */
		"List",		/* 0x12 */
		"Group",	/* 0x13 */
		"Combo",	/* 0x14 */
		NULL, NULL, NULL, NULL, /* 0x15 - 0x18 */
		"Comment",	/* 0x19 */
		NULL, NULL, NULL, NULL,	/* 0x1A - 0x1D */
		"MS Drawing"	/* 0x1E */
	};

	gboolean errors;
1172
	MSObj *obj = ms_obj_new (attrs);
1173

Jody Goldberg's avatar
Jody Goldberg committed
1174 1175 1176 1177
#ifndef NO_DEBUG_EXCEL
	if (ms_excel_object_debug > 0)
		printf ("{ /* OBJ start */\n");
#endif
1178 1179 1180
	errors = (c->importer->ver >= MS_BIFF_V8)
		? ms_obj_read_biff8_obj (q, c, obj)
		: ms_obj_read_pre_biff8_obj (q, c, obj);
1181 1182

	if (errors) {
Jody Goldberg's avatar
Jody Goldberg committed
1183 1184 1185 1186
#ifndef NO_DEBUG_EXCEL
		if (ms_excel_object_debug > 0)
			printf