Update Spanish translation
[gnumeric.git] / plugins / excel / ms-excel-read.c
blobd0cdac78abd4b52424c57cb22758ab31bf16a52e
1 /**
2 * ms-excel-read.c: MS Excel import
4 * Authors:
5 * Jody Goldberg (jody@gnome.org)
6 * Michael Meeks (michael@ximian.com)
8 * (C) 1998-2001 Michael Meeks
9 * (C) 2002-2008 Jody Goldberg
10 * (C) 2013-2013 Morten Welinder
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) version 3.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 * USA
26 **/
27 #include <gnumeric-config.h>
28 #include <gnumeric.h>
30 #include "boot.h"
31 #include "ms-formula-read.h"
32 #include "ms-excel-read.h"
33 #include "ms-obj.h"
34 #include "ms-chart.h"
35 #include "ms-escher.h"
36 #include "ms-excel-util.h"
37 #include "ms-excel-xf.h"
38 #include "formula-types.h"
40 #include <workbook.h>
41 #include <workbook-view.h>
42 #include <sheet.h>
43 #include <sheet-view.h>
44 #include <sheet-style.h>
45 #include <sheet-merge.h>
46 #include <sheet-filter.h>
47 #include <cell.h>
48 #include <func.h>
49 #include <style.h>
50 #include <style-conditions.h>
51 #include <style-font.h>
52 #include <gnm-format.h>
53 #include <print-info.h>
54 #include <selection.h>
55 #include <validation.h>
56 #include <input-msg.h>
57 #include <parse-util.h> /* for cell_name */
58 #include <ranges.h>
59 #include <expr.h>
60 #include <expr-name.h>
61 #include <value.h>
62 #include <hlink.h>
63 #include <application.h>
64 #include <command-context.h>
65 #include <sheet-object-cell-comment.h>
66 #include <sheet-object-widget.h>
67 #include <gnm-so-line.h>
68 #include <gnm-so-filled.h>
69 #include <gnm-so-polygon.h>
70 #include <sheet-object-graph.h>
71 #include <sheet-object-image.h>
72 #include <goffice/goffice.h>
74 #include <gsf/gsf-input.h>
75 #include <gsf/gsf-utils.h>
76 #include <gsf/gsf-msole-utils.h>
77 #include <glib/gi18n-lib.h>
78 #include <glib/gstdio.h>
79 #include <string.h>
80 #include <locale.h>
82 #undef G_LOG_DOMAIN
83 #define G_LOG_DOMAIN "gnumeric:read"
85 typedef struct {
86 ExcelReadSheet *esheet;
87 char *name;
88 guint32 streamStartPos;
89 unsigned index;
90 MsBiffFileType type;
91 GnmSheetType gnm_type;
92 GnmSheetVisibility visibility;
93 } BiffBoundsheetData;
95 #define N_BYTES_BETWEEN_PROGRESS_UPDATES 0x1000
96 #define BMP_HDR_SIZE 14
99 * Check whether the product of the first two arguments exceeds
100 * the third. The function should be overflow-proof.
102 static gboolean
103 product_gt (size_t count, size_t itemsize, size_t space)
105 return itemsize > 0 &&
106 (count > G_MAXUINT / itemsize || count * itemsize > space);
109 static void
110 record_size_barf (size_t count, size_t itemsize, size_t space,
111 const char *locus)
113 g_warning ("File is most likely corrupted.\n"
114 "(Requested %u*%u bytes, but only %u bytes left in record.\n"
115 "The problem occurred in %s.)",
116 (unsigned)count, (unsigned)itemsize,
117 (unsigned)space,
118 locus);
121 #define XL_NEED_BYTES(count) XL_NEED_ITEMS(count,1)
123 #define XL_NEED_ITEMS(count__,size__) \
124 do { \
125 size_t count_ = (count__); \
126 size_t size_ = (size__); \
127 size_t space_ = q->length - (data - q->data); \
128 if (G_UNLIKELY (product_gt (count_, size_, space_))) { \
129 record_size_barf (count_, size_, space_, G_STRFUNC); \
130 return; \
132 } while (0)
135 /* #define NO_DEBUG_EXCEL */
136 #ifndef NO_DEBUG_EXCEL
137 #define d(level, code) do { if (ms_excel_read_debug > level) { code } } while (0)
138 #else
139 #define d(level, code)
140 #endif
142 #define XL_GETROW(p) (GSF_LE_GET_GUINT16(p->data + 0))
143 #define XL_GETCOL(p) (GSF_LE_GET_GUINT16(p->data + 2))
145 char const *excel_builtin_formats[EXCEL_BUILTIN_FORMAT_LEN] = {
146 /* 0x00 */ "General",
147 /* 0x01 */ "0",
148 /* 0x02 */ "0.00",
149 /* 0x03 */ "#,##0",
150 /* 0x04 */ "#,##0.00",
151 /* 0x05 */ "$#,##0_);($#,##0)",
152 /* 0x06 */ "$#,##0_);[Red]($#,##0)",
153 /* 0x07 */ "$#,##0.00_);($#,##0.00)",
154 /* 0x08 */ "$#,##0.00_);[Red]($#,##0.00)",
155 /* 0x09 */ "0%",
156 /* 0x0a */ "0.00%",
157 /* 0x0b */ "0.00E+00",
158 /* 0x0c */ "# ?/?",
159 /* 0x0d */ "# ?" "?/?" "?", /* Don't accidentally use trigraph. */
160 /* 0x0e */ "m/d/yy",
161 /* 0x0f */ "d-mmm-yy",
162 /* 0x10 */ "d-mmm",
163 /* 0x11 */ "mmm-yy",
164 /* 0x12 */ "h:mm AM/PM",
165 /* 0x13 */ "h:mm:ss AM/PM",
166 /* 0x14 */ "h:mm",
167 /* 0x15 */ "h:mm:ss",
168 /* 0x16 */ "m/d/yy h:mm",
169 /* 0x17 */ NULL, /* 0x17-0x24 reserved for intl versions */
170 /* 0x18 */ NULL,
171 /* 0x19 */ NULL,
172 /* 0x1a */ NULL,
173 /* 0x1b */ NULL,
174 /* 0x1c */ NULL,
175 /* 0x1d */ NULL,
176 /* 0x1e */ NULL,
177 /* 0x1f */ NULL,
178 /* 0x20 */ NULL,
179 /* 0x21 */ NULL,
180 /* 0x22 */ NULL,
181 /* 0x23 */ NULL,
182 /* 0x24 */ NULL,
183 /* 0x25 */ "#,##0_);(#,##0)",
184 /* 0x26 */ "#,##0_);[Red](#,##0)",
185 /* 0x27 */ "#,##0.00_);(#,##0.00)",
186 /* 0x28 */ "#,##0.00_);[Red](#,##0.00)",
187 /* 0x29 */ "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
188 /* 0x2a */ "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)",
189 /* 0x2b */ "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
190 /* 0x2c */ "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)",
191 /* 0x2d */ "mm:ss",
192 /* 0x2e */ "[h]:mm:ss",
193 /* 0x2f */ "mm:ss.0",
194 /* 0x30 */ "##0.0E+0",
195 /* 0x31 */ "@"
198 static PangoAttrList *empty_attr_list;
200 static MsBiffVersion
201 esheet_ver (ExcelReadSheet const *esheet)
203 return esheet->container.importer->ver;
206 static void
207 gnm_xl_importer_set_version (GnmXLImporter *importer, MsBiffVersion ver)
209 g_return_if_fail (NULL != importer);
210 g_return_if_fail (MS_BIFF_V_UNKNOWN == importer->ver);
211 importer->ver = ver;
214 static void
215 gnm_xl_importer_set_codepage (GnmXLImporter *importer, int codepage)
217 GIConv str_iconv;
218 if (codepage == 1200 || codepage == 1201)
219 /* this is 'compressed' unicode. unicode characters 0000->00FF
220 * which looks the same as 8859-1. What does Little endian vs
221 * bigendian have to do with this. There is only 1 byte, and it would
222 * certainly not be useful to keep the low byte as 0.
224 str_iconv = g_iconv_open ("UTF-8", "ISO-8859-1");
225 else
226 str_iconv = gsf_msole_iconv_open_for_import (codepage);
228 if (str_iconv == (GIConv)(-1)) {
229 g_warning ("missing converter for codepage %u\n"
230 "falling back to 1252", codepage);
231 str_iconv = gsf_msole_iconv_open_for_import (1252);
234 if (importer->str_iconv != (GIConv)(-1))
235 gsf_iconv_close (importer->str_iconv);
236 importer->str_iconv = str_iconv;
238 /* Store the codepage to make export easier, might
239 * cause problems with double stream files because
240 * we'll lose the codepage in the biff8 version */
241 g_object_set_data (G_OBJECT (importer->wb), "excel-codepage",
242 GINT_TO_POINTER (codepage));
244 d (0, g_printerr ("%s\n", gsf_msole_language_for_lid (
245 gsf_msole_codepage_to_lid (codepage))););
248 static GOFormat *
249 excel_wb_get_fmt (GnmXLImporter *importer, unsigned idx)
251 char const *ans = NULL;
252 BiffFormatData const *d = g_hash_table_lookup (importer->format_table,
253 GUINT_TO_POINTER (idx));
255 if (d)
256 ans = d->name;
257 else if (idx <= 0x31) {
258 ans = excel_builtin_formats[idx];
259 if (!ans)
260 g_printerr ("Foreign undocumented format\n");
261 } else
262 g_printerr ("Unknown format: 0x%x\n", idx);
264 if (ans) {
265 GOFormat *fmt = gnm_format_import
266 (ans,
267 GNM_FORMAT_IMPORT_NULL_INVALID |
268 GNM_FORMAT_IMPORT_PATCHUP_INCOMPLETE);
269 if (!fmt) {
270 g_warning ("Ignoring invalid format [%s]", ans);
271 fmt = go_format_general ();
272 go_format_ref (fmt);
275 return fmt;
276 } else
277 return NULL;
280 static GnmCell *
281 excel_cell_fetch (BiffQuery *q, ExcelReadSheet *esheet)
283 guint16 col, row;
284 Sheet *sheet = esheet->sheet;
286 XL_CHECK_CONDITION_VAL (q->length >= 4, NULL);
288 col = XL_GETCOL (q);
289 row = XL_GETROW (q);
291 XL_CHECK_CONDITION_VAL (col < gnm_sheet_get_max_cols (sheet), NULL);
292 XL_CHECK_CONDITION_VAL (row < gnm_sheet_get_max_rows (sheet), NULL);
294 return sheet_cell_fetch (sheet, col, row);
297 static GnmExprTop const *
298 ms_sheet_parse_expr_internal (ExcelReadSheet *esheet, guint8 const *data, int length)
300 GnmExprTop const *texpr;
302 g_return_val_if_fail (length > 0, NULL);
304 texpr = excel_parse_formula (&esheet->container, esheet, 0, 0,
305 data, length, 0 /* FIXME */,
306 FALSE, NULL);
307 if (ms_excel_read_debug > 8) {
308 char *tmp;
309 GnmParsePos pp;
310 Sheet *sheet = esheet->sheet;
311 Workbook *wb = (sheet == NULL) ? esheet->container.importer->wb : NULL;
313 tmp = gnm_expr_top_as_string (texpr,
314 parse_pos_init (&pp, wb, sheet, 0, 0),
315 gnm_conventions_default);
316 g_printerr ("%s\n", tmp ? tmp : "(null)");
317 g_free (tmp);
320 return texpr;
323 static GnmExprTop const *
324 ms_sheet_parse_expr (MSContainer *container, guint8 const *data, int length)
326 return ms_sheet_parse_expr_internal ((ExcelReadSheet *)container,
327 data, length);
330 static Sheet *
331 ms_sheet_get_sheet (MSContainer const *container)
333 return ((ExcelReadSheet const *)container)->sheet;
336 static GOFormat *
337 ms_sheet_get_fmt (MSContainer const *container, unsigned indx)
339 return excel_wb_get_fmt (container->importer, indx);
342 static GOColor
343 ms_sheet_map_color (ExcelReadSheet const *esheet, MSObj const *obj, MSObjAttrID id,
344 GOColor default_val, gboolean *pauto)
346 gushort r, g, b;
347 MSObjAttr *attr = ms_obj_attr_bag_lookup (obj->attrs, id);
349 if (attr == NULL) {
350 if (pauto) *pauto = TRUE;
351 return default_val;
354 if ((~0x7ffffff) & attr->v.v_uint) {
355 GnmColor *c = excel_palette_get (esheet->container.importer,
356 (0x7ffffff & attr->v.v_uint));
358 r = GO_COLOR_UINT_R (c->go_color);
359 g = GO_COLOR_UINT_G (c->go_color);
360 b = GO_COLOR_UINT_B (c->go_color);
361 style_color_unref (c);
362 } else {
363 r = (attr->v.v_uint) & 0xff;
364 g = (attr->v.v_uint >> 8) & 0xff;
365 b = (attr->v.v_uint >> 16) & 0xff;
368 if (pauto) *pauto = FALSE;
370 return GO_COLOR_FROM_RGBA (r,g,b,0xff);
374 * ms_sheet_obj_anchor_to_pos:
375 * @points Array which receives anchor coordinates in points
376 * @obj The object
377 * @sheet The sheet
379 * Converts anchor coordinates in Excel units to points. Anchor
380 * coordinates are x and y of upper left and lower right corner. Each
381 * is expressed as a pair: Row/cell number + position within cell as
382 * fraction of cell dimension.
384 * NOTE: According to docs, position within cell is expressed as
385 * 1/1024 of cell dimension. However, this doesn't seem to be true
386 * vertically, for Excel 97. We use 256 for >= XL97 and 1024 for
387 * preceding.
389 static gboolean
390 ms_sheet_obj_anchor_to_pos (Sheet const * sheet,
391 G_GNUC_UNUSED MsBiffVersion const ver,
392 guint8 const *raw_anchor,
393 GnmRange *range, double offset[4], GnmSOAnchorMode *mode)
395 /* NOTE :
396 * gnm_float const row_denominator = (ver >= MS_BIFF_V8) ? 256. : 1024.;
397 * damn damn damn
398 * chap03-1.xls suggests that XL95 uses 256 too
399 * Do we have any tests that confirm the docs contention of 1024 ?
401 int i;
403 d (0,
405 g_printerr ("anchored to %s\n", sheet->name_unquoted);
406 gsf_mem_dump (raw_anchor, 18);
409 switch (raw_anchor[0]) {
410 case 2:
411 *mode = GNM_SO_ANCHOR_ONE_CELL;
412 break;
413 case 3:
414 *mode = GNM_SO_ANCHOR_ABSOLUTE;
415 break;
416 default:
417 *mode = GNM_SO_ANCHOR_TWO_CELLS;
418 break;
420 /* Ignore the first 2 bytes. What are they ? */
421 /* Dec/1/2000 JEG: I have not researched it, but this may have some
422 * flags indicating whether or not the object is anchored to the cell
424 raw_anchor += 2;
426 /* Words 0, 4, 8, 12: The row/col of the corners */
427 /* Words 2, 6, 10, 14: distance from cell edge */
428 for (i = 0; i < 4; i++, raw_anchor += 4) {
429 int const pos = GSF_LE_GET_GUINT16 (raw_anchor);
430 int const nths = GSF_LE_GET_GUINT16 (raw_anchor + 2);
432 d (2, {
433 g_printerr ("%d/%d cell %s from ",
434 nths, (i & 1) ? 256 : 1024,
435 (i & 1) ? "widths" : "heights");
436 if (i & 1)
437 g_printerr ("row %d;\n", pos + 1);
438 else
439 g_printerr ("col %s (%d);\n", col_name (pos), pos);
442 if (i & 1) { /* odds are rows */
443 offset[i] = nths / 256.;
444 if (i == 1)
445 range->start.row = pos;
446 else
447 range->end.row = pos;
448 } else {
449 offset[i] = nths / 1024.;
450 if (i == 0)
451 range->start.col = pos;
452 else
453 range->end.col = pos;
457 return FALSE;
460 static void
461 handle_arrow_head (SheetObject *so, const char *prop_name,
462 double width,
463 MSObjAttrBag *attrs, MSObjAttrID typid,
464 MSObjAttrID wid, MSObjAttrID lid)
466 GOArrow arrow;
467 int w = ms_obj_attr_get_int (attrs, wid, 1);
468 int l = ms_obj_attr_get_int (attrs, lid, 1);
469 int typ = ms_obj_attr_get_int (attrs, typid, 0);
470 xls_arrow_from_xl (&arrow, width, typ, l, w);
471 g_object_set (so, prop_name, &arrow, NULL);
474 static void
475 excel_fill_bmp_header(guint8 *bmphdr, guint8 *data, guint32 len)
477 guint bpp;
478 guint offset;
480 bmphdr[0] = 'B';
481 bmphdr[1] = 'M';
482 GSF_LE_SET_GUINT32 (bmphdr + 2, len + BMP_HDR_SIZE);
483 GSF_LE_SET_GUINT16 (bmphdr + 6, 0);
484 GSF_LE_SET_GUINT16 (bmphdr + 8, 0);
485 bpp = len >= 20 ? GSF_LE_GET_GUINT16 (data + 18) : 1;
486 switch (bpp) {
487 case 24: offset = 0; break;
488 case 8: offset = 256 * 3; break;
489 case 4: offset = 16 * 3; break;
490 default: offset = 2 * 3; break;
492 offset += BMP_HDR_SIZE + 2;
493 GSF_LE_SET_GUINT32 (bmphdr + 10, offset);
496 static gboolean
497 ms_sheet_realize_obj (MSContainer *container, MSObj *obj)
499 double offsets[4];
500 PangoAttrList *markup;
501 GnmRange range;
502 ExcelReadSheet *esheet;
503 MSObjAttr *attr, *flip_h, *flip_v;
504 GODrawingAnchorDir direction;
505 SheetObjectAnchor anchor;
506 SheetObject *so;
507 GOStyle *style;
508 GnmSOAnchorMode mode = GNM_SO_ANCHOR_TWO_CELLS;
510 if (obj == NULL)
511 return TRUE;
512 if (obj->gnum_obj == NULL)
513 return FALSE;
514 so = obj->gnum_obj;
516 g_return_val_if_fail (container != NULL, TRUE);
517 esheet = (ExcelReadSheet *)container;
519 /* our comment object is too weak. This anchor is for the text box,
520 * we need to store the indicator */
521 if (obj->excel_type == 0x19 &&
522 obj->comment_pos.col >= 0 && obj->comment_pos.row >= 0) {
523 cell_comment_set_pos (GNM_CELL_COMMENT (obj->gnum_obj),
524 &obj->comment_pos);
525 } else {
526 attr = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_ANCHOR);
527 if (attr == NULL) {
528 g_printerr ("MISSING anchor for obj %p with id %d of type %s\n", (void *)obj, obj->id, obj->excel_type_name);
529 return TRUE;
532 if (ms_sheet_obj_anchor_to_pos (esheet->sheet, container->importer->ver,
533 attr->v.v_ptr, &range, offsets, &mode))
534 return TRUE;
536 flip_h = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_FLIP_H);
537 flip_v = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_FLIP_V);
538 direction =
539 ((flip_h == NULL) ? GOD_ANCHOR_DIR_RIGHT : 0) |
540 ((flip_v == NULL) ? GOD_ANCHOR_DIR_DOWN : 0);
542 sheet_object_anchor_init (&anchor, &range, offsets, direction, GNM_SO_ANCHOR_TWO_CELLS);
543 sheet_object_set_anchor (so, &anchor);
545 sheet_object_set_sheet (so, esheet->sheet);
546 if (mode != GNM_SO_ANCHOR_TWO_CELLS)
547 sheet_object_set_anchor_mode (so, &mode);
550 gpointer label;
551 if (ms_obj_attr_get_ptr (obj->attrs, MS_OBJ_ATTR_TEXT, &label, FALSE))
552 g_object_set (G_OBJECT (so), "text", label, NULL);
556 gpointer name;
557 if (ms_obj_attr_get_ptr (obj->attrs, MS_OBJ_ATTR_OBJ_NAME, &name, FALSE))
558 g_object_set (G_OBJECT (so), "name", name, NULL);
561 markup = ms_obj_attr_get_markup (obj->attrs, MS_OBJ_ATTR_MARKUP, NULL, FALSE);
562 if (markup != NULL)
563 g_object_set (so, "markup", markup, NULL);
565 switch (obj->excel_type) {
566 case 0x00:
567 break;
569 case MSOT_LINE:
570 case MSOT_ARC: {
571 double width;
572 style = go_style_new ();
573 style->line.color = ms_sheet_map_color
574 (esheet, obj, MS_OBJ_ATTR_OUTLINE_COLOR,
575 GO_COLOR_BLACK, &style->line.auto_color);
576 style->line.width = ms_obj_attr_get_uint (obj->attrs,
577 MS_OBJ_ATTR_OUTLINE_WIDTH, 0) / 256.;
578 style->line.auto_dash =
579 (ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_OUTLINE_HIDE) != NULL);
580 style->line.dash_type = style->line.auto_dash
581 ? GO_LINE_NONE
582 : ms_escher_xl_to_line_type (ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_OUTLINE_STYLE, 0));
584 width = style->line.auto_width ? 0 : style->line.width;
585 g_object_set (G_OBJECT (so), "style", style, NULL);
586 g_object_unref (style);
588 handle_arrow_head (so, "start-arrow", width,
589 obj->attrs,
590 MS_OBJ_ATTR_ARROW_START,
591 MS_OBJ_ATTR_ARROW_START_WIDTH,
592 MS_OBJ_ATTR_ARROW_START_LENGTH);
593 handle_arrow_head (so, "end-arrow", width,
594 obj->attrs,
595 MS_OBJ_ATTR_ARROW_END,
596 MS_OBJ_ATTR_ARROW_END_WIDTH,
597 MS_OBJ_ATTR_ARROW_END_LENGTH);
598 break;
601 case MSOT_POLYGON:
602 g_object_set (G_OBJECT (so), "points",
603 ms_obj_attr_get_array (obj->attrs, MS_OBJ_ATTR_POLYGON_COORDS, NULL, TRUE),
604 NULL);
605 /* fallthrough */
607 case MSOT_RECTANGLE:
608 case MSOT_OVAL:
609 case MSOT_TEXTBOX:
610 case MSOT_LABEL:
611 style = go_style_new ();
612 style->line.color = ms_sheet_map_color
613 (esheet, obj, MS_OBJ_ATTR_OUTLINE_COLOR,
614 GO_COLOR_BLACK, &style->line.auto_color);
615 style->line.width = ms_obj_attr_get_uint (obj->attrs,
616 MS_OBJ_ATTR_OUTLINE_WIDTH, 0) / 256.;
617 style->line.auto_dash = FALSE;
618 style->line.dash_type = (ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_OUTLINE_HIDE) != NULL)
619 ? GO_LINE_NONE
620 : ms_escher_xl_to_line_type (ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_OUTLINE_STYLE, 0));
621 style->fill.pattern.back = ms_sheet_map_color
622 (esheet, obj, MS_OBJ_ATTR_FILL_COLOR,
623 GO_COLOR_WHITE, &style->fill.auto_back);
624 style->fill.pattern.fore = ms_sheet_map_color
625 (esheet, obj, MS_OBJ_ATTR_FILL_BACKGROUND,
626 GO_COLOR_BLACK, &style->fill.auto_fore);
627 /* fill type needs work, we now suppot more than solid color */
628 style->fill.type = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_UNFILLED)
629 ? GO_STYLE_FILL_NONE : GO_STYLE_FILL_PATTERN;
630 if (style->fill.type != GO_STYLE_FILL_PATTERN)
631 style->fill.auto_type = FALSE;
633 g_object_set (G_OBJECT (so), "style", style, NULL);
634 g_object_unref (style);
635 break;
637 case MSOT_CHART:
638 /* NOTE : We should not need to do anything for charts */
639 break;
641 case MSOT_BUTTON:
642 break;
644 case MSOT_PICTURE: {
645 double crop_left = 0.0;
646 double crop_top = 0.0;
647 double crop_right = 0.0;
648 double crop_bottom = 0.0;
650 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
651 MS_OBJ_ATTR_BLIP_ID)) != NULL) {
652 MSEscherBlip *blip = ms_container_get_blip (container,
653 attr->v.v_uint - 1);
654 if (blip != NULL) {
655 if (blip->type && !strcmp (blip->type, "dib")) {
656 guint8 *data = g_malloc(blip->data_len + BMP_HDR_SIZE);
657 if (data) {
658 excel_fill_bmp_header(data, blip->data, blip->data_len);
659 memcpy(data + BMP_HDR_SIZE, blip->data, blip->data_len);
660 sheet_object_image_set_image (GNM_SO_IMAGE (so),
661 blip->type, data, blip->data_len + BMP_HDR_SIZE);
662 g_free (data);
664 } else {
665 sheet_object_image_set_image (GNM_SO_IMAGE (so),
666 blip->type, blip->data, blip->data_len);
669 } else if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
670 MS_OBJ_ATTR_IMDATA)) != NULL) {
671 GdkPixbuf *pixbuf = GDK_PIXBUF (attr->v.v_object);
673 if (pixbuf) {
674 gchar *buf = NULL;
675 gsize buf_size;
677 gdk_pixbuf_save_to_buffer
678 (pixbuf, &buf, &buf_size, "png",
679 NULL, NULL);
680 if (buf_size > 0) {
681 sheet_object_image_set_image
682 (GNM_SO_IMAGE (so),
683 "png", buf, buf_size);
685 g_free (buf);
688 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
689 MS_OBJ_ATTR_BLIP_CROP_LEFT)) != NULL)
690 crop_left = (double) attr->v.v_uint / 65536.;
691 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
692 MS_OBJ_ATTR_BLIP_CROP_RIGHT)) != NULL)
693 crop_right = (double) attr->v.v_uint / 65536.;
694 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
695 MS_OBJ_ATTR_BLIP_CROP_TOP)) != NULL)
696 crop_top = (double) attr->v.v_uint / 65536.;
697 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
698 MS_OBJ_ATTR_BLIP_CROP_BOTTOM)) != NULL)
699 crop_bottom = (double) attr->v.v_uint / 65536.;
701 sheet_object_image_set_crop (GNM_SO_IMAGE (so),
702 crop_left, crop_top, crop_right, crop_bottom);
703 break;
706 case MSOT_CHECKBOX:
707 case MSOT_TOGGLE:
708 sheet_widget_checkbox_set_link (obj->gnum_obj,
709 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE));
710 break;
712 case MSOT_OPTION:
713 sheet_widget_radio_button_set_link (obj->gnum_obj,
714 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE));
715 break;
717 case MSOT_SPINNER:
718 case MSOT_SCROLLBAR:
719 sheet_widget_adjustment_set_details (obj->gnum_obj,
720 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE),
721 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_VALUE, 0),
722 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_MIN, 0),
723 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_MAX, 100) - 1,
724 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_INC, 1),
725 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_PAGE, 10));
726 sheet_widget_adjustment_set_horizontal (obj->gnum_obj,
727 ms_obj_attr_get_uint (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_HORIZ, FALSE));
728 break;
730 case MSOT_LIST:
731 case MSOT_COMBO:
732 sheet_widget_list_base_set_links (obj->gnum_obj,
733 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE),
734 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_INPUT_FROM, NULL, FALSE));
735 break;
737 case MSOT_COMMENT: /* cell comment text box */
738 break;
740 default:
741 d (2, g_printerr ("EXCEL: unhandled excel object of type %s (0x%x) id = %d.",
742 obj->excel_type_name, obj->excel_type, obj->id););
743 return TRUE;
746 return FALSE;
749 static SheetObject *
750 ms_sheet_create_obj (MSContainer *container, MSObj *obj)
752 SheetObject *so = NULL;
754 if (obj == NULL)
755 return NULL;
757 g_return_val_if_fail (container != NULL, NULL);
759 switch (obj->excel_type) {
760 case MSOT_LINE:
761 case MSOT_ARC:
762 so = g_object_new (GNM_SO_LINE_TYPE, NULL);
763 break;
765 case 0x00: /* draw the group border */
766 case MSOT_RECTANGLE:
767 case MSOT_OVAL:
768 case MSOT_TEXTBOX:
769 case MSOT_LABEL:
770 so = g_object_new (GNM_SO_FILLED_TYPE,
771 "is-oval", obj->excel_type == 3,
772 NULL);
773 break;
775 case MSOT_CHART:
776 so = sheet_object_graph_new (NULL);
777 break;
779 /* Button */
780 case MSOT_BUTTON:
781 so = g_object_new (sheet_widget_button_get_type (), NULL);
782 break;
783 case MSOT_PICTURE:
784 so = g_object_new (GNM_SO_IMAGE_TYPE, NULL); /* Picture */
785 break;
786 case MSOT_POLYGON:
787 so = g_object_new (GNM_SO_POLYGON_TYPE, NULL);
788 break;
789 case MSOT_CHECKBOX:
790 so = g_object_new (sheet_widget_checkbox_get_type (), NULL);
791 break;
792 case MSOT_OPTION:
793 so = g_object_new (sheet_widget_radio_button_get_type (), NULL);
794 break;
795 case MSOT_SPINNER:
796 so = g_object_new (sheet_widget_spinbutton_get_type (), NULL);
797 break;
798 case MSOT_SCROLLBAR:
799 so = g_object_new (sheet_widget_scrollbar_get_type (), NULL);
800 break;
801 case MSOT_LIST:
802 so = g_object_new (sheet_widget_list_get_type (), NULL);
803 break;
805 /* ignore combos associateed with filters */
806 case MSOT_COMBO: {
807 ExcelReadSheet *esheet = (ExcelReadSheet *)container;
809 if (!obj->auto_combo)
810 so = g_object_new (sheet_widget_combo_get_type (), NULL);
812 /* ok, there are combos to go with the autofilter it can stay */
813 else if (esheet != NULL)
814 esheet->filter = NULL;
816 break;
818 case MSOT_COMMENT:
819 so = g_object_new (cell_comment_get_type (), NULL);
820 break;
822 /* Gnumeric specific addition to handle toggle button controls */
823 case MSOT_TOGGLE:
824 so = g_object_new (sheet_widget_toggle_button_get_type (), NULL);
825 break;
827 default:
828 g_warning ("EXCEL: unhandled excel object of type %s (0x%x) id = %d.",
829 obj->excel_type_name, obj->excel_type, obj->id);
830 return NULL;
833 return so;
837 * excel_init_margins
838 * @esheet ExcelReadSheet
840 * Excel only saves margins when any of the margins differs from the
841 * default. So we must initialize the margins to Excel's defaults, which
842 * are:
843 * Top, bottom: 1 in - 72 pt
844 * Left, right: 3/4 in - 48 pt
845 * Header, footer: 1/2 in - 36 pt
847 static void
848 excel_init_margins (ExcelReadSheet *esheet)
850 GnmPrintInformation *pi;
851 double points;
852 double short_points;
854 g_return_if_fail (esheet != NULL);
855 g_return_if_fail (esheet->sheet != NULL);
856 g_return_if_fail (esheet->sheet->print_info != NULL);
858 pi = esheet->sheet->print_info;
859 print_info_set_edge_to_below_header (pi,GO_IN_TO_PT (1.0));
860 print_info_set_edge_to_above_footer (pi,GO_IN_TO_PT (1.0));
862 points = GO_IN_TO_PT (0.75);
863 short_points = GO_IN_TO_PT (0.5);
864 print_info_set_margins (pi, short_points, short_points, points, points);
867 static void
868 excel_shared_formula_free (XLSharedFormula *sf)
870 if (sf != NULL) {
871 g_free (sf->data);
872 g_free (sf);
876 static ExcelReadSheet *
877 excel_sheet_new (GnmXLImporter *importer, char const *sheet_name, GnmSheetType type)
879 static MSContainerClass const vtbl = {
880 &ms_sheet_realize_obj,
881 &ms_sheet_create_obj,
882 &ms_sheet_parse_expr,
883 &ms_sheet_get_sheet,
884 &ms_sheet_get_fmt,
885 NULL
887 int rows = (importer->ver >= MS_BIFF_V8 ? XLS_MaxRow_V8 : XLS_MaxRow_V7);
888 ExcelReadSheet *esheet;
889 Sheet *sheet;
891 sheet = workbook_sheet_by_name (importer->wb, sheet_name);
892 if (sheet) {
893 unsigned ui;
895 for (ui = 0; ui < importer->excel_sheets->len; ui++) {
896 ExcelReadSheet *es = g_ptr_array_index (importer->excel_sheets, ui);
897 if (es->sheet == sheet) {
898 g_warning ("Duplicate definition of sheet %s\n", sheet_name);
899 return NULL;
902 } else {
903 sheet = sheet_new_with_type (importer->wb, sheet_name, type,
904 XLS_MaxCol, rows);
905 workbook_sheet_attach (importer->wb, sheet);
906 d (1, g_printerr ("Adding sheet '%s'\n", sheet_name););
909 /* Flag a respan here in case nothing else does */
910 sheet_flag_recompute_spans (sheet);
912 esheet = g_new (ExcelReadSheet, 1);
913 esheet->sheet = sheet;
914 esheet->filter = NULL;
915 esheet->freeze_panes = FALSE;
916 esheet->active_pane = 3; /* The default */
917 esheet->shared_formulae = g_hash_table_new_full
918 ((GHashFunc)&gnm_cellpos_hash,
919 (GCompareFunc)&gnm_cellpos_equal,
920 NULL, (GDestroyNotify) &excel_shared_formula_free);
921 esheet->tables = g_hash_table_new_full
922 ((GHashFunc)&gnm_cellpos_hash,
923 (GCompareFunc)&gnm_cellpos_equal,
924 NULL, (GDestroyNotify) g_free);
925 esheet->biff2_prev_xf_index = -1;
927 excel_init_margins (esheet);
928 ms_container_init (&esheet->container, &vtbl,
929 &importer->container, importer);
930 g_ptr_array_add (importer->excel_sheets, esheet);
932 return esheet;
935 void
936 excel_unexpected_biff (BiffQuery *q, char const *state,
937 int debug_level)
939 #ifndef NO_DEBUG_EXCEL
940 if (debug_level > 1) {
941 g_print ("Unexpected Opcode in %s: 0x%hx, length 0x%x\n",
942 state, q->opcode, q->length);
943 if (debug_level > 2)
944 gsf_mem_dump (q->data, q->length);
946 #endif
950 * excel_read_string_header :
951 * @data: a pointer to the start of the string header
952 * @maxlen: the length of the data area
953 * @use_utf16: Is the content in 8 or 16 bit chars
954 * @n_markup: number of trailing markup records
955 * @has_extended: Is there trailing extended string info (eg japanese PHONETIC)
956 * @post_data_len:
958 * returns the length of the header (in bytes)
960 static guint32
961 excel_read_string_header (guint8 const *data, guint32 maxlen,
962 gboolean *use_utf16,
963 unsigned *n_markup,
964 gboolean *has_extended,
965 unsigned *post_data_len)
967 guint8 header;
968 guint32 len;
970 if (G_UNLIKELY (maxlen < 1))
971 goto error;
973 header = GSF_LE_GET_GUINT8 (data);
974 if (((header & 0xf2) != 0))
975 goto error;
977 *use_utf16 = (header & 0x1) != 0;
979 if ((header & 0x8) != 0) {
980 if (G_UNLIKELY (maxlen < 3))
981 goto error;
982 *n_markup = GSF_LE_GET_GUINT16 (data + 1);
983 *post_data_len = *n_markup * 4; /* 4 bytes per */
984 len = 3;
985 } else {
986 *n_markup = 0;
987 *post_data_len = 0;
988 len = 1;
991 *has_extended = (header & 0x4) != 0;
992 if (*has_extended) {
993 guint32 len_ext_rst;
995 if (G_UNLIKELY (maxlen < len + 4))
996 goto error;
997 len_ext_rst = GSF_LE_GET_GUINT32 (data + len); /* A byte length */
998 *post_data_len += len_ext_rst;
999 len += 4;
1001 d (4, g_printerr ("Extended string support unimplemented; "
1002 "ignoring %u bytes\n", len_ext_rst););
1005 return len;
1007 error:
1008 *use_utf16 = *has_extended = FALSE;
1009 *n_markup = 0;
1010 *post_data_len = 0;
1011 g_warning ("Invalid string record.");
1012 return 0;
1015 char *
1016 excel_get_chars (GnmXLImporter const *importer,
1017 guint8 const *ptr, size_t length, gboolean use_utf16, guint16 const *codepage)
1019 char* ans;
1020 size_t i;
1021 GIConv str_iconv = importer->str_iconv;
1023 if (use_utf16) {
1024 gunichar2 *uni_text = g_alloca (sizeof (gunichar2)*length);
1026 for (i = 0; i < length; i++, ptr += 2)
1027 uni_text [i] = GSF_LE_GET_GUINT16 (ptr);
1028 ans = g_utf16_to_utf8 (uni_text, length, NULL, NULL, NULL);
1029 } else {
1030 size_t outbytes = (length + 2) * 8;
1031 char *outbuf = g_new (char, outbytes + 1);
1032 char *ptr2 = (char *)ptr;
1034 ans = outbuf;
1035 if (NULL != codepage)
1036 str_iconv = gsf_msole_iconv_open_for_import (*codepage);
1037 g_iconv (str_iconv,
1038 &ptr2, &length, &outbuf, &outbytes);
1039 if (codepage)
1040 g_iconv_close (str_iconv);
1042 i = outbuf - ans;
1043 ans[i] = 0;
1044 ans = g_realloc (ans, i + 1);
1046 return ans;
1049 char *
1050 excel_get_text (GnmXLImporter const *importer,
1051 guint8 const *pos, guint32 length,
1052 guint32 *byte_length, guint16 const *codepage, guint32 maxlen)
1054 char *ans;
1055 guint8 const *ptr;
1056 unsigned byte_len, trailing_data_len, n_markup, str_len_bytes;
1057 gboolean use_utf16, has_extended;
1059 if (byte_length == NULL)
1060 byte_length = &byte_len;
1062 if (importer->ver >= MS_BIFF_V8) {
1063 *byte_length = 1; /* the header */
1064 if (length == 0)
1065 return NULL;
1066 ptr = pos + excel_read_string_header
1067 (pos, maxlen,
1068 &use_utf16, &n_markup, &has_extended,
1069 &trailing_data_len);
1070 *byte_length += trailing_data_len;
1071 } else {
1072 *byte_length = 0; /* no header */
1073 if (length == 0)
1074 return NULL;
1075 trailing_data_len = 0;
1076 use_utf16 = has_extended = FALSE;
1077 n_markup = 0;
1078 ptr = pos;
1081 str_len_bytes = (use_utf16 ? 2 : 1) * length;
1083 if (*byte_length > maxlen) {
1084 *byte_length = maxlen;
1085 length = 0;
1086 } else if (maxlen - *byte_length < str_len_bytes) {
1087 *byte_length = maxlen;
1088 length = (maxlen - *byte_length) / (use_utf16 ? 2 : 1);
1089 } else
1090 *byte_length += str_len_bytes;
1092 ans = excel_get_chars (importer, ptr, length, use_utf16, codepage);
1094 d (4, {
1095 g_printerr ("String len %d, byte length %d: %s %s %s:\n",
1096 length, *byte_length,
1097 (use_utf16 ? "UTF16" : "1byte"),
1098 ((n_markup > 0) ? "has markup" :""),
1099 (has_extended ? "has extended phonetic info" : ""));
1100 gsf_mem_dump (pos, *byte_length);
1103 return ans;
1106 static char *
1107 excel_biff_text (GnmXLImporter const *importer,
1108 const BiffQuery *q, guint32 ofs, guint32 length)
1110 XL_CHECK_CONDITION_VAL (q->length >= ofs, NULL);
1112 return excel_get_text (importer, q->data + ofs, length,
1113 NULL, NULL, q->length - ofs);
1116 char *
1117 excel_biff_text_1 (GnmXLImporter const *importer,
1118 const BiffQuery *q, guint32 ofs)
1120 guint32 length;
1122 XL_CHECK_CONDITION_VAL (q->length >= (ofs + 1), NULL);
1124 length = GSF_LE_GET_GUINT8 (q->data + ofs);
1125 ofs++;
1127 return excel_get_text (importer, q->data + ofs, length,
1128 NULL, NULL, q->length - ofs);
1131 char *
1132 excel_biff_text_2 (GnmXLImporter const *importer,
1133 const BiffQuery *q, guint32 ofs)
1135 guint32 length;
1137 XL_CHECK_CONDITION_VAL (q->length >= (ofs + 2), NULL);
1139 length = GSF_LE_GET_GUINT16 (q->data + ofs);
1140 ofs += 2;
1142 return excel_get_text (importer, q->data + ofs, length,
1143 NULL, NULL, q->length - ofs);
1146 typedef struct {
1147 unsigned first, last;
1148 PangoAttrList *accum;
1149 } TXORun;
1151 static gboolean
1152 append_markup (PangoAttribute *src, TXORun *run)
1154 if (run->last > run->first) {
1155 PangoAttribute *dst = pango_attribute_copy (src);
1156 dst->start_index = run->first; /* inclusive */
1157 dst->end_index = run->last; /* exclusive */
1158 pango_attr_list_change (run->accum, dst);
1160 return FALSE;
1163 static GOFormat *
1164 excel_read_LABEL_markup (BiffQuery *q, ExcelReadSheet *esheet,
1165 char const *str, unsigned str_len)
1167 guint8 const * const end = q->data + q->length;
1168 guint8 const *ptr = q->data + 8 + str_len;
1169 MSContainer const *c = &esheet->container;
1170 TXORun txo_run;
1171 unsigned n;
1172 unsigned int clen = g_utf8_strlen (str, -1);
1174 d (0, {
1175 g_printerr ("strlen=%d len=%d\n", str_len, (int)strlen (str));
1176 ms_biff_query_dump (q);
1179 txo_run.last = strlen (str);
1181 if (esheet_ver (esheet) >= MS_BIFF_V8) {
1182 XL_CHECK_CONDITION_VAL (ptr+2 <= end , NULL);
1183 n = 4 * GSF_LE_GET_GUINT16 (ptr);
1184 ptr += 2;
1186 XL_CHECK_CONDITION_VAL (ptr + n == end , NULL);
1188 txo_run.accum = pango_attr_list_new ();
1189 while (n > 0) {
1190 guint16 o,l;
1192 n -= 4;
1194 o = GSF_LE_GET_GUINT16 (ptr + n);
1195 l = GSF_LE_GET_GUINT16 (ptr + n + 2);
1196 XL_CHECK_CONDITION_VAL (o <= clen,
1197 go_format_new_markup (txo_run.accum, FALSE));
1199 txo_run.first = g_utf8_offset_to_pointer (str, o) - str;
1200 XL_CHECK_CONDITION_VAL (txo_run.first < txo_run.last,
1201 go_format_new_markup (txo_run.accum, FALSE));
1203 pango_attr_list_filter (ms_container_get_markup (c, l),
1204 (PangoAttrFilterFunc) append_markup,
1205 &txo_run);
1206 txo_run.last = txo_run.first;
1208 } else {
1209 XL_CHECK_CONDITION_VAL (ptr+1 <= end , NULL);
1210 n = 2 * GSF_LE_GET_GUINT8 (ptr);
1211 ptr += 1;
1213 XL_CHECK_CONDITION_VAL (ptr + n == end , NULL);
1215 txo_run.accum = pango_attr_list_new ();
1216 while (n > 0) {
1217 n -= 2;
1218 txo_run.first = g_utf8_offset_to_pointer (str,
1219 GSF_LE_GET_GUINT8 (ptr + n)) - str;
1220 pango_attr_list_filter (ms_container_get_markup (
1221 c, GSF_LE_GET_GUINT8 (ptr + n + 1)),
1222 (PangoAttrFilterFunc) append_markup, &txo_run);
1223 txo_run.last = txo_run.first;
1226 return go_format_new_markup (txo_run.accum, FALSE);
1230 * NB. Whilst the string proper is split, and whilst we get several headers,
1231 * it seems that the attributes appear in a single block after the end
1232 * of the string, which may also be split over continues.
1234 static guint32
1235 sst_read_string (BiffQuery *q, MSContainer const *c,
1236 ExcelStringEntry *res, guint32 offset)
1238 guint32 get_len, chars_left, total_len, total_end_len = 0;
1239 unsigned i, post_data_len, n_markup, total_n_markup = 0;
1240 gboolean use_utf16, has_extended;
1241 char *str, *old_res, *res_str = NULL;
1243 offset = ms_biff_query_bound_check (q, offset, 2);
1244 if (offset == (guint32)-1)
1245 return offset;
1246 XL_CHECK_CONDITION_VAL (offset < q->length, offset);
1247 total_len = GSF_LE_GET_GUINT16 (q->data + offset);
1248 offset += 2;
1249 do {
1250 offset = ms_biff_query_bound_check (q, offset, 1);
1251 if (offset == (guint32)-1) {
1252 g_free (res_str);
1253 return offset;
1255 offset += excel_read_string_header
1256 (q->data + offset, q->length - offset,
1257 &use_utf16, &n_markup, &has_extended,
1258 &post_data_len);
1259 total_end_len += post_data_len;
1260 total_n_markup += n_markup;
1261 chars_left = (q->length - offset) / (use_utf16 ? 2 : 1);
1262 get_len = (chars_left > total_len) ? total_len : chars_left;
1263 total_len -= get_len;
1265 str = excel_get_chars (c->importer,
1266 q->data + offset, get_len, use_utf16, NULL);
1267 offset += get_len * (use_utf16 ? 2 : 1);
1269 /* Handle corrupted string. */
1270 if (!str)
1271 str = g_strdup ("");
1273 if (res_str != NULL) {
1274 old_res = res_str;
1275 res_str = g_strconcat (old_res, str, NULL);
1276 g_free (str);
1277 g_free (old_res);
1278 } else
1279 res_str = str;
1280 } while (total_len > 0);
1282 if (total_n_markup > 0) {
1283 TXORun txo_run;
1284 PangoAttrList *prev_markup = NULL;
1286 txo_run.accum = pango_attr_list_new ();
1287 txo_run.first = 0;
1288 for (i = total_n_markup ; i-- > 0 ; offset += 4) {
1289 offset = ms_biff_query_bound_check (q, offset, 4);
1290 if (offset == (guint32)-1) {
1291 g_free (res_str);
1292 pango_attr_list_unref (txo_run.accum);
1293 return offset;
1295 if ((q->length >= offset + 4)) {
1296 guint16 o = GSF_LE_GET_GUINT16 (q->data + offset);
1297 size_t l = strlen (res_str);
1298 txo_run.last = g_utf8_offset_to_pointer (res_str, MIN (o, l)) - res_str;
1299 if (prev_markup != NULL)
1300 pango_attr_list_filter (prev_markup,
1301 (PangoAttrFilterFunc) append_markup, &txo_run);
1302 txo_run.first = txo_run.last;
1303 prev_markup = ms_container_get_markup (
1304 c, GSF_LE_GET_GUINT16 (q->data + offset + 2));
1305 } else
1306 g_warning ("A TXO entry is across CONTINUEs. We need to handle those properly");
1308 txo_run.last = G_MAXINT;
1309 pango_attr_list_filter (prev_markup,
1310 (PangoAttrFilterFunc) append_markup, &txo_run);
1311 res->markup = go_format_new_markup (txo_run.accum, FALSE);
1313 total_end_len -= 4*total_n_markup;
1316 res->content = go_string_new_nocopy (res_str);
1317 return offset + total_end_len;
1320 static void
1321 excel_read_SST (BiffQuery *q, GnmXLImporter *importer)
1323 guint32 offset;
1324 unsigned i, sst_len;
1326 XL_CHECK_CONDITION (q->length >= 8);
1328 d (4, {
1329 g_printerr ("SST total = %u, sst = %u\n",
1330 GSF_LE_GET_GUINT32 (q->data + 0),
1331 GSF_LE_GET_GUINT32 (q->data + 4));
1332 gsf_mem_dump (q->data, q->length);
1335 sst_len = GSF_LE_GET_GUINT32 (q->data + 4);
1336 XL_CHECK_CONDITION (sst_len < INT_MAX / sizeof (ExcelStringEntry));
1338 importer->sst_len = sst_len;
1339 importer->sst = g_new0 (ExcelStringEntry, importer->sst_len);
1341 offset = 8;
1342 for (i = 0; i < importer->sst_len; i++) {
1343 offset = sst_read_string (q, &importer->container, importer->sst + i, offset);
1344 if (offset == (guint32)-1)
1345 break;
1347 if (importer->sst[i].content == NULL)
1348 d (4, g_printerr ("Blank string in table at 0x%x.\n", i););
1349 #ifndef NO_DEBUG_EXCEL
1350 else if (ms_excel_read_debug > 4)
1351 g_printerr ("%s\n", importer->sst[i].content->str);
1352 #endif
1356 static void
1357 excel_read_EXSST (BiffQuery *q, GnmXLImporter *importer)
1359 XL_CHECK_CONDITION (q->length >= 2);
1360 d (10, g_printerr ("Bucketsize = %hu,\tnum buckets = %d\n",
1361 GSF_LE_GET_GUINT16 (q->data), (q->length - 2) / 8););
1364 static void
1365 excel_read_1904 (BiffQuery *q, GnmXLImporter *importer)
1367 XL_CHECK_CONDITION (q->length >= 2);
1368 if (GSF_LE_GET_GUINT16 (q->data) == 1)
1369 workbook_set_1904 (importer->wb, TRUE);
1372 GnmValue *
1373 xls_value_new_err (GnmEvalPos const *pos, guint8 err)
1375 switch (err) {
1376 case 0: return value_new_error_NULL (pos);
1377 case 7: return value_new_error_DIV0 (pos);
1378 case 15: return value_new_error_VALUE (pos);
1379 case 23: return value_new_error_REF (pos);
1380 case 29: return value_new_error_NAME (pos);
1381 case 36: return value_new_error_NUM (pos);
1382 case 42: return value_new_error_NA (pos);
1383 default: return value_new_error (pos, _("#UNKNOWN!"));
1387 MsBiffBofData *
1388 ms_biff_bof_data_new (BiffQuery *q)
1390 MsBiffBofData *ans = g_new (MsBiffBofData, 1);
1392 if (q->length >= 4) {
1394 /* Determine type from BOF */
1395 switch (q->opcode) {
1396 case BIFF_BOF_v0: ans->version = MS_BIFF_V2; break;
1397 case BIFF_BOF_v2: ans->version = MS_BIFF_V3; break;
1398 case BIFF_BOF_v4: ans->version = MS_BIFF_V4; break;
1399 case BIFF_BOF_v8:
1400 d (2, {
1401 g_printerr ("Complicated BIFF version 0x%x\n",
1402 GSF_LE_GET_GUINT16 (q->non_decrypted_data));
1403 gsf_mem_dump (q->non_decrypted_data, q->length);
1406 switch (GSF_LE_GET_GUINT16 (q->non_decrypted_data)) {
1407 case 0x0600:
1408 ans->version = MS_BIFF_V8;
1409 break;
1410 case 0x0500: /* * OR ebiff7: FIXME ? ! */
1411 ans->version = MS_BIFF_V7;
1412 break;
1413 /* The following are non-standard records written
1414 by buggy tools. Taken from OO docs. */
1415 case 0x0400:
1416 ans->version = MS_BIFF_V4;
1417 break;
1418 case 0x0300:
1419 ans->version = MS_BIFF_V3;
1420 break;
1421 case 0x0200:
1422 case 0x0007:
1423 case 0x0000:
1424 ans->version = MS_BIFF_V2;
1425 break;
1426 default:
1427 g_printerr ("Unknown BIFF sub-number 0x%X in BOF %x\n",
1428 GSF_LE_GET_GUINT16 (q->non_decrypted_data), q->opcode);
1429 ans->version = MS_BIFF_V_UNKNOWN;
1431 break;
1433 default:
1434 g_printerr ("Unknown BIFF number in BOF %x\n", q->opcode);
1435 ans->version = MS_BIFF_V_UNKNOWN;
1436 g_printerr ("Biff version %d\n", ans->version);
1438 switch (GSF_LE_GET_GUINT16 (q->non_decrypted_data + 2)) {
1439 case 0x0005: ans->type = MS_BIFF_TYPE_Workbook; break;
1440 case 0x0006: ans->type = MS_BIFF_TYPE_VBModule; break;
1441 case 0x0010: ans->type = MS_BIFF_TYPE_Worksheet; break;
1442 case 0x0020: ans->type = MS_BIFF_TYPE_Chart; break;
1443 case 0x0040: ans->type = MS_BIFF_TYPE_Macrosheet; break;
1444 case 0x0100: ans->type = MS_BIFF_TYPE_Workspace; break;
1445 default:
1446 ans->type = MS_BIFF_TYPE_Unknown;
1447 g_printerr ("Unknown BIFF type in BOF %x\n", GSF_LE_GET_GUINT16 (q->non_decrypted_data + 2));
1448 break;
1450 /* Now store in the directory array: */
1451 d (2, g_printerr ("BOF %x, %d == %d, %d\n", q->opcode, q->length,
1452 ans->version, ans->type););
1453 } else {
1454 g_printerr ("Not a BOF !\n");
1455 ans->version = MS_BIFF_V_UNKNOWN;
1456 ans->type = MS_BIFF_TYPE_Unknown;
1459 return ans;
1462 void
1463 ms_biff_bof_data_destroy (MsBiffBofData *data)
1465 g_free (data);
1468 static void
1469 excel_read_BOUNDSHEET (BiffQuery *q, GnmXLImporter *importer)
1471 BiffBoundsheetData *bs;
1472 char const *default_name = "Unknown%d";
1473 gboolean oldstyle = (importer->ver <= MS_BIFF_V4);
1475 XL_CHECK_CONDITION (q->length >= (oldstyle ? 1 : 6));
1477 bs = g_new0 (BiffBoundsheetData, 1);
1478 bs->gnm_type = GNM_SHEET_DATA;
1480 if (oldstyle) {
1481 bs->streamStartPos = 0; /* Excel 4 doesn't tell us */
1482 bs->type = MS_BIFF_TYPE_Worksheet;
1483 default_name = _("Sheet%d");
1484 bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1485 bs->name = excel_biff_text_1 (importer, q, 0);
1486 } else {
1487 if (importer->ver > MS_BIFF_V8)
1488 g_printerr ("Unknown BIFF Boundsheet spec. Assuming same as Biff7 FIXME\n");
1489 bs->streamStartPos = GSF_LE_GET_GUINT32 (q->non_decrypted_data);
1491 /* NOTE : MS Docs appear wrong. It is visibility _then_ type */
1492 switch (GSF_LE_GET_GUINT8 (q->data + 5)) {
1493 case 0: bs->type = MS_BIFF_TYPE_Worksheet;
1494 default_name = _("Sheet%d");
1495 break;
1496 case 1: bs->type = MS_BIFF_TYPE_Macrosheet;
1497 bs->gnm_type = GNM_SHEET_XLM;
1498 default_name = _("Macro%d");
1499 break;
1500 case 2: bs->type = MS_BIFF_TYPE_Chart;
1501 bs->gnm_type = GNM_SHEET_OBJECT;
1502 default_name = _("Chart%d");
1503 break;
1504 case 6: bs->type = MS_BIFF_TYPE_VBModule;
1505 default_name = _("Module%d");
1506 break;
1507 default:
1508 g_printerr ("Unknown boundsheet type: %d\n", GSF_LE_GET_GUINT8 (q->data + 4));
1509 bs->type = MS_BIFF_TYPE_Unknown;
1511 switch ((GSF_LE_GET_GUINT8 (q->data + 4)) & 0x3) {
1512 case 0: bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1513 break;
1514 case 1: bs->visibility = GNM_SHEET_VISIBILITY_HIDDEN;
1515 break;
1516 case 2: bs->visibility = GNM_SHEET_VISIBILITY_VERY_HIDDEN;
1517 break;
1518 default:
1519 g_printerr ("Unknown sheet hiddenness %d\n", (GSF_LE_GET_GUINT8 (q->data + 4)) & 0x3);
1520 bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1523 /* TODO: find some documentation on this.
1524 * Sample data and OpenCalc imply that the docs are incorrect. It
1525 * seems like the name length is 1 byte. Loading sample sheets in
1526 * other locales universally seem to treat the first byte as a length
1527 * and the second as the unicode flag header.
1529 bs->name = excel_biff_text_1 (importer, q, 6);
1532 /* TODO: find some documentation on this.
1533 * It appears that if the name is null it defaults to Sheet%d?
1534 * However, we have only one test case and no docs.
1536 if (bs->name == NULL || bs->name[0] == 0) {
1537 g_free (bs->name);
1538 bs->name = g_strdup_printf (default_name,
1539 importer->boundsheet_sheet_by_index->len + 1);
1542 switch (bs->type) {
1543 case MS_BIFF_TYPE_Worksheet :
1544 case MS_BIFF_TYPE_Macrosheet :
1545 case MS_BIFF_TYPE_Chart :
1546 bs->esheet = excel_sheet_new (importer, bs->name, bs->gnm_type);
1548 if (bs->esheet && bs->esheet->sheet)
1549 g_object_set (bs->esheet->sheet,
1550 "visibility", bs->visibility,
1551 NULL);
1552 break;
1553 default :
1554 bs->esheet = NULL;
1557 bs->index = importer->boundsheet_sheet_by_index->len;
1558 g_ptr_array_add (importer->boundsheet_sheet_by_index, bs->esheet ? bs->esheet->sheet : NULL);
1559 g_hash_table_insert (importer->boundsheet_data_by_stream,
1560 GUINT_TO_POINTER (bs->streamStartPos), bs);
1562 d (1, g_printerr ("Boundsheet: %d) '%s' %p, %d:%d\n", bs->index,
1563 bs->name, bs->esheet, bs->type, bs->visibility););
1566 static void
1567 biff_boundsheet_data_destroy (BiffBoundsheetData *d)
1569 g_free (d->name);
1570 g_free (d);
1573 static void
1574 excel_read_FORMAT (BiffQuery *q, GnmXLImporter *importer)
1576 MsBiffVersion const ver = importer->ver;
1577 BiffFormatData *d;
1579 if (ver >= MS_BIFF_V7) {
1580 XL_CHECK_CONDITION (q->length >= 4);
1582 d = g_new (BiffFormatData, 1);
1583 d->idx = GSF_LE_GET_GUINT16 (q->data);
1584 d->name = (ver >= MS_BIFF_V8)
1585 ? excel_biff_text_2 (importer, q, 2)
1586 : excel_biff_text_1 (importer, q, 2);
1587 } else {
1588 size_t minlen = (ver >= MS_BIFF_V4 ? 3 : 1);
1589 XL_CHECK_CONDITION (q->length >= minlen);
1591 d = g_new (BiffFormatData, 1);
1592 /* no usable index */
1593 d->idx = g_hash_table_size (importer->format_table);
1594 d->name = (ver >= MS_BIFF_V4)
1595 ? excel_biff_text_1 (importer, q, 2)
1596 : excel_biff_text_1 (importer, q, 0);
1599 d (3, g_printerr ("Format data: 0x%x == '%s'\n", d->idx, d->name););
1601 g_hash_table_insert (importer->format_table, GUINT_TO_POINTER (d->idx), d);
1604 static void
1605 excel_read_FONT (BiffQuery *q, GnmXLImporter *importer)
1607 MsBiffVersion const ver = importer->ver;
1608 ExcelFont *fd;
1609 guint16 data;
1610 guint8 data1;
1612 XL_CHECK_CONDITION (q->length >= 4);
1614 fd = g_new (ExcelFont, 1);
1615 fd->height = GSF_LE_GET_GUINT16 (q->data + 0);
1616 data = GSF_LE_GET_GUINT16 (q->data + 2);
1617 fd->italic = (data & 0x2) == 0x2;
1618 fd->struck_out = (data & 0x8) == 0x8;
1619 fd->script = GO_FONT_SCRIPT_STANDARD;
1620 fd->underline = XLS_ULINE_NONE;
1621 fd->codepage = 1252;
1623 if (ver <= MS_BIFF_V2) {
1624 int cp;
1625 guint16 opcode;
1626 fd->boldness = (data & 0x1) ? 0x2bc : 0x190;
1627 fd->underline = (data & 0x4) ? XLS_ULINE_SINGLE : XLS_ULINE_NONE;
1628 fd->fontname = excel_biff_text_1 (importer, q, 4);
1629 if (ms_biff_query_peek_next (q, &opcode) &&
1630 opcode == BIFF_FONT_COLOR) {
1631 ms_biff_query_next (q);
1632 XL_CHECK_CONDITION (q->length >= 2);
1633 fd->color_idx = GSF_LE_GET_GUINT16 (q->data);
1634 } else
1635 fd->color_idx = 0x7f;
1636 cp = gnm_font_override_codepage (fd->fontname);
1637 fd->codepage = (cp > 0 ? cp : 1252);
1638 } else if (ver <= MS_BIFF_V4) /* Guess */ {
1639 int cp;
1640 XL_CHECK_CONDITION (q->length >= 6);
1641 fd->color_idx = GSF_LE_GET_GUINT16 (q->data + 4);
1642 fd->boldness = (data & 0x1) ? 0x2bc : 0x190;
1643 fd->underline = (data & 0x4) ? XLS_ULINE_SINGLE : XLS_ULINE_NONE;
1644 fd->fontname = excel_biff_text_1 (importer, q, 6);
1645 cp = gnm_font_override_codepage (fd->fontname);
1646 fd->codepage = (cp > 0 ? cp : 1252);
1647 } else {
1648 XL_CHECK_CONDITION (q->length >= 13);
1650 fd->color_idx = GSF_LE_GET_GUINT16 (q->data + 4);
1651 fd->boldness = GSF_LE_GET_GUINT16 (q->data + 6);
1652 data = GSF_LE_GET_GUINT16 (q->data + 8);
1653 switch (data) {
1654 case 0: fd->script = GO_FONT_SCRIPT_STANDARD; break;
1655 case 1: fd->script = GO_FONT_SCRIPT_SUPER; break;
1656 case 2: fd->script = GO_FONT_SCRIPT_SUB; break;
1657 default:
1658 g_printerr ("Unknown script %d\n", data);
1659 break;
1662 data1 = GSF_LE_GET_GUINT8 (q->data + 10);
1663 switch (data1) {
1664 case 0: fd->underline = XLS_ULINE_NONE; break;
1665 case 1: fd->underline = XLS_ULINE_SINGLE; break;
1666 case 2: fd->underline = XLS_ULINE_DOUBLE; break;
1667 case 0x21: fd->underline = XLS_ULINE_SINGLE_ACC; break; /* single accounting */
1668 case 0x22: fd->underline = XLS_ULINE_DOUBLE_ACC; break; /* double accounting */
1669 default:
1670 g_printerr ("Unknown uline %#x\n", (int)data1);
1671 break;
1673 fd->fontname = excel_biff_text_1 (importer, q, 14);
1675 data1 = GSF_LE_GET_GUINT8 (q->data + 12);
1676 switch (data1) {
1677 case 0: {
1678 int cp = gnm_font_override_codepage (fd->fontname);
1679 if (cp >= 0) {
1680 fd->codepage = cp;
1681 break;
1683 if (importer->codepage_override > 0) {
1684 fd->codepage = importer->codepage_override;
1685 break;
1688 /* no break */
1689 case 1:
1690 case 255: fd->codepage = 1252; break; /* ANSI Latin, System Default, OEM Latin I */
1691 case 77: fd->codepage = 10000; break; /* Apple */
1692 case 128: fd->codepage = 932; break; /* Japanese Shift-JIS */
1693 case 129: fd->codepage = 949; break; /* Korean Hangul */
1694 case 130: fd->codepage = 1361; break; /* Korean Johab */
1695 case 134: fd->codepage = 936; break; /* Chinese Simplified */
1696 case 136: fd->codepage = 950; break; /* Chinese Traditional */
1697 case 161: fd->codepage = 1253; break; /* Greek */
1698 case 162: fd->codepage = 1254; break; /* Turkish */
1699 case 163: fd->codepage = 1258; break; /* Vietnamese */
1700 case 177: fd->codepage = 1255; break; /* Hebrew */
1701 case 178: fd->codepage = 1256; break; /* Arabic */
1702 case 186: fd->codepage = 1257; break; /* Baltic */
1703 case 204: fd->codepage = 1251; break; /* Russian */
1704 case 222: fd->codepage = 874; break; /* Thai */
1705 case 238: fd->codepage = 1250; break; /* Central European */
1706 default:
1707 g_printerr ("Unknown charset %#x\n", (int) data1);
1708 break;
1711 fd->color_idx &= 0x7f; /* Undocumented but a good idea */
1713 if (fd->fontname == NULL) {
1714 /* Note sure why -- see #418868. */
1715 fd->fontname = g_strdup ("Arial");
1718 fd->attrs = NULL;
1719 fd->go_font = NULL;
1721 fd->index = g_hash_table_size (importer->font_data);
1722 if (fd->index >= 4) /* Weird: for backwards compatibility */
1723 fd->index++;
1724 d (1, g_printerr ("Insert font '%s' (%d) size %d pts color %d\n",
1725 fd->fontname, fd->index, fd->height / 20, fd->color_idx););
1726 d (3, g_printerr ("Font color = 0x%x\n", fd->color_idx););
1728 g_hash_table_insert (importer->font_data,
1729 GINT_TO_POINTER (fd->index), fd);
1733 static void
1734 excel_font_free (ExcelFont *fd)
1736 if (NULL != fd->attrs) {
1737 pango_attr_list_unref (fd->attrs);
1738 fd->attrs = NULL;
1740 if (NULL != fd->go_font) {
1741 go_font_unref (fd->go_font);
1742 fd->go_font = NULL;
1744 g_free (fd->fontname);
1745 g_free (fd);
1748 static void
1749 biff_format_data_destroy (BiffFormatData *d)
1751 g_free (d->name);
1752 g_free (d);
1755 /** Default color table for BIFF5/BIFF7. */
1756 ExcelPaletteEntry const excel_default_palette_v7 [] = {
1757 { 0, 0, 0}, {255,255,255}, {255, 0, 0}, { 0,255, 0},
1758 { 0, 0,255}, {255,255, 0}, {255, 0,255}, { 0,255,255},
1760 {128, 0, 0}, { 0,128, 0}, { 0, 0,128}, {128,128, 0},
1761 {128, 0,128}, { 0,128,128}, {192,192,192}, {128,128,128},
1763 {128,128,255}, {128, 32, 96}, {255,255,192}, {160,224,224},
1764 { 96, 0,128}, {255,128,128}, { 0,128,192}, {192,192,255},
1766 { 0, 0,128}, {255, 0,255}, {255,255, 0}, { 0,255,255},
1767 {128, 0,128}, {128, 0, 0}, { 0,128,128}, { 0, 0,255},
1769 { 0,204,255}, {105,255,255}, {204,255,204}, {255,255,153},
1770 {166,202,240}, {204,156,204}, {204,153,255}, {227,227,227},
1772 { 51,102,255}, { 51,204,204}, { 51,153, 51}, {153,153, 51},
1773 {153,102, 51}, {153,102,102}, {102,102,153}, {150,150,150},
1775 { 51, 51,204}, { 51,102,102}, { 0, 51, 0}, { 51, 51, 0},
1776 {102, 51, 0}, {153, 51,102}, { 51, 51,153}, { 66, 66, 66}
1779 ExcelPaletteEntry const excel_default_palette_v8 [] = {
1780 { 0, 0, 0}, {255,255,255}, {255, 0, 0}, { 0,255, 0},
1781 { 0, 0,255}, {255,255, 0}, {255, 0,255}, { 0,255,255},
1783 {128, 0, 0}, { 0,128, 0}, { 0, 0,128}, {128,128, 0},
1784 {128, 0,128}, { 0,128,128}, {192,192,192}, {128,128,128},
1786 {153,153,255}, {153, 51,102}, {255,255,204}, {204,255,255},
1787 {102, 0,102}, {255,128,128}, { 0,102,204}, {204,204,255},
1789 { 0, 0,128}, {255, 0,255}, {255,255, 0}, { 0,255,255},
1790 {128, 0,128}, {128, 0, 0}, { 0,128,128}, { 0, 0,255},
1792 { 0,204,255}, {204,255,255}, {204,255,204}, {255,255,153},
1793 {153,204,255}, {255,153,204}, {204,153,255}, {255,204,153},
1795 { 51,102,255}, { 51,204,204}, {153,204, 0}, {255,204, 0},
1796 {255,153, 0}, {255,102, 0}, {102,102,153}, {150,150,150},
1798 { 0, 51,102}, { 51,153,102}, { 0, 51, 0}, { 51, 51, 0},
1799 {153, 51, 0}, {153, 51,102}, { 51, 51,153}, { 51, 51, 51}
1802 GnmColor *
1803 excel_palette_get (GnmXLImporter *importer, gint idx)
1805 ExcelPalette *pal;
1807 /* return black on failure */
1808 g_return_val_if_fail (importer != NULL, style_color_black ());
1810 if (NULL == (pal = importer->palette)) {
1811 int entries = EXCEL_DEF_PAL_LEN;
1812 ExcelPaletteEntry const *defaults = (importer->ver >= MS_BIFF_V8)
1813 ? excel_default_palette_v8 : excel_default_palette_v7;
1815 pal = importer->palette = g_new (ExcelPalette, 1);
1816 pal->length = entries;
1817 pal->red = g_new (int, entries);
1818 pal->green = g_new (int, entries);
1819 pal->blue = g_new (int, entries);
1820 pal->gnm_colors = g_new (GnmColor *, entries);
1822 while (--entries >= 0) {
1823 pal->red[entries] = defaults[entries].r;
1824 pal->green[entries] = defaults[entries].g;
1825 pal->blue[entries] = defaults[entries].b;
1826 pal->gnm_colors[entries] = NULL;
1830 /* NOTE: not documented but seems close
1831 * If you find a normative reference please forward it.
1833 * The color index field seems to use
1834 * 8-63 = Palette index 0-55
1835 * 64 = auto pattern, auto border
1836 * 65 = auto background
1837 * 127 = auto font
1839 * 65 is always white, and 127 always black. 64 is black
1840 * if the fDefaultHdr flag in WINDOW2 is unset, otherwise it's
1841 * the grid color from WINDOW2.
1844 d (4, g_printerr ("Color Index %d\n", idx););
1846 if (idx == 1 || idx == 65)
1847 return style_color_white ();
1848 switch (idx) {
1849 case 0: /* black */
1850 case 64: /* system text ? */
1851 case 81: /* tooltip text */
1852 case 0x7fff: /* system text ? */
1853 return style_color_black ();
1854 case 1: /* white */
1855 case 65: /* system back ? */
1856 return style_color_white ();
1858 case 80: /* tooltip background */
1859 return gnm_color_new_rgb8 (0xff, 0xff, 0xe0);
1861 case 2: return gnm_color_new_rgb8 (0xff, 0, 0); /* red */
1862 case 3: return gnm_color_new_rgb8 ( 0, 0xff, 0); /* green */
1863 case 4: return gnm_color_new_rgb8 ( 0, 0, 0xff); /* blue */
1864 case 5: return gnm_color_new_rgb8 (0xff, 0xff, 0); /* yellow */
1865 case 6: return gnm_color_new_rgb8 (0xff, 0, 0xff); /* magenta */
1866 case 7: return gnm_color_new_rgb8 ( 0, 0xff, 0xff); /* cyan */
1867 default:
1868 break;
1871 idx -= 8;
1872 if (idx < 0 || pal->length <= idx) {
1873 g_warning ("EXCEL: color index (%d) is out of range (8..%d). Defaulting to black",
1874 idx + 8, pal->length+8);
1875 return style_color_black ();
1878 if (pal->gnm_colors[idx] == NULL) {
1879 pal->gnm_colors[idx] =
1880 gnm_color_new_rgb8 (pal->red[idx],
1881 pal->green[idx],
1882 pal->blue[idx]);
1883 g_return_val_if_fail (pal->gnm_colors[idx],
1884 style_color_black ());
1885 d (5, {
1886 const GnmColor *c = pal->gnm_colors[idx];
1887 g_printerr ("New color in slot %d: RGBA = %x,%x,%x,%x\n",
1888 idx,
1889 GO_COLOR_UINT_R (c->go_color),
1890 GO_COLOR_UINT_G (c->go_color),
1891 GO_COLOR_UINT_B (c->go_color),
1892 GO_COLOR_UINT_A (c->go_color));
1896 style_color_ref (pal->gnm_colors[idx]);
1897 return pal->gnm_colors[idx];
1900 static void
1901 excel_palette_destroy (ExcelPalette *pal)
1903 guint16 lp;
1905 g_free (pal->red);
1906 g_free (pal->green);
1907 g_free (pal->blue);
1908 for (lp = 0; lp < pal->length; lp++)
1909 style_color_unref (pal->gnm_colors[lp]);
1910 g_free (pal->gnm_colors);
1911 g_free (pal);
1914 static void
1915 excel_read_PALETTE (BiffQuery *q, GnmXLImporter *importer)
1917 int lp, len;
1918 ExcelPalette *pal;
1920 XL_CHECK_CONDITION (q->length >= 2);
1921 len = GSF_LE_GET_GUINT16 (q->data);
1922 XL_CHECK_CONDITION (q->length >= 2u + len * 4u);
1924 pal = g_new (ExcelPalette, 1);
1925 pal->length = len;
1926 pal->red = g_new (int, len);
1927 pal->green = g_new (int, len);
1928 pal->blue = g_new (int, len);
1929 pal->gnm_colors = g_new (GnmColor *, len);
1931 d (3, g_printerr ("New palette with %d entries\n", len););
1933 for (lp = 0; lp < len; lp++) {
1934 guint32 num = GSF_LE_GET_GUINT32 (q->data + 2 + lp * 4);
1936 /* NOTE the order of bytes is different from what one would
1937 * expect */
1938 pal->blue[lp] = (num & 0x00ff0000) >> 16;
1939 pal->green[lp] = (num & 0x0000ff00) >> 8;
1940 pal->red[lp] = (num & 0x000000ff) >> 0;
1941 d (5, g_printerr ("Colour %d: 0x%8x (%x,%x,%x)\n", lp,
1942 num, pal->red[lp], pal->green[lp], pal->blue[lp]););
1944 pal->gnm_colors[lp] = NULL;
1946 if (importer->palette)
1947 excel_palette_destroy (importer->palette);
1948 importer->palette = pal;
1953 * Search for a font record from its index in the workbooks font table
1954 * NB. index 4 is omitted supposedly for backwards compatiblity
1955 * Returns the font color if there is one.
1957 ExcelFont const *
1958 excel_font_get (GnmXLImporter const *importer, unsigned font_idx)
1960 ExcelFont const *fd =
1961 g_hash_table_lookup (importer->font_data,
1962 GINT_TO_POINTER (font_idx));
1963 if (!fd) {
1964 g_warning ("Invalid font index %d\n", font_idx);
1965 /* Try fallback. */
1966 fd = g_hash_table_lookup (importer->font_data,
1967 GINT_TO_POINTER (0));
1970 return fd;
1973 GOFont const *
1974 excel_font_get_gofont (ExcelFont const *efont)
1976 if (NULL == efont->go_font) {
1977 PangoFontDescription *desc = pango_font_description_new ();
1979 d (1, { g_printerr ("EFONT: %s %d %d %d\n",
1980 efont->fontname,
1981 efont->boldness,
1982 efont->italic,
1983 efont->height); });
1984 #warning FINISH when GOFont is smarter
1985 pango_font_description_set_family (desc, efont->fontname);
1986 pango_font_description_set_weight (desc, efont->boldness);
1987 pango_font_description_set_style (desc,
1988 efont->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1989 pango_font_description_set_size (desc,
1990 efont->height * PANGO_SCALE / 20);
1992 ((ExcelFont *)efont)->go_font = go_font_new_by_desc (desc);
1994 return efont->go_font;
1997 static BiffXFData const *
1998 excel_get_xf (ExcelReadSheet *esheet, unsigned xfidx)
2000 GPtrArray const * const p = esheet->container.importer->XF_cell_records;
2002 g_return_val_if_fail (p != NULL, NULL);
2004 if (esheet_ver (esheet) == MS_BIFF_V2) {
2005 /* ignore the replicated info that comes with the index
2006 * we've already parsed the XF record */
2007 xfidx &= 0x3f;
2008 if (xfidx == 0x3f) {
2009 if (esheet->biff2_prev_xf_index < 0) {
2010 g_warning ("extension xf with no preceding old_xf record, using default as fallback");
2011 xfidx = 15;
2012 } else
2013 xfidx = esheet->biff2_prev_xf_index;
2017 if (xfidx >= p->len) {
2018 XL_CHECK_CONDITION_VAL (p->len > 0, NULL);
2019 g_warning ("XL: Xf index 0x%X is not in the range[0..0x%X)", xfidx, p->len);
2020 xfidx = 0;
2022 /* FIXME: when we can handle styles too deal with this correctly */
2023 /* g_return_val_if_fail (xf->xftype == MS_BIFF_X_CELL, NULL); */
2024 return g_ptr_array_index (p, xfidx);
2027 static GnmUnderline
2028 xls_uline_to_gnm_underline (MsBiffFontUnderline mul)
2030 g_return_val_if_fail (mul >= XLS_ULINE_NONE, UNDERLINE_NONE);
2031 g_return_val_if_fail (mul <= XLS_ULINE_DOUBLE_ACC, UNDERLINE_NONE);
2033 switch (mul) {
2034 case XLS_ULINE_SINGLE:
2035 return UNDERLINE_SINGLE;
2036 case XLS_ULINE_DOUBLE:
2037 return UNDERLINE_DOUBLE;
2038 case XLS_ULINE_SINGLE_ACC:
2039 return UNDERLINE_SINGLE_LOW;
2040 case XLS_ULINE_DOUBLE_ACC:
2041 return UNDERLINE_DOUBLE_LOW;
2042 case XLS_ULINE_NONE:
2043 default:
2044 return UNDERLINE_NONE;
2049 /* Adds a ref the result */
2050 static GnmStyle *
2051 excel_get_style_from_xf (ExcelReadSheet *esheet, BiffXFData const *xf)
2053 ExcelFont const *fd;
2054 GnmColor *pattern_color, *back_color, *font_color;
2055 int pattern_index, back_index, font_index;
2056 GnmStyle *mstyle;
2057 int i;
2059 if (xf == NULL)
2060 return NULL;
2062 /* If we've already done the conversion use the cached style */
2063 if (xf->mstyle != NULL) {
2064 gnm_style_ref (xf->mstyle);
2065 return xf->mstyle;
2068 /* Create a new style and fill it in */
2069 mstyle = gnm_style_new_default ();
2071 /* Format */
2072 if (xf->style_format)
2073 gnm_style_set_format (mstyle, xf->style_format);
2075 /* protection */
2076 gnm_style_set_contents_locked (mstyle, xf->locked);
2077 gnm_style_set_contents_hidden (mstyle, xf->hidden);
2079 /* Alignment */
2080 gnm_style_set_align_v (mstyle, xf->valign);
2081 gnm_style_set_align_h (mstyle, xf->halign);
2082 gnm_style_set_wrap_text (mstyle, xf->wrap_text);
2083 gnm_style_set_shrink_to_fit (mstyle, xf->shrink_to_fit);
2084 gnm_style_set_indent (mstyle, xf->indent);
2085 gnm_style_set_rotation (mstyle, xf->rotation);
2086 gnm_style_set_text_dir (mstyle, xf->text_dir);
2088 /* Font */
2089 fd = excel_font_get (esheet->container.importer, xf->font_idx);
2090 if (fd != NULL) {
2091 gnm_style_set_font_name (mstyle, fd->fontname);
2092 gnm_style_set_font_size (mstyle, fd->height / 20.0);
2093 gnm_style_set_font_bold (mstyle, fd->boldness >= 0x2bc);
2094 gnm_style_set_font_italic (mstyle, fd->italic);
2095 gnm_style_set_font_strike (mstyle, fd->struck_out);
2096 gnm_style_set_font_script (mstyle, fd->script);
2097 gnm_style_set_font_uline
2098 (mstyle, xls_uline_to_gnm_underline (fd->underline));
2099 font_index = fd->color_idx;
2100 } else
2101 font_index = 127; /* Default to Black */
2103 /* Background */
2104 gnm_style_set_pattern (mstyle, xf->fill_pattern_idx);
2106 /* Solid patterns seem to reverse the meaning */
2107 if (xf->fill_pattern_idx == 1) {
2108 pattern_index = xf->pat_backgnd_col;
2109 back_index = xf->pat_foregnd_col;
2110 } else {
2111 pattern_index = xf->pat_foregnd_col;
2112 back_index = xf->pat_backgnd_col;
2115 d (4, g_printerr ("back = %d, pat = %d, font = %d, pat_style = %d\n",
2116 back_index, pattern_index, font_index, xf->fill_pattern_idx););
2118 if (font_index == 127)
2119 font_color = style_color_auto_font ();
2120 else
2121 font_color = excel_palette_get (esheet->container.importer, font_index);
2123 switch (back_index) {
2124 case 64:
2125 back_color = sheet_style_get_auto_pattern_color (esheet->sheet);
2126 break;
2127 case 65:
2128 back_color = style_color_auto_back ();
2129 break;
2130 default:
2131 back_color = excel_palette_get (esheet->container.importer, back_index);
2132 break;
2135 switch (pattern_index) {
2136 case 64: /* Normal case for auto pattern color */
2137 pattern_color = sheet_style_get_auto_pattern_color
2138 (esheet->sheet);
2139 break;
2140 case 65:
2141 /* Mutated form, also observed in the wild, but only for
2142 solid fill. I. e.: this color is not visible. */
2143 pattern_color = style_color_auto_back ();
2144 break;
2145 default:
2146 pattern_color = excel_palette_get (esheet->container.importer, pattern_index);
2147 break;
2150 g_return_val_if_fail (back_color && pattern_color && font_color, NULL);
2152 d (4, g_printerr ("back = #%02x%02x%02x, pat = #%02x%02x%02x, font = #%02x%02x%02x, pat_style = %d\n",
2153 GO_COLOR_UINT_R (back_color->go_color),
2154 GO_COLOR_UINT_G (back_color->go_color),
2155 GO_COLOR_UINT_B (back_color->go_color),
2156 GO_COLOR_UINT_R (pattern_color->go_color),
2157 GO_COLOR_UINT_G (pattern_color->go_color),
2158 GO_COLOR_UINT_B (pattern_color->go_color),
2159 GO_COLOR_UINT_R (font_color->go_color),
2160 GO_COLOR_UINT_G (font_color->go_color),
2161 GO_COLOR_UINT_B (font_color->go_color),
2162 xf->fill_pattern_idx););
2164 gnm_style_set_font_color (mstyle, font_color);
2165 gnm_style_set_back_color (mstyle, back_color);
2166 gnm_style_set_pattern_color (mstyle, pattern_color);
2168 /* Borders */
2169 for (i = 0; i < STYLE_ORIENT_MAX; i++) {
2170 GnmStyle *tmp = mstyle;
2171 GnmStyleElement const t = MSTYLE_BORDER_TOP + i;
2172 GnmStyleBorderLocation const sbl = GNM_STYLE_BORDER_TOP + i;
2173 int const color_index = xf->border_color[i];
2174 GnmColor *color;
2176 switch (color_index) {
2177 case 64:
2178 color = sheet_style_get_auto_pattern_color
2179 (esheet->sheet);
2180 d (4, g_printerr ("border with color_index=%d\n",
2181 color_index););
2182 break;
2183 case 65:
2184 color = style_color_auto_back ();
2185 /* We haven't seen this yet.
2186 We know that 64 and 127 occur in the wild */
2187 d (4, g_printerr ("border with color_index=%d\n",
2188 color_index););
2189 break;
2190 case 127:
2191 color = style_color_auto_font ();
2192 break;
2193 default:
2194 color = excel_palette_get (esheet->container.importer, color_index);
2195 break;
2197 gnm_style_set_border (tmp, t,
2198 gnm_style_border_fetch (xf->border_type[i],
2199 color,
2200 gnm_style_border_get_orientation (sbl)));
2203 /* Set the cache (const_cast) */
2204 ((BiffXFData *)xf)->mstyle = mstyle;
2205 gnm_style_ref (mstyle);
2206 return xf->mstyle;
2209 static GnmBorder *
2210 excel_choose_border (GnmBorder *b1, GnmBorder *b2)
2212 /* double > thick > medium > medium-dash > medium-dash-dot > slanted dash-dot >
2213 medium dash-dot-dot > thin > dashed > dotted > dash-dot > dash-dot-dot > hair */
2214 static int choice[GNM_STYLE_BORDER_SLANTED_DASH_DOT + 1]
2215 [GNM_STYLE_BORDER_SLANTED_DASH_DOT + 1]
2216 = { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_NONE */
2217 { 1,0,0,1,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_THIN */
2218 { 1,1,0,1,1,0,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM */
2219 { 1,0,0,0,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_DASHED */
2220 { 1,0,0,0,0,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_DOTTED */
2221 { 1,1,1,1,1,0,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_THICK */
2222 { 1,1,1,1,1,1,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_DOUBLE */
2223 { 1,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_HAIR */
2224 { 1,1,0,1,1,0,0,1,0,1,1,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM_DASH */
2225 { 1,0,0,0,0,0,0,1,0,0,0,1,0,0 }, /* GNM_STYLE_BORDER_DASH_DOT */
2226 { 1,1,0,1,1,0,0,1,0,1,0,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM_DASH_DOT */
2227 { 1,0,0,0,0,0,0,1,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_DASH_DOT_DOT */
2228 { 1,1,0,1,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT */
2229 { 1,1,0,1,1,0,0,1,0,1,0,1,1,0 } /* GNM_STYLE_BORDER_SLANTED_DASH_DOT */
2232 return (choice[b1->line_type][b2->line_type]) ? b1 : b2;
2235 static BiffXFData const *
2236 excel_set_xf (ExcelReadSheet *esheet, BiffQuery *q)
2238 guint16 col, row;
2239 BiffXFData const *xf;
2240 GnmStyle *mstyle;
2241 Sheet *sheet = esheet->sheet;
2243 XL_CHECK_CONDITION_VAL (q->length >= 6, NULL);
2244 col = XL_GETCOL (q);
2245 row = XL_GETROW (q);
2246 xf = excel_get_xf (esheet, GSF_LE_GET_GUINT16 (q->data + 4));
2247 XL_CHECK_CONDITION_VAL (col < gnm_sheet_get_max_cols (sheet), xf);
2248 XL_CHECK_CONDITION_VAL (row < gnm_sheet_get_max_rows (sheet), xf);
2249 mstyle = excel_get_style_from_xf (esheet, xf);
2251 d (3, g_printerr ("%s!%s%d = xf(0x%hx) = style (%p) [LEN = %u]\n", sheet->name_unquoted,
2252 col_name (col), row + 1, GSF_LE_GET_GUINT16 (q->data + 4), mstyle, q->length););
2254 if (mstyle != NULL) {
2255 GnmBorder *top_b, *left_b;
2257 sheet_style_set_pos (sheet, col, row, mstyle);
2259 /* In Excel & Gnumeric generated xls-files we do not have a conflict */
2260 /* between borders of adjacent cells, but according to bug #660605 */
2261 /* there are xls files in the wild that have a conflict. We need to */
2262 /* resolve these conflicts to ensure consistent behaviour when we edit */
2263 /* borders and to provide the expected border appearance. */
2265 top_b = gnm_style_get_border (mstyle, MSTYLE_BORDER_TOP);
2266 left_b = gnm_style_get_border (mstyle, MSTYLE_BORDER_LEFT);
2268 if ((row > 0 && top_b != NULL && top_b->line_type != GNM_STYLE_BORDER_NONE) ||
2269 (col > 0 && left_b != NULL && left_b->line_type != GNM_STYLE_BORDER_NONE)) {
2270 GnmBorder **overlay = g_new0 (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
2271 GnmRange range;
2273 if (row > 0 &&
2274 top_b != NULL && top_b->line_type != GNM_STYLE_BORDER_NONE) {
2275 GnmStyle const *previous = sheet_style_get (sheet, col, row - 1);
2276 if (previous != NULL) {
2277 GnmBorder *prev_b = gnm_style_get_border
2278 (previous, MSTYLE_BORDER_BOTTOM);
2279 if (prev_b != NULL &&
2280 prev_b->line_type != GNM_STYLE_BORDER_NONE &&
2281 prev_b->line_type != top_b->line_type)
2282 overlay[GNM_STYLE_BORDER_TOP] =
2283 gnm_style_border_ref
2284 (excel_choose_border (top_b, prev_b));
2287 if (col > 0 &&
2288 left_b != NULL && left_b->line_type != GNM_STYLE_BORDER_NONE) {
2289 GnmStyle const *previous = sheet_style_get (sheet, col - 1, row);
2290 if (previous != NULL) {
2291 GnmBorder *prev_b = gnm_style_get_border
2292 (previous, MSTYLE_BORDER_RIGHT);
2293 if (prev_b != NULL &&
2294 prev_b->line_type != GNM_STYLE_BORDER_NONE &&
2295 prev_b->line_type != left_b->line_type)
2296 overlay[GNM_STYLE_BORDER_LEFT] =
2297 gnm_style_border_ref
2298 (excel_choose_border (left_b, prev_b));
2302 /* We are using sheet_style_apply_border rather than */
2303 /* sheet_style_apply_pos since it clears the appropriate */
2304 /* adjacent borders */
2305 range_init (&range, col, row, col, row);
2306 sheet_style_apply_border (sheet, &range, overlay);
2307 gnm_style_border_unref (overlay[GNM_STYLE_BORDER_TOP]);
2308 gnm_style_border_unref (overlay[GNM_STYLE_BORDER_LEFT]);
2309 g_free (overlay);
2312 return xf;
2315 static void
2316 excel_set_xf_segment (ExcelReadSheet *esheet,
2317 int start_col, int end_col,
2318 int start_row, int end_row, unsigned xfidx)
2320 GnmRange range;
2321 GnmStyle *mstyle = excel_get_style_from_xf (esheet,
2322 excel_get_xf (esheet, xfidx));
2324 if (mstyle == NULL)
2325 return;
2327 range.start.col = start_col;
2328 range.start.row = start_row;
2329 range.end.col = end_col;
2330 range.end.row = end_row;
2331 sheet_style_set_range (esheet->sheet, &range, mstyle);
2333 d (3, {
2334 g_printerr ("%s!", esheet->sheet->name_unquoted);
2335 range_dump (&range, "");
2336 g_printerr (" = xf(%d)\n", xfidx);
2340 static GnmStyleBorderType
2341 biff_xf_map_border (int b)
2343 switch (b) {
2344 case 0: /* None */
2345 return GNM_STYLE_BORDER_NONE;
2346 case 1: /* Thin */
2347 return GNM_STYLE_BORDER_THIN;
2348 case 2: /* Medium */
2349 return GNM_STYLE_BORDER_MEDIUM;
2350 case 3: /* Dashed */
2351 return GNM_STYLE_BORDER_DASHED;
2352 case 4: /* Dotted */
2353 return GNM_STYLE_BORDER_DOTTED;
2354 case 5: /* Thick */
2355 return GNM_STYLE_BORDER_THICK;
2356 case 6: /* Double */
2357 return GNM_STYLE_BORDER_DOUBLE;
2358 case 7: /* Hair */
2359 return GNM_STYLE_BORDER_HAIR;
2360 case 8: /* Medium Dashed */
2361 return GNM_STYLE_BORDER_MEDIUM_DASH;
2362 case 9: /* Dash Dot */
2363 return GNM_STYLE_BORDER_DASH_DOT;
2364 case 10: /* Medium Dash Dot */
2365 return GNM_STYLE_BORDER_MEDIUM_DASH_DOT;
2366 case 11: /* Dash Dot Dot */
2367 return GNM_STYLE_BORDER_DASH_DOT_DOT;
2368 case 12: /* Medium Dash Dot Dot */
2369 return GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT;
2370 case 13: /* Slanted Dash Dot*/
2371 return GNM_STYLE_BORDER_SLANTED_DASH_DOT;
2373 g_printerr ("Unknown border style %d\n", b);
2374 return GNM_STYLE_BORDER_NONE;
2377 static int
2378 excel_map_pattern_index_from_excel (int const i)
2380 static int const map_from_excel[] = {
2382 1, 3, 2, 4, 7, 8,
2383 10, 9, 11, 12, 13, 14,
2384 15, 16, 17, 18, 5, 6
2387 /* Default to Solid if out of range */
2388 XL_CHECK_CONDITION_VAL (i >= 0 && i < (int)G_N_ELEMENTS (map_from_excel), 0);
2390 return map_from_excel[i];
2393 static GnmHAlign
2394 halign_from_excel (guint e)
2396 switch (e) {
2397 case 0: return GNM_HALIGN_GENERAL;
2398 case 1: return GNM_HALIGN_LEFT;
2399 case 2: return GNM_HALIGN_CENTER;
2400 case 3: return GNM_HALIGN_RIGHT;
2401 case 4: return GNM_HALIGN_FILL;
2402 case 5: return GNM_HALIGN_JUSTIFY;
2403 case 6: return GNM_HALIGN_CENTER_ACROSS_SELECTION;
2404 case 7: return GNM_HALIGN_DISTRIBUTED;
2406 default:
2407 g_printerr ("Unknown halign %d\n", e);
2408 return GNM_HALIGN_GENERAL;
2412 static GnmVAlign
2413 valign_from_excel (guint e)
2415 switch (e) {
2416 case 0: return GNM_VALIGN_TOP;
2417 case 1: return GNM_VALIGN_CENTER;
2418 case 2: return GNM_VALIGN_BOTTOM;
2419 case 3: return GNM_VALIGN_JUSTIFY;
2420 case 4: return GNM_VALIGN_DISTRIBUTED;
2421 default:
2422 g_printerr ("Unknown valign %d\n", e);
2423 return GNM_VALIGN_TOP;
2427 static int
2428 rotation_from_excel_v8 (guint e)
2430 if (e == 0xff)
2431 return -1;
2432 else if (e > 90)
2433 return 360 + 90 - e;
2434 else
2435 return e;
2438 static int
2439 rotation_from_excel_v7 (guint e)
2441 switch (e) {
2442 default:
2443 case 0: return 0;
2444 case 1: return -1;
2445 case 2: return 90;
2446 case 3: return 270;
2450 static void
2451 excel_read_XF_OLD (BiffQuery *q, GnmXLImporter *importer)
2453 BiffXFData *xf;
2454 guint16 data;
2455 guint8 subdata;
2457 d ( 2, g_printerr ("XF # %d\n", importer->XF_cell_records->len); );
2458 d ( 2, gsf_mem_dump (q->data, q->length); );
2460 XL_CHECK_CONDITION (q->length >= (importer->ver >= MS_BIFF_V3 ? 12 : 4));
2462 xf = g_new0 (BiffXFData, 1);
2463 xf->font_idx = q->data[0];
2464 xf->format_idx = (importer->ver >= MS_BIFF_V3)
2465 ? q->data[1] : (q->data[2] & 0x3f);
2466 xf->style_format = (xf->format_idx > 0)
2467 ? excel_wb_get_fmt (importer, xf->format_idx) : NULL;
2468 xf->is_simple_format = xf->style_format == NULL ||
2469 go_format_is_simple (xf->style_format);
2471 if (importer->ver >= MS_BIFF_V3) {
2472 xf->locked = (q->data[2] & 0x1) != 0;
2473 xf->hidden = (q->data[2] & 0x2) != 0;
2474 xf->xftype = (q->data[2] & 0x4)
2475 ? MS_BIFF_X_STYLE : MS_BIFF_X_CELL;
2476 } else {
2477 xf->locked = (q->data[1] & 0x40) != 0;
2478 xf->hidden = (q->data[1] & 0x80) != 0;
2479 xf->xftype = MS_BIFF_X_CELL;
2481 xf->parentstyle = 0; /* TODO extract for biff 3 and biff4 */
2482 xf->format = MS_BIFF_F_MS;
2483 xf->wrap_text = FALSE;
2484 xf->shrink_to_fit = FALSE;
2487 xf->halign = GNM_HALIGN_GENERAL;
2489 data = (importer->ver >= MS_BIFF_V3) ? q->data[4] : q->data[3];
2490 switch (data & 0x07) {
2491 default :
2492 case 0: xf->halign = GNM_HALIGN_GENERAL; break;
2493 case 1: xf->halign = GNM_HALIGN_LEFT; break;
2494 case 2: xf->halign = GNM_HALIGN_CENTER; break;
2495 case 3: xf->halign = GNM_HALIGN_RIGHT; break;
2496 case 4: xf->halign = GNM_HALIGN_FILL; break;
2497 case 5: xf->halign = GNM_HALIGN_JUSTIFY; break;
2498 case 6: xf->halign = GNM_HALIGN_CENTER_ACROSS_SELECTION; break;
2501 xf->valign = GNM_VALIGN_BOTTOM;
2502 xf->rotation = 0;
2503 xf->indent = 0;
2504 xf->differences = 0;
2505 xf->text_dir = GNM_TEXT_DIR_CONTEXT;
2507 if (importer->ver >= MS_BIFF_V4) {
2508 xf->wrap_text = (data & 0x0008) != 0;
2509 switch (data & 0x30) {
2510 case 0x00: xf->valign = GNM_VALIGN_TOP; break;
2511 case 0x10: xf->valign = GNM_VALIGN_CENTER; break;
2512 default :
2513 case 0x20: xf->valign = GNM_VALIGN_BOTTOM; break;
2515 switch (data & 0xc0) {
2516 case 0x00: xf->rotation = 0; break;
2517 case 0x40: xf->rotation = -1; break;
2518 case 0x80: xf->rotation = 90; break;
2519 case 0xc0: xf->rotation = 270; break;
2521 } else if (importer->ver >= MS_BIFF_V3) {
2522 xf->wrap_text = (data & 0x0008) != 0;
2523 if (xf->wrap_text)
2524 xf->valign = GNM_VALIGN_TOP;
2527 if (importer->ver >= MS_BIFF_V3) {
2528 data = GSF_LE_GET_GUINT16 (q->data + 6);
2529 xf->pat_backgnd_col = (data & 0xf800) >> 11;
2530 if (xf->pat_backgnd_col >= 24)
2531 xf->pat_backgnd_col += 40; /* Defaults */
2532 xf->pat_foregnd_col = (data & 0x07c0) >> 6;
2533 if (xf->pat_foregnd_col >= 24)
2534 xf->pat_foregnd_col += 40; /* Defaults */
2535 xf->fill_pattern_idx =
2536 excel_map_pattern_index_from_excel(data & 0x001f);
2538 data = GSF_LE_GET_GUINT8 (q->data + 10);
2539 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border(data & 0x07);
2540 subdata = data >> 3;
2541 xf->border_color[STYLE_BOTTOM] = (subdata==24) ? 64 : subdata;
2542 data = GSF_LE_GET_GUINT8 (q->data + 8);
2543 xf->border_type[STYLE_TOP] = biff_xf_map_border(data & 0x07);
2544 subdata = data >> 3;
2545 xf->border_color[STYLE_TOP] = (subdata==24) ? 64 : subdata;
2546 data = GSF_LE_GET_GUINT8 (q->data + 9);
2547 xf->border_type[STYLE_LEFT] = biff_xf_map_border(data & 0x07);
2548 subdata = data >> 3;
2549 xf->border_color[STYLE_LEFT] = (subdata==24) ? 64 : subdata;
2550 data = GSF_LE_GET_GUINT8 (q->data + 11);
2551 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (data & 0x07);
2552 subdata = data >> 3;
2553 xf->border_color[STYLE_RIGHT] = (subdata==24) ? 64 : subdata;
2554 } else /* MS_BIFF_V2 */ {
2555 xf->pat_foregnd_col = 0;
2556 xf->pat_backgnd_col = 1;
2558 data = q->data[3];
2559 xf->border_type[STYLE_LEFT] = (data & 0x08) ? 1 : 0;
2560 xf->border_color[STYLE_LEFT] = 0;
2561 xf->border_type[STYLE_RIGHT] = (data & 0x10) ? 1: 0;
2562 xf->border_color[STYLE_RIGHT] = 0;
2563 xf->border_type[STYLE_TOP] = (data & 0x20) ? 1: 0;
2564 xf->border_color[STYLE_TOP] = 0;
2565 xf->border_type[STYLE_BOTTOM] = (data & 0x40) ? 1: 0;
2566 xf->border_color[STYLE_BOTTOM] = 0;
2567 xf->fill_pattern_idx = (data & 0x80) ? 5: 0;
2570 xf->border_type[STYLE_DIAGONAL] = 0;
2571 xf->border_color[STYLE_DIAGONAL] = 0;
2572 xf->border_type[STYLE_REV_DIAGONAL] = 0;
2573 xf->border_color[STYLE_REV_DIAGONAL] = 0;
2575 /* Init the cache */
2576 xf->mstyle = NULL;
2578 g_ptr_array_add (importer->XF_cell_records, xf);
2581 static void
2582 excel_read_XF (BiffQuery *q, GnmXLImporter *importer)
2584 BiffXFData *xf;
2585 guint32 data, subdata;
2587 if (importer->ver >= MS_BIFF_V8)
2588 XL_CHECK_CONDITION (q->length >= 20);
2589 else
2590 XL_CHECK_CONDITION (q->length >= 16);
2592 xf = g_new (BiffXFData, 1);
2593 xf->font_idx = GSF_LE_GET_GUINT16 (q->data);
2594 xf->format_idx = GSF_LE_GET_GUINT16 (q->data + 2);
2595 xf->style_format = (xf->format_idx > 0)
2596 ? excel_wb_get_fmt (importer, xf->format_idx) : NULL;
2597 xf->is_simple_format = xf->style_format == NULL ||
2598 go_format_is_simple (xf->style_format);
2600 data = GSF_LE_GET_GUINT16 (q->data + 4);
2601 xf->locked = (data & 0x0001) != 0;
2602 xf->hidden = (data & 0x0002) != 0;
2603 xf->xftype = (data & 0x0004) ? MS_BIFF_X_STYLE : MS_BIFF_X_CELL;
2604 xf->format = (data & 0x0008) ? MS_BIFF_F_LOTUS : MS_BIFF_F_MS;
2605 xf->parentstyle = (data & 0xfff0) >> 4;
2607 if (xf->xftype == MS_BIFF_X_CELL && xf->parentstyle != 0) {
2608 /* TODO Add support for parent styles
2609 * XL implements a simple form of inheritance with styles.
2610 * If a style's parent changes a value and the child has not
2611 * overridden that value explicitly the child gets updated.
2615 data = GSF_LE_GET_GUINT16 (q->data + 6);
2616 xf->halign = halign_from_excel (data & 0x0007);
2617 xf->wrap_text = (data & 0x0008) != 0;
2618 xf->valign = valign_from_excel ((data & 0x0070) >> 4);
2619 xf->rotation = (importer->ver >= MS_BIFF_V8)
2620 ? rotation_from_excel_v8 (data >> 8)
2621 : rotation_from_excel_v7 ((data >> 8) & 3);
2623 if (importer->ver >= MS_BIFF_V8) {
2624 guint16 const data = GSF_LE_GET_GUINT16 (q->data + 8);
2626 /* FIXME: This code seems irrelevant for merging.
2627 * The undocumented record MERGECELLS appears to be the correct source.
2628 * Nothing seems to set the merge flags.
2630 /* gboolean const merge = (data & 0x20) ? TRUE : FALSE; */
2632 xf->indent = data & 0x0f;
2633 xf->shrink_to_fit = (data & 0x10) ? TRUE : FALSE;
2635 subdata = (data & 0x00C0) >> 10;
2636 switch (subdata) {
2637 default: g_warning ("Unknown text direction %d.", subdata);
2638 /* Fall through. */
2639 case 0: xf->text_dir = GNM_TEXT_DIR_CONTEXT; break;
2640 case 1: xf->text_dir = GNM_TEXT_DIR_LTR; break;
2641 case 2: xf->text_dir = GNM_TEXT_DIR_RTL; break;
2643 } else {
2644 xf->text_dir = GNM_TEXT_DIR_CONTEXT;
2645 xf->shrink_to_fit = FALSE;
2646 xf->indent = 0;
2649 xf->differences = data & 0xFC00;
2651 if (importer->ver >= MS_BIFF_V8) { /* Very different */
2652 int has_diagonals, diagonal_style;
2653 data = GSF_LE_GET_GUINT16 (q->data + 10);
2654 subdata = data;
2655 xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0xf);
2656 subdata = subdata >> 4;
2657 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0xf);
2658 subdata = subdata >> 4;
2659 xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0xf);
2660 subdata = subdata >> 4;
2661 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border (subdata & 0xf);
2662 subdata = subdata >> 4;
2664 data = GSF_LE_GET_GUINT16 (q->data + 12);
2665 subdata = data;
2666 xf->border_color[STYLE_LEFT] = (subdata & 0x7f);
2667 subdata = subdata >> 7;
2668 xf->border_color[STYLE_RIGHT] = (subdata & 0x7f);
2669 subdata = (data & 0xc000) >> 14;
2670 has_diagonals = subdata & 0x3;
2672 data = GSF_LE_GET_GUINT32 (q->data + 14);
2673 subdata = data;
2674 xf->border_color[STYLE_TOP] = (subdata & 0x7f);
2675 subdata = subdata >> 7;
2676 xf->border_color[STYLE_BOTTOM] = (subdata & 0x7f);
2677 subdata = subdata >> 7;
2679 /* Assign the colors whether we have a border or not. We will
2680 * handle that later */
2681 xf->border_color[STYLE_DIAGONAL] =
2682 xf->border_color[STYLE_REV_DIAGONAL] = (subdata & 0x7f);
2684 /* Ok. Now use the flag from above to assign borders */
2685 diagonal_style = biff_xf_map_border (((data & 0x01e00000) >> 21) & 0xf);
2686 xf->border_type[STYLE_DIAGONAL] = (has_diagonals & 0x2)
2687 ? diagonal_style : GNM_STYLE_BORDER_NONE;
2688 xf->border_type[STYLE_REV_DIAGONAL] = (has_diagonals & 0x1)
2689 ? diagonal_style : GNM_STYLE_BORDER_NONE;
2691 xf->fill_pattern_idx =
2692 excel_map_pattern_index_from_excel ((data>>26) & 0x3f);
2694 data = GSF_LE_GET_GUINT16 (q->data + 18);
2695 xf->pat_foregnd_col = (data & 0x007f);
2696 xf->pat_backgnd_col = (data & 0x3f80) >> 7;
2698 d (2, g_printerr ("Color f=0x%x b=0x%x pat=0x%x\n",
2699 xf->pat_foregnd_col,
2700 xf->pat_backgnd_col,
2701 xf->fill_pattern_idx););
2703 } else { /* Biff 7 */
2704 data = GSF_LE_GET_GUINT16 (q->data + 8);
2705 xf->pat_foregnd_col = (data & 0x007f);
2706 /* Documentation is wrong, background color is one bit more
2707 * than documented */
2708 xf->pat_backgnd_col = (data & 0x3f80) >> 7;
2710 data = GSF_LE_GET_GUINT16 (q->data + 10);
2711 xf->fill_pattern_idx =
2712 excel_map_pattern_index_from_excel (data & 0x3f);
2714 d (2, g_printerr ("Color f=0x%x b=0x%x pat=0x%x\n",
2715 xf->pat_foregnd_col,
2716 xf->pat_backgnd_col,
2717 xf->fill_pattern_idx););
2719 /* Luckily this maps nicely onto the new set. */
2720 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border ((data & 0x1c0) >> 6);
2721 xf->border_color[STYLE_BOTTOM] = (data & 0xfe00) >> 9;
2723 data = GSF_LE_GET_GUINT16 (q->data + 12);
2724 subdata = data;
2725 xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0x07);
2726 subdata = subdata >> 3;
2727 xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0x07);
2728 subdata = subdata >> 3;
2729 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0x07);
2731 subdata = subdata >> 3;
2732 xf->border_color[STYLE_TOP] = subdata;
2734 data = GSF_LE_GET_GUINT16 (q->data + 14);
2735 subdata = data;
2736 xf->border_color[STYLE_LEFT] = (subdata & 0x7f);
2737 subdata = subdata >> 7;
2738 xf->border_color[STYLE_RIGHT] = (subdata & 0x7f);
2740 /* Init the diagonals which were not availabile in Biff7 */
2741 xf->border_type[STYLE_DIAGONAL] =
2742 xf->border_type[STYLE_REV_DIAGONAL] = 0;
2743 xf->border_color[STYLE_DIAGONAL] =
2744 xf->border_color[STYLE_REV_DIAGONAL] = 127;
2747 /* Init the cache */
2748 xf->mstyle = NULL;
2750 g_ptr_array_add (importer->XF_cell_records, xf);
2751 d (2, g_printerr ("XF(0x%04x): S=%d L=%d H=%d L=%d xty=%d Font=%d Fmt=%d Fg=%d Bg=%d Pat=%d\n",
2752 importer->XF_cell_records->len - 1,
2753 xf->is_simple_format, xf->locked, xf->hidden, xf->format,
2754 xf->xftype,
2755 xf->font_idx,
2756 xf->format_idx,
2757 xf->pat_foregnd_col,
2758 xf->pat_backgnd_col,
2759 xf->fill_pattern_idx););
2762 static void
2763 excel_read_XF_INDEX (BiffQuery *q, ExcelReadSheet *esheet)
2765 XL_CHECK_CONDITION (q->length >= 2);
2766 esheet->biff2_prev_xf_index = GSF_LE_GET_GUINT16 (q->data);
2769 static void
2770 biff_xf_data_destroy (BiffXFData *xf)
2772 go_format_unref (xf->style_format);
2773 xf->style_format = NULL;
2775 if (xf->mstyle) {
2776 gnm_style_unref (xf->mstyle);
2777 xf->mstyle = NULL;
2779 g_free (xf);
2782 static GnmExprTop const *
2783 excel_formula_shared (BiffQuery *q, ExcelReadSheet *esheet, GnmCell *cell)
2785 guint16 opcode, data_len, data_ofs, array_data_len;
2786 GnmRange r;
2787 gboolean is_array;
2788 GnmExprTop const *texpr;
2789 guint8 const *data;
2791 if (!ms_biff_query_peek_next (q, &opcode) ||
2792 !(opcode == BIFF_SHRFMLA ||
2793 opcode == BIFF_ARRAY_v0 || opcode == BIFF_ARRAY_v2 ||
2794 opcode == BIFF_TABLE_v0 || opcode == BIFF_TABLE_v2)) {
2795 g_warning ("EXCEL: unexpected record '0x%x' after a formula in '%s'.",
2796 opcode, cell_name (cell));
2797 return NULL;
2799 ms_biff_query_next (q);
2801 XL_CHECK_CONDITION_VAL (q->length >= 6, NULL);
2802 xls_read_range8 (&r, q->data);
2804 if (opcode == BIFF_TABLE_v0 || opcode == BIFF_TABLE_v2) {
2805 XLDataTable *dt;
2806 GnmExprList *args = NULL;
2807 GnmCellRef ref;
2808 guint16 flags;
2810 XL_CHECK_CONDITION_VAL (q->length >= 16, NULL);
2812 flags = GSF_LE_GET_GUINT16 (q->data + 6);
2814 d (2, { range_dump (&r, " <-- contains data table\n");
2815 gsf_mem_dump (q->data, q->length); });
2817 dt = g_new0 (XLDataTable, 1);
2818 dt->table = r;
2819 dt->c_in.row = GSF_LE_GET_GUINT16 (q->data + 8);
2820 dt->c_in.col = GSF_LE_GET_GUINT16 (q->data + 10);
2821 dt->r_in.row = GSF_LE_GET_GUINT16 (q->data + 12);
2822 dt->r_in.col = GSF_LE_GET_GUINT16 (q->data + 14);
2823 g_hash_table_replace (esheet->tables, &dt->table.start, dt);
2825 args = gnm_expr_list_append
2826 (args,
2827 gnm_expr_new_cellref
2828 (gnm_cellref_init (&ref, NULL,
2829 dt->c_in.col - r.start.col,
2830 dt->c_in.row - r.start.row, TRUE)));
2831 if (flags & 0x8) {
2832 args = gnm_expr_list_append
2833 (args,
2834 gnm_expr_new_cellref
2835 (gnm_cellref_init (&ref, NULL,
2836 dt->r_in.col - r.start.col,
2837 dt->r_in.row - r.start.row, TRUE)));
2838 } else {
2839 GnmExpr const *missing = gnm_expr_new_constant (value_new_empty ());
2840 args = (flags & 4)
2841 ? gnm_expr_list_append (args, missing)
2842 : gnm_expr_list_prepend (args, missing);
2844 texpr = gnm_expr_top_new (gnm_expr_new_funcall (gnm_func_lookup ("table", NULL), args));
2845 gnm_cell_set_array (esheet->sheet, &r, texpr);
2846 gnm_expr_top_unref (texpr);
2847 return NULL;
2850 d (2, range_dump (&r, " <-- contains a shared formula\n"););
2852 is_array = (q->opcode != BIFF_SHRFMLA);
2854 data_ofs = (esheet_ver (esheet) > MS_BIFF_V4 && is_array) ? 14 : 10;
2855 XL_CHECK_CONDITION_VAL (q->length >= data_ofs, NULL);
2856 data = q->data + data_ofs;
2857 data_len = GSF_LE_GET_GUINT16 (q->data + (data_ofs - 2));
2858 XL_CHECK_CONDITION_VAL (q->length >= data_ofs + data_len, NULL);
2859 array_data_len = data_len > 0 ? q->length - (data_ofs + data_len) : 0;
2861 texpr = excel_parse_formula (&esheet->container, esheet,
2862 r.start.col, r.start.row,
2863 data, data_len, array_data_len,
2864 !is_array, NULL);
2866 if (g_hash_table_lookup (esheet->shared_formulae, &cell->pos)) {
2867 g_printerr ("Duplicate shared formula for cell %s\n", cell_name (cell));
2868 } else {
2869 XLSharedFormula *sf = g_new (XLSharedFormula, 1);
2871 /* WARNING: Do NOT use the upper left corner as the hashkey.
2872 * For some bizzare reason XL appears to sometimes not
2873 * flag the formula as shared until later.
2874 * Use the location of the cell we are reading as the key.
2876 sf->key = cell->pos;
2877 sf->is_array = is_array;
2878 sf->data = data_len > 0 ? g_memdup (data, data_len + array_data_len) : NULL;
2879 sf->data_len = data_len;
2880 sf->array_data_len = array_data_len;
2881 sf->being_parsed = FALSE;
2883 d (1, g_printerr ("Shared formula, extent %s\n", range_as_string (&r)););
2885 g_hash_table_insert (esheet->shared_formulae, &sf->key, sf);
2888 g_return_val_if_fail (texpr != NULL, NULL);
2890 if (is_array) {
2891 gnm_cell_set_array (esheet->sheet, &r, texpr);
2892 gnm_expr_top_unref (texpr);
2893 return NULL;
2894 } else
2895 return texpr;
2899 * NOTE: There must be _no_ path through this function that does not set the
2900 * cell value.
2902 static void
2903 excel_read_FORMULA (BiffQuery *q, ExcelReadSheet *esheet)
2905 /* Pre-retrieve incase this is a string */
2906 gboolean array_elem, is_string = FALSE;
2907 guint16 col, row, options;
2908 guint16 expr_length;
2909 guint offset;
2910 guint8 const *val_dat = q->data + 6;
2911 GnmExprTop const *texpr;
2912 GnmCell *cell;
2913 GnmValue *val = NULL;
2915 XL_CHECK_CONDITION (q->length >= 16);
2916 col = XL_GETCOL (q);
2917 row = XL_GETROW (q);
2918 options = GSF_LE_GET_GUINT16 (q->data + 14);
2920 excel_set_xf (esheet, q);
2922 cell = excel_cell_fetch (q, esheet);
2923 if (!cell)
2924 return;
2926 /* TODO TODO TODO: Wishlist
2927 * We should make an array of minimum sizes for each BIFF type
2928 * and have this checking done there.
2930 if (esheet_ver (esheet) >= MS_BIFF_V5) {
2931 XL_CHECK_CONDITION (q->length >= 22);
2932 expr_length = GSF_LE_GET_GUINT16 (q->data + 20);
2933 offset = 22;
2935 /* TODO : it would be nice to figure out how to allocate recalc tags.
2936 * that would avoid the scary
2937 * 'this file was calculated with a different version of XL'
2938 * warning when exiting without changing. */
2939 d (1, g_printerr ("Formula in %s!%s has recalc tag 0x%x;\n",
2940 esheet->sheet->name_quoted, cell_name (cell),
2941 GSF_LE_GET_GUINT32 (q->data + 16)););
2943 } else if (esheet_ver (esheet) >= MS_BIFF_V3) {
2944 XL_CHECK_CONDITION (q->length >= 18);
2945 expr_length = GSF_LE_GET_GUINT16 (q->data + 16);
2946 offset = 18;
2947 } else {
2948 XL_CHECK_CONDITION (q->length >= 17);
2949 expr_length = GSF_LE_GET_GUINT8 (q->data + 16);
2950 offset = 17;
2951 val_dat++; /* compensate for the 3 byte style */
2953 XL_CHECK_CONDITION (q->length >= offset + expr_length);
2955 /* Get the current value so that we can format, do this BEFORE handling
2956 * shared/array formulas or strings in case we need to go to the next
2957 * record */
2958 if (GSF_LE_GET_GUINT16 (val_dat + 6) != 0xffff) {
2959 val = value_new_float (gsf_le_get_double (val_dat));
2960 } else {
2961 guint8 const val_type = GSF_LE_GET_GUINT8 (val_dat);
2962 switch (val_type) {
2963 case 0: is_string = TRUE; break;
2964 case 1: val = value_new_bool (GSF_LE_GET_GUINT8 (val_dat + 2) != 0);
2965 break;
2966 case 2: val = xls_value_new_err (NULL, GSF_LE_GET_GUINT8 (val_dat + 2));
2967 break;
2968 case 3: val = value_new_empty (); /* Empty (Undocumented) */
2969 break;
2970 default:
2971 g_printerr ("Unknown type (%x) for cell's (%s) current val\n",
2972 val_type, cell_name (cell));
2976 texpr = excel_parse_formula (&esheet->container, esheet, col, row,
2977 q->data + offset, expr_length,
2978 q->length - (offset + expr_length),
2979 FALSE, &array_elem);
2980 #if 0
2981 /* dump the trailing array and natural language data */
2982 gsf_mem_dump (q->data + offset + expr_length,
2983 q->length - offset - expr_length);
2984 #endif
2986 /* Error was flaged by parse_formula */
2987 if (texpr == NULL && !array_elem)
2988 texpr = excel_formula_shared (q, esheet, cell);
2990 if (is_string) {
2991 guint16 opcode;
2992 if (ms_biff_query_peek_next (q, &opcode) &&
2993 (opcode == BIFF_STRING_v0 || opcode == BIFF_STRING_v2)) {
2994 char *v = NULL;
2995 if (ms_biff_query_next (q)) {
2997 * NOTE: the Excel developers kit docs are
2998 * WRONG. There is an article that
2999 * clarifies the behaviour to be the std
3000 * unicode format rather than the pure
3001 * length version the docs describe.
3003 * NOTE : Apparently some apps actually store a
3004 * 0 length string record for an empty.
3005 * DAMN! this was us! we were screwing
3006 * up when exporting ""
3008 guint16 const len = (q->length >= 2) ? GSF_LE_GET_GUINT16 (q->data) : 0;
3010 if (len > 0)
3011 v = excel_biff_text (esheet->container.importer, q, 2, len);
3012 else
3014 * Pre-Biff8 seems to use len=0
3015 * Should that be a string or an EMPTY?
3017 v = g_strdup ("");
3019 if (v) {
3020 val = value_new_string_nocopy (v);
3021 } else {
3022 GnmEvalPos ep;
3023 val = value_new_error (eval_pos_init_cell (&ep, cell),
3024 "INVALID STRING");
3025 g_warning ("EXCEL: invalid STRING record in %s",
3026 cell_name (cell));
3028 } else {
3029 /* There should be a STRING record here */
3030 GnmEvalPos ep;
3031 val = value_new_error (eval_pos_init_cell (&ep, cell),
3032 "MISSING STRING");
3033 g_warning ("EXCEL: missing STRING record for %s",
3034 cell_name (cell));
3038 /* We MUST have a value */
3039 if (val == NULL) {
3040 GnmEvalPos ep;
3041 val = value_new_error (eval_pos_init_cell (&ep, cell),
3042 "MISSING Value");
3043 g_warning ("EXCEL: Invalid state. Missing Value in %s?",
3044 cell_name (cell));
3047 if (gnm_cell_is_array (cell)) {
3048 /* Array expressions were already stored in the cells (without
3049 * recalc), and without a value. Handle either the first
3050 * instance or the followers.
3052 gnm_cell_assign_value (cell, val);
3053 } else if (!gnm_cell_has_expr (cell)) {
3054 /* Just in case things screwed up, at least save the value */
3055 if (texpr != NULL) {
3056 gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
3057 gnm_expr_top_unref (texpr);
3058 } else
3059 gnm_cell_assign_value (cell, val);
3060 } else {
3062 * NOTE: Only the expression is screwed.
3063 * The value and format can still be set.
3065 g_warning ("EXCEL: Multiple expressions for cell %s!%s",
3066 esheet->sheet->name_quoted, cell_name (cell));
3067 gnm_cell_set_value (cell, val);
3068 if (texpr)
3069 gnm_expr_top_unref (texpr);
3073 * 0x1 = AlwaysCalc
3074 * 0x2 = CalcOnLoad
3076 if (options & 0x3)
3077 cell_queue_recalc (cell);
3080 XLSharedFormula *
3081 excel_sheet_shared_formula (ExcelReadSheet const *esheet,
3082 GnmCellPos const *key)
3084 g_return_val_if_fail (esheet != NULL, NULL);
3086 d (5, g_printerr ("FIND SHARED: %s\n", cellpos_as_string (key)););
3088 return g_hash_table_lookup (esheet->shared_formulae, key);
3091 XLDataTable *
3092 excel_sheet_data_table (ExcelReadSheet const *esheet,
3093 GnmCellPos const *key)
3095 g_return_val_if_fail (esheet != NULL, NULL);
3097 d (5, g_printerr ("FIND DATA TABLE: %s\n", cellpos_as_string (key)););
3099 return g_hash_table_lookup (esheet->tables, key);
3102 static void
3103 excel_sheet_insert_val (ExcelReadSheet *esheet, BiffQuery *q, GnmValue *v)
3105 GnmCell *cell = excel_cell_fetch (q, esheet);
3107 if (cell) {
3108 (void)excel_set_xf (esheet, q);
3109 gnm_cell_set_value (cell, v);
3110 } else
3111 value_release (v);
3114 static void
3115 excel_read_NOTE (BiffQuery *q, ExcelReadSheet *esheet)
3117 GnmCellPos pos;
3118 Sheet *sheet = esheet->sheet;
3119 guint16 row, col;
3121 XL_CHECK_CONDITION (q->length >= 4);
3123 row = XL_GETROW (q);
3124 col = XL_GETCOL (q);
3125 XL_CHECK_CONDITION (col < gnm_sheet_get_max_cols (sheet));
3126 XL_CHECK_CONDITION (row < gnm_sheet_get_max_rows (sheet));
3127 pos.row = XL_GETROW (q);
3128 pos.col = XL_GETCOL (q);
3130 if (esheet_ver (esheet) >= MS_BIFF_V8) {
3131 guint16 options, obj_id;
3132 gboolean hidden;
3133 MSObj *obj;
3134 char *author;
3136 XL_CHECK_CONDITION (q->length >= 8);
3137 options = GSF_LE_GET_GUINT16 (q->data + 4);
3138 hidden = (options & 0x2)==0;
3139 obj_id = GSF_LE_GET_GUINT16 (q->data + 6);
3141 /* Docs claim that only 0x2 is valid, all other flags should
3142 * be 0 but we have seen examples with 0x100 'pusiuhendused juuli 2003.xls'
3144 * docs mention 0x002 == hidden
3145 * real life adds 0x080 == ???
3146 * real life adds 0x100 == no indicator visible ??? */
3147 if (options & 0xe7d)
3148 g_warning ("unknown flag on NOTE record %hx", options);
3150 author = excel_biff_text_2 (esheet->container.importer, q, 8);
3151 d (1, g_printerr ("Comment at %s%d id %d options"
3152 " 0x%x hidden %d by '%s'\n",
3153 col_name (pos.col), pos.row + 1,
3154 obj_id, options, hidden, author););
3156 obj = ms_container_get_obj (&esheet->container, obj_id);
3157 if (obj != NULL) {
3158 cell_comment_author_set (GNM_CELL_COMMENT (obj->gnum_obj), author);
3159 obj->comment_pos = pos;
3160 } else {
3161 /* hmm, how did this happen ? we should have seen
3162 * some escher records earlier */
3163 cell_set_comment (sheet, &pos, author, NULL, NULL);
3165 g_free (author);
3166 } else {
3167 guint len;
3168 GString *comment;
3170 XL_CHECK_CONDITION (q->length >= 6);
3171 len = GSF_LE_GET_GUINT16 (q->data + 4);
3172 comment = g_string_sized_new (len);
3174 for (; len > 2048 ; len -= 2048) {
3175 guint16 opcode;
3177 g_string_append (comment,
3178 excel_biff_text (esheet->container.importer,
3179 q, 6, 2048));
3181 if (!ms_biff_query_peek_next (q, &opcode) ||
3182 opcode != BIFF_NOTE || !ms_biff_query_next (q) ||
3183 q->length < 4 ||
3184 XL_GETROW (q) != 0xffff || XL_GETCOL (q) != 0) {
3185 g_warning ("Invalid Comment record");
3186 g_string_free (comment, TRUE);
3187 return;
3190 g_string_append (comment, excel_biff_text (esheet->container.importer, q, 6, len));
3192 d (2, g_printerr ("Comment in %s%d: '%s'\n",
3193 col_name (pos.col), pos.row + 1, comment->str););
3195 cell_set_comment (sheet, &pos, NULL, comment->str, NULL);
3196 g_string_free (comment, TRUE);
3200 static void
3201 excel_sheet_destroy (ExcelReadSheet *esheet)
3203 if (esheet == NULL)
3204 return;
3206 if (esheet->shared_formulae != NULL) {
3207 g_hash_table_destroy (esheet->shared_formulae);
3208 esheet->shared_formulae = NULL;
3210 if (esheet->tables != NULL) {
3211 g_hash_table_destroy (esheet->tables);
3212 esheet->tables = NULL;
3215 /* There appear to be workbooks like guai.xls that have a filter NAME
3216 * defined but no visible combos, so we remove a filter if it has no
3217 * objects */
3218 if (esheet->filter != NULL) {
3219 gnm_filter_remove (esheet->filter);
3220 gnm_filter_unref (esheet->filter);
3221 esheet->filter = NULL;
3224 ms_container_finalize (&esheet->container);
3226 g_free (esheet);
3229 static GnmExprTop const *
3230 ms_wb_parse_expr (MSContainer *container, guint8 const *data, int length)
3232 ExcelReadSheet dummy_sheet;
3233 memset (&dummy_sheet, 0, sizeof (dummy_sheet));
3234 dummy_sheet.container.importer = (GnmXLImporter *)container;
3235 return ms_sheet_parse_expr_internal (&dummy_sheet, data, length);
3238 static GOFormat *
3239 ms_wb_get_fmt (MSContainer const *container, unsigned indx)
3241 return excel_wb_get_fmt (((GnmXLImporter *)container), indx);
3244 static void
3245 add_attr (PangoAttrList *attr_list, PangoAttribute *attr)
3247 attr->start_index = 0;
3248 attr->end_index = 0;
3249 pango_attr_list_insert (attr_list, attr);
3252 static PangoAttrList *
3253 ms_wb_get_font_markup (MSContainer const *c, unsigned indx)
3255 GnmXLImporter *importer = (GnmXLImporter *)c;
3256 ExcelFont const *fd = excel_font_get (importer, indx);
3258 if (fd == NULL || indx == 0)
3259 return empty_attr_list;
3261 if (fd->attrs == NULL) {
3262 ExcelFont const *fd0 = excel_font_get (importer, 0);
3263 PangoAttrList *attrs;
3265 attrs = pango_attr_list_new ();
3266 if (strcmp (fd->fontname, fd0->fontname) != 0)
3267 add_attr (attrs, pango_attr_family_new (fd->fontname));
3268 if (fd->height != fd0->height)
3269 add_attr (attrs, pango_attr_size_new (fd->height * PANGO_SCALE / 20));
3270 if (fd->boldness != fd0->boldness)
3271 add_attr (attrs, pango_attr_weight_new (fd->boldness));
3272 if (fd->italic != fd0->italic)
3273 add_attr (attrs, pango_attr_style_new (fd->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
3274 if (fd->struck_out != fd0->struck_out)
3275 add_attr (attrs, pango_attr_strikethrough_new (fd->struck_out));
3276 if (fd->underline != fd0->underline) {
3277 PangoUnderline underline = gnm_translate_underline_to_pango
3278 (xls_uline_to_gnm_underline (fd->underline));
3279 add_attr (attrs, pango_attr_underline_new (underline));
3282 switch (fd->script) {
3283 case GO_FONT_SCRIPT_SUB:
3284 add_attr (attrs, go_pango_attr_subscript_new (TRUE));
3285 break;
3286 default:
3287 case GO_FONT_SCRIPT_STANDARD:
3288 /* Just assume fd0 is standard. */
3289 break;
3290 case GO_FONT_SCRIPT_SUPER:
3291 add_attr (attrs, go_pango_attr_superscript_new (TRUE));
3292 break;
3295 if (fd->color_idx != fd0->color_idx) {
3296 GnmColor *color = (fd->color_idx == 127)
3297 ? style_color_black ()
3298 : excel_palette_get (importer, fd->color_idx);
3299 add_attr (attrs, go_color_to_pango (color->go_color, TRUE));
3300 style_color_unref (color);
3303 ((ExcelFont *)fd)->attrs = attrs;
3306 return fd->attrs;
3309 static gint
3310 gnm_xl_get_codepage (char const *enc)
3312 /* These names must match charset_trans_array in go-charmap-sel.c */
3313 static struct {
3314 char const *name;
3315 gint codepage;
3316 } charset_trans_array[] = {
3317 {"IBM864", 0},
3318 {"IBM864i", 0},
3319 {"ISO-8859-6", 0},
3320 {"ISO-8859-6-E", 0},
3321 {"ISO-8859-6-I", 0},
3322 {"x-mac-arabic", 0},
3323 {"windows-1256", 1256},
3324 {"armscii-8", 0},
3325 {"ISO-8859-13", 0},
3326 {"ISO-8859-4", 0},
3327 {"windows-1257", 1257},
3328 {"ISO-8859-14", 0},
3329 {"IBM852", 0},
3330 {"ISO-8859-2", 0},
3331 {"x-mac-ce", 0},
3332 {"windows-1250", 1250},
3333 {"gb18030", 0},
3334 {"GB2312", 0},
3335 {"x-gbk", 0},
3336 {"HZ-GB-2312", 0},
3337 {"windows-936", 936},
3338 {"Big5", 0},
3339 {"Big5-HKSCS", 0},
3340 {"x-euc-tw", 0},
3341 {"x-mac-croatian", 0},
3342 {"IBM855", 0},
3343 {"ISO-8859-5", 0},
3344 {"ISO-IR-111", 0},
3345 {"KOI8-R", 0},
3346 {"x-mac-cyrillic", 0},
3347 {"windows-1251", 1251},
3348 {"IBM866", 0},
3349 {"KOI8-U", 0},
3350 {"x-mac-ukrainian", 0},
3351 {"ANSI_X3.4-1968#ASCII", 0},
3352 {"x-mac-farsi", 0},
3353 {"geostd8", 0},
3354 {"ISO-8859-7", 0},
3355 {"x-mac-greek", 0},
3356 {"windows-1253", 0},
3357 {"x-mac-gujarati", 0},
3358 {"x-mac-gurmukhi", 0},
3359 {"IBM862", 0},
3360 {"ISO-8859-8-E", 0},
3361 {"ISO-8859-8-I", 0},
3362 {"x-mac-hebrew", 0},
3363 {"windows-1255", 1255},
3364 {"x-mac-devanagari", 0},
3365 {"x-mac-icelandic", 0},
3366 {"EUC-JP", 0},
3367 {"ISO-2022-JP", 0},
3368 {"CP932", 0},
3369 {"EUC-KR", 0},
3370 {"ISO-2022-KR", 0},
3371 {"x-johab", 0},
3372 {"x-windows-949", 0},
3373 {"ISO-8859-10", 0},
3374 {"x-mac-romanian", 0},
3375 {"ISO-8859-16", 0},
3376 {"ISO-8859-3", 0},
3377 {"TIS-620", 0},
3378 {"IBM857", 0},
3379 {"ISO-8859-9", 0},
3380 {"x-mac-turkish", 0},
3381 {"windows-1254", 1254},
3382 {"UTF-7", 0},
3383 {"UTF-8", 0},
3384 {"UTF-16BE", 0},
3385 {"UTF-16LE", 0},
3386 {"UTF-32BE", 0},
3387 {"UTF-32LE", 0},
3388 {"x-user-defined", 0},
3389 {"x-viet-tcvn5712", 0},
3390 {"VISCII", 0},
3391 {"x-viet-vps", 0},
3392 {"windows-1258", 1258},
3393 {"ISO-8859-8", 0},
3394 {"IBM850", 0},
3395 {"ISO-8859-1", 0},
3396 {"ISO-8859-15", 0},
3397 {"x-mac-roman", 0},
3398 {"windows-1252", 1252},
3399 {"T61.8bit", 0},
3400 {"x-imap4-modified-utf7", 0},
3401 {"x-u-escaped", 0}
3403 guint i;
3405 if (enc == NULL)
3406 return 0;
3408 for (i = 0; i < G_N_ELEMENTS(charset_trans_array); i++)
3409 if (0 == strcmp (enc, charset_trans_array[i].name))
3410 return charset_trans_array[i].codepage;
3412 return 0;
3415 static GnmXLImporter *
3416 gnm_xl_importer_new (GOIOContext *context, WorkbookView *wb_view, char const *opt_enc)
3418 static MSContainerClass const vtbl = {
3419 NULL, NULL,
3420 &ms_wb_parse_expr,
3421 NULL,
3422 &ms_wb_get_fmt,
3423 &ms_wb_get_font_markup
3426 GnmXLImporter *importer = g_new (GnmXLImporter, 1);
3428 importer->ver = MS_BIFF_V_UNKNOWN;
3429 importer->context = context;
3430 importer->wbv = wb_view;
3431 importer->wb = wb_view_get_workbook (wb_view);
3432 importer->str_iconv = (GIConv)(-1);
3433 importer->codepage_override = gnm_xl_get_codepage (opt_enc);
3434 gnm_xl_importer_set_codepage (importer, (importer->codepage_override > 0) ?
3435 importer->codepage_override : 1252); /* set a default */
3437 importer->expr_sharer = gnm_expr_sharer_new ();
3438 importer->v8.supbook = g_array_new (FALSE, FALSE, sizeof (ExcelSupBook));
3439 importer->v8.externsheet = NULL;
3441 importer->names = g_ptr_array_new ();
3442 importer->num_name_records = 0;
3444 importer->boundsheet_sheet_by_index = g_ptr_array_new ();
3445 importer->boundsheet_data_by_stream = g_hash_table_new_full (
3446 g_direct_hash, g_direct_equal,
3447 NULL, (GDestroyNotify) biff_boundsheet_data_destroy);
3448 importer->font_data = g_hash_table_new_full (
3449 g_direct_hash, g_direct_equal,
3450 NULL, (GDestroyNotify)excel_font_free);
3451 importer->excel_sheets = g_ptr_array_new ();
3452 importer->XF_cell_records = g_ptr_array_new ();
3453 importer->pivot.cache_by_index = g_ptr_array_new ();
3454 importer->pivot.slicer = NULL;
3455 importer->pivot.field_count = 0;
3456 importer->pivot.record_count = 0;
3457 importer->format_table = g_hash_table_new_full (
3458 g_direct_hash, g_direct_equal,
3459 NULL, (GDestroyNotify)biff_format_data_destroy);
3460 importer->palette = NULL;
3461 importer->sst = NULL;
3462 importer->sst_len = 0;
3464 ms_container_init (&importer->container, &vtbl, NULL, importer);
3465 return importer;
3468 static void
3469 excel_workbook_reset_style (GnmXLImporter *importer)
3471 unsigned i;
3473 g_hash_table_destroy (importer->font_data);
3474 importer->font_data = g_hash_table_new_full (
3475 g_direct_hash, g_direct_equal,
3476 NULL, (GDestroyNotify)excel_font_free);
3478 for (i = 0; i < importer->XF_cell_records->len; i++)
3479 biff_xf_data_destroy (g_ptr_array_index (importer->XF_cell_records, i));
3480 g_ptr_array_free (importer->XF_cell_records, TRUE);
3481 importer->XF_cell_records = g_ptr_array_new ();
3483 g_hash_table_destroy (importer->format_table);
3484 importer->format_table = g_hash_table_new_full (
3485 g_direct_hash, g_direct_equal,
3486 NULL, (GDestroyNotify)biff_format_data_destroy);
3489 static void
3490 gnm_xl_importer_free (GnmXLImporter *importer)
3492 unsigned i, j;
3493 GSList *real_order = NULL;
3494 Sheet *sheet;
3496 for (i = importer->boundsheet_sheet_by_index->len; i-- > 0 ; ) {
3497 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, i);
3498 if (sheet != NULL)
3499 real_order = g_slist_prepend (real_order, sheet);
3502 if (real_order != NULL) {
3503 workbook_sheet_reorder (importer->wb, real_order);
3504 g_slist_free (real_order);
3507 gnm_expr_sharer_destroy (importer->expr_sharer);
3509 g_hash_table_destroy (importer->boundsheet_data_by_stream);
3510 importer->boundsheet_data_by_stream = NULL;
3511 g_ptr_array_free (importer->boundsheet_sheet_by_index, TRUE);
3512 importer->boundsheet_sheet_by_index = NULL;
3514 for (i = 0; i < importer->excel_sheets->len; i++)
3515 excel_sheet_destroy (g_ptr_array_index (importer->excel_sheets, i));
3516 g_ptr_array_free (importer->excel_sheets, TRUE);
3517 importer->excel_sheets = NULL;
3519 if (NULL != importer->pivot.slicer) {
3520 g_object_unref (importer->pivot.slicer);
3521 importer->pivot.slicer = NULL;
3523 for (i = 0; i < importer->pivot.cache_by_index->len; i++) {
3524 GObject *cache = g_ptr_array_index (importer->pivot.cache_by_index, i);
3525 if (NULL != cache)
3526 g_object_unref (cache);
3528 g_ptr_array_free (importer->pivot.cache_by_index, TRUE);
3529 importer->pivot.cache_by_index = NULL;
3531 for (i = 0; i < importer->XF_cell_records->len; i++)
3532 biff_xf_data_destroy (g_ptr_array_index (importer->XF_cell_records, i));
3533 g_ptr_array_free (importer->XF_cell_records, TRUE);
3534 importer->XF_cell_records = NULL;
3536 g_hash_table_destroy (importer->font_data);
3537 importer->font_data = NULL;
3539 g_hash_table_destroy (importer->format_table);
3540 importer->format_table = NULL;
3542 if (importer->palette) {
3543 excel_palette_destroy (importer->palette);
3544 importer->palette = NULL;
3547 for (i = 0; i < importer->v8.supbook->len; i++ ) {
3548 ExcelSupBook *sup = &(g_array_index (importer->v8.supbook,
3549 ExcelSupBook, i));
3550 for (j = 0; j < sup->externname->len; j++ ) {
3551 GnmNamedExpr *nexpr = g_ptr_array_index (sup->externname, j);
3552 if (nexpr)
3553 expr_name_unref (nexpr);
3555 g_ptr_array_free (sup->externname, TRUE);
3557 g_array_free (importer->v8.supbook, TRUE);
3558 importer->v8.supbook = NULL;
3559 if (importer->v8.externsheet != NULL) {
3560 g_array_free (importer->v8.externsheet, TRUE);
3561 importer->v8.externsheet = NULL;
3564 if (importer->sst != NULL) {
3565 unsigned i = importer->sst_len;
3566 while (i-- > 0) {
3567 go_string_unref (importer->sst[i].content);
3568 go_format_unref (importer->sst[i].markup);
3570 g_free (importer->sst);
3573 for (i = importer->names->len; i-- > 0 ; ) {
3574 GnmNamedExpr *nexpr = g_ptr_array_index (importer->names, i);
3575 if (!nexpr)
3576 continue;
3578 /* NAME placeholders need removal, EXTERNNAME
3579 * placeholders will no be active */
3580 if (expr_name_is_active (nexpr) &&
3581 expr_name_is_placeholder (nexpr) &&
3582 /* FIXME: Why do we need this? */
3583 nexpr->ref_count == 2) {
3584 d (1, g_printerr ("Removing name %s\n", expr_name_name (nexpr)););
3585 expr_name_remove (nexpr);
3587 expr_name_unref (nexpr);
3589 g_ptr_array_free (importer->names, TRUE);
3590 importer->names = NULL;
3592 if (importer->str_iconv != (GIConv)(-1)) {
3593 gsf_iconv_close (importer->str_iconv);
3594 importer->str_iconv = (GIConv)(-1);
3597 ms_container_finalize (&importer->container);
3598 g_free (importer);
3602 * Unpacks a MS Excel RK structure,
3604 static GnmValue *
3605 biff_get_rk (guint8 const *ptr)
3607 gint32 number;
3608 enum eType {
3609 eIEEE = 0, eIEEEx100 = 1, eInt = 2, eIntx100 = 3
3610 } type;
3612 number = GSF_LE_GET_GUINT32 (ptr);
3613 type = (number & 0x3);
3614 switch (type) {
3615 case eIEEE:
3616 case eIEEEx100:
3618 guint8 tmp[8];
3619 gnm_float answer;
3620 int lp;
3622 /* Think carefully about big/little endian issues before
3623 changing this code. */
3624 for (lp = 0; lp < 4; lp++) {
3625 tmp[lp + 4]= (lp > 0) ? ptr[lp]: (ptr[lp] & 0xfc);
3626 tmp[lp] = 0;
3629 answer = (gnm_float)gsf_le_get_double (tmp);
3630 return value_new_float (type == eIEEEx100 ? answer / 100 : answer);
3632 case eInt:
3633 return value_new_int (number >> 2);
3634 case eIntx100:
3635 number >>= 2;
3636 if ((number % 100) == 0)
3637 return value_new_int (number / 100);
3638 else
3639 return value_new_float ((gnm_float)number / 100);
3641 while (1) abort ();
3644 static char const *
3645 excel_builtin_name (guint8 const *ptr)
3647 switch (*ptr) {
3648 case 0x00: return "Consolidate_Area";
3649 case 0x01: return "Auto_Open";
3650 case 0x02: return "Auto_Close";
3651 case 0x03: return "Extract";
3652 case 0x04: return "Database";
3653 case 0x05: return "Criteria";
3654 case 0x06: return "Print_Area";
3655 case 0x07: return "Print_Titles";
3656 case 0x08: return "Recorder";
3657 case 0x09: return "Data_Form";
3658 case 0x0A: return "Auto_Activate";
3659 case 0x0B: return "Auto_Deactivate";
3660 case 0x0C: return "Sheet_Title";
3661 case 0x0D: return "_FilterDatabase";
3663 default:
3664 g_warning ("Unknown builtin named expression %d", (int)*ptr);
3666 return NULL;
3669 static GnmNamedExpr *
3670 excel_parse_name (GnmXLImporter *importer, Sheet *sheet, char *name,
3671 guint8 const *expr_data, unsigned expr_len,
3672 unsigned array_data_len,
3673 gboolean link_to_container,
3674 GnmNamedExpr *stub)
3676 GnmParsePos pp;
3677 GnmNamedExpr *nexpr;
3678 GnmExprTop const *texpr = NULL;
3679 char *err = NULL;
3681 g_return_val_if_fail (name != NULL, NULL);
3683 parse_pos_init (&pp, importer->wb, sheet, 0, 0);
3685 if (expr_len == 0) {
3686 /* This seems to indicate a placeholder for an unknown name */
3687 texpr = gnm_expr_top_new_constant (value_new_error_NAME (NULL));
3688 } else {
3689 texpr = excel_parse_formula (&importer->container, NULL, 0, 0,
3690 expr_data, expr_len,
3691 array_data_len,
3692 TRUE, NULL);
3694 if (texpr == NULL) {
3695 go_io_warning (importer->context, _("Failure parsing name '%s'"), name);
3696 texpr = gnm_expr_top_new_constant (value_new_error_REF (NULL));
3697 } else d (2, {
3698 char *tmp = gnm_expr_top_as_string
3699 (texpr, &pp, gnm_conventions_default);
3700 g_printerr ("Expression: %s\n", tmp);
3701 g_free (tmp);
3705 if (0 == strcmp (name, "Print_Area")) {
3706 GnmValue *val = gnm_expr_get_range (texpr->expr);
3707 if (val != NULL && VALUE_IS_CELLRANGE (val)) {
3708 int height, width;
3710 if (sheet == NULL) {
3711 Sheet *start_sheet, *end_sheet;
3712 GnmRange dest;
3714 /* Turn a global Print_Area name into a local
3715 name for the sheet it specifies. This
3716 triggers on the file from 632050. */
3717 gnm_rangeref_normalize_pp (value_get_rangeref (val),
3718 &pp,
3719 &start_sheet,
3720 &end_sheet,
3721 &dest);
3722 if (start_sheet && end_sheet == start_sheet) {
3723 sheet = start_sheet;
3724 pp.sheet = sheet;
3725 gnm_expr_top_unref (texpr);
3726 texpr = gnm_expr_top_new_constant (value_new_cellrange_r (NULL, &dest));
3730 if (sheet) {
3731 GnmRange r;
3733 range_init_rangeref (&r, value_get_rangeref (val));
3734 height = range_height (&r);
3735 width = range_width (&r);
3736 if (height == gnm_sheet_get_max_rows (sheet) &&
3737 width == gnm_sheet_get_max_cols (sheet)) {
3738 gnm_expr_top_unref (texpr);
3739 texpr = NULL;
3743 value_release (val);
3745 /* Completely ignore Print_Area settings of #REF! */
3746 if (texpr == NULL || gnm_expr_top_is_err (texpr, GNM_ERROR_REF)) {
3747 if (texpr) gnm_expr_top_unref (texpr);
3748 return NULL;
3752 nexpr = expr_name_add (&pp, name,
3753 texpr,
3754 &err, link_to_container, stub);
3755 if (nexpr == NULL) {
3756 go_io_warning (importer->context, "%s", err);
3757 g_free (err);
3758 return NULL;
3761 return nexpr;
3764 static char *
3765 excel_read_name_str (GnmXLImporter *importer,
3766 guint8 const *data, unsigned datalen, unsigned *name_len, gboolean is_builtin)
3768 gboolean use_utf16, has_extended;
3769 unsigned trailing_data_len, n_markup;
3770 char *name = NULL;
3772 /* Lovely, they put suffixes on builtins. Then those !#$^
3773 * dipsticks put the unicode header _before_ the builtin id
3774 * and stored the id as a character (possibly two byte).
3775 * NOTE : len is in _characters_ (not bytes) does not include
3776 * the header */
3777 if (is_builtin && *name_len) {
3778 guint8 const *str = data;
3779 char const *builtin;
3780 unsigned clen;
3782 if (importer->ver < MS_BIFF_V8) {
3783 use_utf16 = has_extended = FALSE;
3784 n_markup = trailing_data_len = 0;
3785 } else {
3786 int hlen = excel_read_string_header
3787 (str, datalen,
3788 &use_utf16, &n_markup, &has_extended,
3789 &trailing_data_len);
3790 str += hlen;
3791 datalen -= hlen;
3794 clen = use_utf16 ? 2 : 1;
3796 /* pull out the magic builtin enum */
3797 if (datalen >= clen) {
3798 builtin = excel_builtin_name (str);
3799 str += clen;
3800 datalen -= clen;
3801 } else
3802 builtin = "bogus";
3804 if (--(*name_len)) {
3805 char *tmp;
3807 *name_len = MIN (*name_len, datalen / clen);
3808 tmp = excel_get_chars (importer, str, *name_len, use_utf16, NULL);
3809 name = g_strconcat (builtin, tmp, NULL);
3810 g_free (tmp);
3811 *name_len = clen * (*name_len);
3812 } else
3813 name = g_strdup (builtin);
3815 *name_len += str - data;
3816 } else /* converts char len to byte len, and handles header */
3817 name = excel_get_text (importer, data, *name_len, name_len, NULL, datalen);
3818 return name;
3821 static void
3822 excel_read_EXTERNNAME (BiffQuery *q, MSContainer *container)
3824 MsBiffVersion const ver = container->importer->ver;
3825 GnmNamedExpr *nexpr = NULL;
3826 char *name = NULL;
3827 unsigned array_data_len = 0; /* Is this always true? */
3829 d (2, {
3830 g_printerr ("EXTERNNAME\n");
3831 gsf_mem_dump (q->data, q->length); });
3833 /* use biff version to differentiate, not the record version because
3834 * the version is the same for very old and new, with _v2 used for
3835 * some intermediate variants */
3836 if (ver >= MS_BIFF_V7) {
3837 unsigned expr_len = 0;
3838 guint8 const *expr_data = NULL;
3839 guint16 flags;
3840 guint32 namelen;
3842 XL_CHECK_CONDITION (q->length >= 7);
3844 flags = GSF_LE_GET_GUINT8 (q->data);
3845 namelen = GSF_LE_GET_GUINT8 (q->data + 6);
3847 name = excel_read_name_str (container->importer, q->data + 7, q->length - 7, &namelen, flags&1);
3848 if ((flags & (~1)) == 0) { /* all flags but builtin must be 0 */
3849 if (7 + 2 + namelen <= q->length) {
3850 unsigned el = GSF_LE_GET_GUINT16 (q->data + 7 + namelen);
3851 if (7 + 2 + namelen + el <= q->length) {
3852 expr_len = el;
3853 expr_data = q->data + 9 + namelen;
3854 } else
3855 go_io_warning (container->importer->context,
3856 _("Incorrect expression for name '%s': content will be lost.\n"),
3857 name);
3859 } else if ((flags & 0x10) == 0) /* DDE */
3860 go_io_warning (container->importer->context,
3861 _("DDE links are not supported yet.\nName '%s' will be lost.\n"),
3862 name ? name : "NULL");
3863 else /* OLE */
3864 go_io_warning (container->importer->context,
3865 _("OLE links are not supported yet.\nName '%s' will be lost.\n"),
3866 name ? name : "NULL");
3868 nexpr = excel_parse_name (container->importer, NULL,
3869 name, expr_data, expr_len,
3870 array_data_len, FALSE, NULL);
3871 } else if (ver >= MS_BIFF_V5) {
3872 XL_CHECK_CONDITION (q->length >= 7);
3874 name = excel_biff_text_1 (container->importer, q, 6);
3875 nexpr = excel_parse_name (container->importer, NULL,
3876 name, NULL, 0, array_data_len,
3877 FALSE, NULL);
3878 } else {
3879 XL_CHECK_CONDITION (q->length >= 3);
3881 name = excel_biff_text_1 (container->importer, q, 2);
3882 nexpr = excel_parse_name (container->importer, NULL,
3883 name, NULL, 0, array_data_len,
3884 FALSE, NULL);
3887 /* nexpr is potentially NULL if there was an error */
3888 if (ver >= MS_BIFF_V8) {
3889 GnmXLImporter *importer = container->importer;
3890 ExcelSupBook const *sup;
3892 g_return_if_fail (importer->v8.supbook->len > 0);
3894 /* The name is associated with the last SUPBOOK records seen */
3895 sup = &(g_array_index (importer->v8.supbook, ExcelSupBook,
3896 importer->v8.supbook->len-1));
3897 g_ptr_array_add (sup->externname, nexpr);
3898 } else {
3899 GPtrArray *externnames = container->v7.externnames;
3900 if (externnames == NULL)
3901 externnames = container->v7.externnames = g_ptr_array_new ();
3902 g_ptr_array_add (externnames, nexpr);
3904 g_free (name);
3907 /* Do some error checking to handle the magic name associated with an
3908 * autofilter in a sheet. Do not make it an error.
3909 * We have lots of examples of things that are not autofilters.
3911 static void
3912 excel_prepare_autofilter (GnmXLImporter *importer, GnmNamedExpr *nexpr)
3914 if (nexpr->pos.sheet != NULL) {
3915 GnmValue *v = gnm_expr_top_get_range (nexpr->texpr);
3916 if (v != NULL) {
3917 GnmSheetRange r;
3918 gboolean valid = gnm_sheet_range_from_value (&r, v);
3919 value_release (v);
3921 if (valid) {
3922 unsigned i;
3923 GnmFilter *filter;
3924 ExcelReadSheet *esheet;
3926 filter = gnm_filter_new (r.sheet, &r.range);
3927 expr_name_remove (nexpr);
3929 for (i = 0 ; i < importer->excel_sheets->len; i++) {
3930 esheet = g_ptr_array_index (importer->excel_sheets, i);
3931 if (esheet->sheet == r.sheet) {
3932 g_return_if_fail (esheet->filter == NULL);
3933 esheet->filter = filter;
3934 break;
3942 static void
3943 excel_read_NAME (BiffQuery *q, GnmXLImporter *importer, ExcelReadSheet *esheet)
3945 MsBiffVersion const ver = importer->ver;
3946 GnmNamedExpr *nexpr = NULL;
3947 guint16 expr_len, sheet_index, flags = 0;
3948 guint8 const *data;
3949 gboolean builtin_name = FALSE;
3950 char *name = NULL;
3951 /* length in characters (not bytes) in the same pos for all versions */
3952 unsigned name_len;
3953 /* guint8 kb_shortcut = GSF_LE_GET_GUINT8 (q->data + 2); */
3954 /* int fn_grp_idx = (flags & 0xfc0)>>6; */
3956 XL_CHECK_CONDITION (q->length >= 4);
3958 name_len = GSF_LE_GET_GUINT8 (q->data + 3);
3960 d (2, {
3961 g_printerr ("NAME\n");
3962 gsf_mem_dump (q->data, q->length); });
3964 if (ver >= MS_BIFF_V2) {
3965 flags = GSF_LE_GET_GUINT16 (q->data);
3966 builtin_name = (flags & 0x0020) != 0;
3969 /* use biff version to differentiate, not the record version because
3970 * the version is the same for very old and new, with _v2 used for
3971 * some intermediate variants */
3972 if (ver >= MS_BIFF_V8) {
3973 XL_CHECK_CONDITION (q->length >= 14);
3974 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3975 sheet_index = GSF_LE_GET_GUINT16 (q->data + 8);
3976 data = q->data + 14;
3977 } else if (ver >= MS_BIFF_V7) {
3978 XL_CHECK_CONDITION (q->length >= 14);
3979 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3980 /* opencalc docs claim 8 is the right one, XL docs say 6 == 8
3981 * pivot.xls suggests that at least for local builtin names 6
3982 * is correct and 8 is bogus for == biff7 */
3983 sheet_index = GSF_LE_GET_GUINT16 (q->data + 6);
3984 data = q->data + 14;
3985 } else if (ver >= MS_BIFF_V3) {
3986 XL_CHECK_CONDITION (q->length >= 6);
3987 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3988 data = q->data + 6;
3989 sheet_index = 0; /* no sheets */
3990 } else {
3991 XL_CHECK_CONDITION (q->length >= 5);
3992 expr_len = GSF_LE_GET_GUINT8 (q->data + 4);
3993 data = q->data + 5;
3994 sheet_index = 0; /* no sheets */
3997 XL_NEED_BYTES (name_len);
3998 name = excel_read_name_str (importer, data, q->length - (data - q->data), &name_len, builtin_name);
3999 XL_NEED_BYTES (name_len);
4000 data += name_len;
4002 if (name != NULL) {
4003 unsigned array_data_len;
4004 Sheet *sheet = NULL;
4005 d (1, g_printerr ("NAME=%s, sheet_index=%d flags=0x%x\n",
4006 name, sheet_index, flags););
4007 if (sheet_index > 0) {
4008 /* NOTE : the docs lie the index for biff7 is
4009 * indeed a reference to the externsheet
4010 * however we have examples in biff8 that can
4011 * only to be explained by a 1 based index to
4012 * the boundsheets. Which is not unreasonable
4013 * given that these are local names */
4014 if (importer->ver >= MS_BIFF_V8) {
4015 if (sheet_index <= importer->boundsheet_sheet_by_index->len &&
4016 sheet_index > 0)
4017 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, sheet_index-1);
4018 else
4019 g_warning ("So much for that theory 2");
4020 } else
4021 sheet = excel_externsheet_v7 (&importer->container, sheet_index);
4024 if (sheet == XL_EXTERNSHEET_MAGIC_SELFREF)
4025 sheet = esheet ? esheet->sheet : NULL;
4026 else if (sheet == XL_EXTERNSHEET_MAGIC_DELETED)
4027 sheet = NULL;
4029 /* do we have a stub from a forward decl ? */
4030 if (importer->num_name_records < importer->names->len)
4031 nexpr = g_ptr_array_index (importer->names, importer->num_name_records);
4033 XL_NEED_BYTES (expr_len);
4034 array_data_len = expr_len ? q->length - (data - q->data) - expr_len : 0;
4035 nexpr = excel_parse_name (importer, sheet,
4036 name, data, expr_len,
4037 array_data_len, TRUE, nexpr);
4038 g_free (name);
4039 data += expr_len;
4041 /* Add a ref to keep it around after the excel-sheet/wb goes
4042 * away. externnames do not get references and are unrefed
4043 * after import finishes, which destroys them if they are not
4044 * in use. */
4045 if (nexpr != NULL) {
4046 expr_name_ref (nexpr);
4047 nexpr->is_hidden = (flags & 0x0001) ? TRUE : FALSE;
4049 /* Undocumented magic.
4050 * XL stores a hidden name with the details of an autofilter */
4051 if (nexpr->is_hidden && !strcmp (expr_name_name (nexpr), "_FilterDatabase"))
4052 excel_prepare_autofilter (importer, nexpr);
4053 /* g_warning ("flags = %hx, state = %s\n", flags, global ? "global" : "sheet"); */
4055 else if ((flags & 0xE) == 0xE) /* Function & VB-Proc & Proc */
4056 gnm_func_add_placeholder (importer->wb,
4057 expr_name_name (nexpr), "VBA");
4061 /* nexpr is potentially NULL if there was an error */
4062 if (importer->num_name_records < importer->names->len)
4063 g_ptr_array_index (importer->names, importer->num_name_records) = nexpr;
4064 else if (importer->num_name_records == importer->names->len)
4065 g_ptr_array_add (importer->names, nexpr);
4066 importer->num_name_records++;
4068 d (5, {
4069 guint8 menu_txt_len = GSF_LE_GET_GUINT8 (q->data + 10);
4070 guint8 descr_txt_len = GSF_LE_GET_GUINT8 (q->data + 11);
4071 guint8 help_txt_len = GSF_LE_GET_GUINT8 (q->data + 12);
4072 guint8 status_txt_len = GSF_LE_GET_GUINT8 (q->data + 13);
4073 const guint8 *end = q->data + q->length;
4074 char *menu_txt;
4075 char *descr_txt;
4076 char *help_txt;
4077 char *status_txt;
4079 menu_txt = excel_get_text (importer, data, menu_txt_len, NULL, NULL, end - data);
4080 data += menu_txt_len;
4081 descr_txt = excel_get_text (importer, data, descr_txt_len, NULL, NULL, end - data);
4082 data += descr_txt_len;
4083 help_txt = excel_get_text (importer, data, help_txt_len, NULL, NULL, end - data);
4084 data += help_txt_len;
4085 status_txt = excel_get_text (importer, data, status_txt_len, NULL, NULL, end - data);
4087 g_printerr ("Name record: '%s', '%s', '%s', '%s', '%s'\n",
4088 nexpr ? expr_name_name (nexpr) : "(null)",
4089 menu_txt ? menu_txt : "(null)",
4090 descr_txt ? descr_txt : "(null)",
4091 help_txt ? help_txt : "(null)",
4092 status_txt ? status_txt : "(null)");
4094 if ((flags & 0x0001) != 0) g_printerr (" Hidden");
4095 if ((flags & 0x0002) != 0) g_printerr (" Function");
4096 if ((flags & 0x0004) != 0) g_printerr (" VB-Proc");
4097 if ((flags & 0x0008) != 0) g_printerr (" Proc");
4098 if ((flags & 0x0010) != 0) g_printerr (" CalcExp");
4099 if ((flags & 0x0020) != 0) g_printerr (" BuiltIn");
4100 if ((flags & 0x1000) != 0) g_printerr (" BinData");
4101 g_printerr ("\n");
4103 g_free (menu_txt);
4104 g_free (descr_txt);
4105 g_free (help_txt);
4106 g_free (status_txt);
4110 static void
4111 excel_read_XCT (BiffQuery *q, GnmXLImporter *importer)
4113 guint16 last_col, opcode;
4114 guint8 const *data;
4115 unsigned len;
4116 int count;
4117 Sheet *sheet = NULL;
4118 GnmCell *cell;
4119 GnmValue *v;
4120 GnmEvalPos ep;
4122 if (importer->ver >= MS_BIFF_V8) {
4123 guint16 supbook;
4125 XL_CHECK_CONDITION (q->length == 4);
4127 count = GSF_LE_GET_GINT16 (q->data);
4128 supbook = GSF_LE_GET_GUINT16 (q->data+2);
4129 } else {
4130 XL_CHECK_CONDITION (q->length == 2);
4132 count = GSF_LE_GET_GINT16 (q->data);
4135 if (count < 0) /* WHAT THE HECK DOES NEGATIVE MEAN ?? */
4136 count = -count;
4138 if (sheet != NULL)
4139 eval_pos_init_sheet (&ep, sheet);
4141 while (count-- > 0) {
4142 if (!ms_biff_query_peek_next (q, &opcode)) {
4143 g_warning ("Expected a CRN record");
4144 return;
4145 } else if (opcode != BIFF_CRN) {
4146 g_warning ("Expected a CRN record not a %hx", opcode);
4147 return;
4149 ms_biff_query_next (q);
4151 XL_CHECK_CONDITION (q->length >= 4);
4153 ep.eval.col = GSF_LE_GET_GUINT8 (q->data+0);
4154 last_col = GSF_LE_GET_GUINT8 (q->data+1);
4155 ep.eval.row = GSF_LE_GET_GUINT16 (q->data+2);
4157 /* ignore content for sheets that are already loaded */
4158 if (sheet == NULL)
4159 continue;
4161 for (data = q->data + 4; ep.eval.col <= last_col ; ep.eval.col++) {
4162 guint8 oper;
4163 XL_NEED_BYTES (1);
4165 oper = *data++;
4166 switch (oper) {
4167 case 1:
4168 XL_NEED_BYTES (8);
4169 v = value_new_float (GSF_LE_GET_DOUBLE (data));
4170 data += 8;
4171 break;
4172 case 2:
4173 XL_NEED_BYTES (1);
4174 len = *data++;
4175 v = value_new_string_nocopy (
4176 excel_get_text (importer, data, len, NULL, NULL, q->data + q->length - data));
4177 data += len;
4178 break;
4180 case 4:
4181 XL_NEED_BYTES (2);
4182 v = value_new_bool (GSF_LE_GET_GUINT16 (data) != 0);
4183 /* FIXME: 8?? */
4184 data += 8;
4185 break;
4187 case 16:
4188 XL_NEED_BYTES (2);
4189 v = xls_value_new_err (&ep, GSF_LE_GET_GUINT16 (data));
4190 /* FIXME: 8?? */
4191 data += 8;
4192 break;
4194 default :
4195 g_warning ("Unknown oper type 0x%x in a CRN record", oper);
4196 v = NULL;
4199 if (v != NULL) {
4200 cell = sheet_cell_fetch (sheet, ep.eval.col, ep.eval.row);
4201 if (cell)
4202 gnm_cell_set_value (cell, v);
4203 else
4204 value_release (v);
4210 static XL_font_width const *
4211 xl_find_fontspec (ExcelReadSheet *esheet, double *size20)
4213 /* Use the 'Normal' Style which is by definition the 0th */
4214 BiffXFData const *xf = excel_get_xf (esheet, 0);
4215 ExcelFont const *fd = (xf != NULL)
4216 ? excel_font_get (esheet->container.importer, xf->font_idx)
4217 : NULL;
4218 *size20 = (fd != NULL) ? (fd->height / (20. * 10.)) : 1.;
4219 return xl_lookup_font_specs ((fd != NULL) ? fd->fontname : "Arial");
4223 * get_row_height_units:
4224 * @height height in Excel units
4226 * Converts row height from Excel units to points. Returns height in points.
4228 * Excel specifies row height in 1/20 of a point.
4230 * What we now print out is just 0.5% shorter than theoretical
4231 * height. The height of what Excel prints out varies in mysterious
4232 * ways. Sometimes it is close to theoretical, sometimes it is a few %
4233 * shorter. I don't see any point in correcting for the 0.5% until we
4234 * know the whole story.
4236 static double
4237 get_row_height_units (guint16 height)
4239 return 1. / 20. * height;
4242 static void
4243 excel_read_ROW (BiffQuery *q, ExcelReadSheet *esheet)
4245 guint16 row, height;
4246 guint16 flags = 0;
4247 guint16 flags2 = 0;
4248 guint16 xf;
4249 gboolean is_std_height;
4251 XL_CHECK_CONDITION (q->length >= (q->opcode == BIFF_ROW_v2 ? 16 : 8));
4253 row = GSF_LE_GET_GUINT16 (q->data);
4254 #if 0
4255 /* Unnecessary info for now.
4256 * do we want to preallocate based on this info?
4258 guint16 const start_col = GSF_LE_GET_GUINT16 (q->data + 2);
4259 guint16 const end_col = GSF_LE_GET_GUINT16 (q->data + 4) - 1;
4260 #endif
4261 height = GSF_LE_GET_GUINT16 (q->data + 6);
4263 /* If the bit is on it indicates that the row is of 'standard' height.
4264 * However the remaining bits still include the size.
4266 is_std_height = (height & 0x8000) != 0;
4268 if (q->opcode == BIFF_ROW_v2) {
4269 flags = GSF_LE_GET_GUINT16 (q->data + 12);
4270 flags2 = GSF_LE_GET_GUINT16 (q->data + 14);
4272 xf = flags2 & 0xfff;
4274 d (1, {
4275 g_printerr ("Row %d height 0x%x, flags=0x%x 0x%x;\n", row + 1, height, flags, flags2);
4276 if (is_std_height)
4277 g_printerr ("%s\n", "Is Std Height;\n");
4278 if (flags2 & 0x1000)
4279 g_printerr ("%s\n", "Top thick;\n");
4280 if (flags2 & 0x2000)
4281 g_printerr ("%s\n", "Bottom thick;\n");
4284 /* TODO: Put mechanism in place to handle thick margins */
4285 /* TODO: Columns actually set the size even when it is the default.
4286 * Which approach is better?
4288 if (!is_std_height) {
4289 double hu = get_row_height_units (height);
4290 sheet_row_set_size_pts (esheet->sheet, row, hu,
4291 (flags & 0x40) ? TRUE : FALSE);
4294 if (flags & 0x20)
4295 colrow_set_visibility (esheet->sheet, FALSE, FALSE, row, row);
4297 if (flags & 0x80) {
4298 if (xf != 0)
4299 excel_set_xf_segment (esheet,
4300 0, gnm_sheet_get_max_cols (esheet->sheet) - 1,
4301 row, row, xf);
4302 d (1, g_printerr ("row %d has flags 0x%x a default style %hd;\n",
4303 row + 1, flags, xf););
4306 if ((unsigned)(flags & 0x17) > 0)
4307 col_row_info_set_outline (sheet_row_fetch (esheet->sheet, row),
4308 (unsigned)(flags & 0x7), flags & 0x10);
4311 static void
4312 excel_read_TAB_COLOR (BiffQuery *q, ExcelReadSheet *esheet)
4314 /* this is a guess, but the only field I see
4315 * changing seems to be the colour.
4317 #if 0
4318 0 | 62 8 0 0 0 0 0 0 0 0 0 0 14 0 0 0 | b...............
4319 10 | 0 0 0 XX XX XX XX XX XX XX XX XX XX XX XX | ...************
4321 office 12 seems to add 8 bytes
4322 #endif
4323 guint8 color_index;
4324 GnmColor *color;
4325 GnmColor *text_color;
4326 int contrast;
4328 XL_CHECK_CONDITION (q->length >= 20);
4330 /* be conservative for now, we have not seen a palette larger than 56
4331 * so this is largely moot, this is probably a uint32
4333 color_index = GSF_LE_GET_GUINT8 (q->data + 16);
4334 color = excel_palette_get (esheet->container.importer, color_index);
4335 contrast = GO_COLOR_UINT_R (color->go_color) +
4336 GO_COLOR_UINT_G (color->go_color) +
4337 GO_COLOR_UINT_B (color->go_color);
4338 if (contrast >= 0x180)
4339 text_color = style_color_black ();
4340 else
4341 text_color = style_color_white ();
4342 g_object_set (esheet->sheet,
4343 "tab-foreground", text_color,
4344 "tab-background", color,
4345 NULL);
4346 d (1, g_printerr ("%s tab colour = %08x\n",
4347 esheet->sheet->name_unquoted,
4348 color->go_color););
4350 style_color_unref (text_color);
4351 style_color_unref (color);
4354 static void
4355 excel_read_COLINFO (BiffQuery *q, ExcelReadSheet *esheet)
4357 int i;
4358 double scale, width;
4359 guint16 firstcol, lastcol;
4360 int charwidths;
4361 guint16 xf, options;
4362 gboolean hidden, customWidth, bestFit, collapsed;
4363 unsigned outline_level;
4364 XL_font_width const *spec;
4366 XL_CHECK_CONDITION (q->length >= 10);
4368 firstcol = GSF_LE_GET_GUINT16 (q->data);
4369 lastcol = GSF_LE_GET_GUINT16 (q->data + 2);
4370 charwidths = GSF_LE_GET_GUINT16 (q->data + 4);
4371 xf = GSF_LE_GET_GUINT16 (q->data + 6);
4372 options = GSF_LE_GET_GUINT16 (q->data + 8);
4373 hidden = (options & 0x0001) != 0;
4374 customWidth = (options & 0x0002) != 0; /* undocumented */
4375 bestFit = (options & 0x0004) != 0; /* undocumented */
4376 collapsed = (options & 0x1000) != 0;
4377 outline_level = (unsigned)((options >> 8) & 0x7);
4378 spec = xl_find_fontspec (esheet, &scale);
4380 XL_CHECK_CONDITION (firstcol < gnm_sheet_get_max_cols (esheet->sheet));
4381 g_return_if_fail (spec != NULL);
4383 /* Widths appear to be quoted including margins and the leading
4384 * gridline that gnumeric expects. The charwidths here are not
4385 * strictly linear. So I measured in increments of -2 -1 0 1 2 around
4386 * the default width when using each font @ 10pts as
4387 * the Normal Style. The pixel calculation is then reduced to
4389 * (default_size + ((quoted_width - baseline) / step))
4390 * * scale : fonts != 10pts
4391 * * 72/96 : value in pts so that zoom is not a factor
4393 * NOTE: These measurements do NOT correspond to what is shown to the
4394 * user */
4395 width = 8. * spec->defcol_unit +
4396 (double)(charwidths - spec->colinfo_baseline) / spec->colinfo_step;
4397 width *= scale * 72./96.;
4399 if (width <= 0) { /* Columns are of default width */
4400 width = esheet->sheet->cols.default_style.size_pts;
4401 hidden = TRUE;
4402 } else if (width < 4) /* gnumeric can not draw without a margin */
4403 width = 4;
4405 d (1, {
4406 g_printerr ("Column Formatting %s!%s of width "
4407 "%u/256 characters (%f pts)\n",
4408 esheet->sheet->name_quoted,
4409 cols_name (firstcol, lastcol), charwidths, width);
4410 g_printerr ("Options 0x%hx, default style %hu\n", options, xf);
4413 /* NOTE: seems like this is inclusive firstcol, inclusive lastcol */
4414 if (lastcol >= gnm_sheet_get_max_cols (esheet->sheet))
4415 lastcol = gnm_sheet_get_max_cols (esheet->sheet) - 1;
4416 for (i = firstcol; i <= lastcol; i++) {
4417 sheet_col_set_size_pts (esheet->sheet, i, width,
4418 customWidth && !bestFit);
4419 if (outline_level > 0 || collapsed)
4420 col_row_info_set_outline (sheet_col_fetch (esheet->sheet, i),
4421 outline_level, collapsed);
4424 if (xf != 0)
4425 excel_set_xf_segment (esheet, firstcol, lastcol,
4426 0, gnm_sheet_get_max_rows (esheet->sheet) - 1, xf);
4428 if (hidden)
4429 colrow_set_visibility (esheet->sheet, TRUE, FALSE,
4430 firstcol, lastcol);
4433 /* Add a bmp header so that gdk-pixbuf can do the work */
4434 static GdkPixbuf *
4435 excel_read_os2bmp (BiffQuery *q, guint32 image_len)
4437 GError *err = NULL;
4438 GdkPixbufLoader *loader = NULL;
4439 GdkPixbuf *pixbuf = NULL;
4440 gboolean ret = FALSE;
4441 guint8 bmphdr[BMP_HDR_SIZE];
4443 XL_CHECK_CONDITION_VAL (q->length >= 8 && image_len < q->length - 8, NULL);
4445 loader = gdk_pixbuf_loader_new_with_type ("bmp", &err);
4446 if (!loader)
4447 return NULL;
4448 excel_fill_bmp_header(bmphdr, q->data, image_len);
4449 ret = gdk_pixbuf_loader_write (loader, bmphdr, sizeof bmphdr, &err);
4450 if (ret)
4451 ret = gdk_pixbuf_loader_write (loader, q->data+8,
4452 q->length-8, &err);
4453 gdk_pixbuf_loader_close (loader, ret ? &err : NULL);
4454 if (ret) {
4455 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
4456 g_object_ref (pixbuf);
4457 } else {
4458 g_message ("Unable to read OS/2 BMP image: %s\n",
4459 err->message);
4460 g_error_free (err);
4462 g_object_unref (loader);
4463 return pixbuf;
4466 /* When IMDATA or BG_PIC is bitmap, the format is OS/2 BMP, but the
4467 * 14 bytes header is missing.
4469 GdkPixbuf *
4470 excel_read_IMDATA (BiffQuery *q, gboolean keep_image)
4472 guint32 image_len;
4473 GdkPixbuf *pixbuf = NULL;
4474 guint16 format;
4476 XL_CHECK_CONDITION_VAL (q->length >= 8, NULL);
4478 format = GSF_LE_GET_GUINT16 (q->data);
4479 image_len = GSF_LE_GET_GUINT32 (q->data + 4);
4481 switch (format) {
4482 case 0x2: break; /* Windows metafile/Mac pict */
4483 case 0x9: /* OS/2 BMP sans header */
4485 pixbuf = excel_read_os2bmp (q, image_len);
4487 break;
4488 case 0xe: break; /* Native format */
4489 default: break; /* Unknown format */
4492 /* Dump formats which weren't handled above to file */
4493 if (format != 0x9) {
4494 char const *from_name;
4495 char const *format_name;
4496 guint16 const format = GSF_LE_GET_GUINT16 (q->data);
4497 guint16 const from_env = GSF_LE_GET_GUINT16 (q->data + 2);
4499 switch (from_env) {
4500 case 1: from_name = "Windows"; break;
4501 case 2: from_name = "Macintosh"; break;
4502 default: from_name = "Unknown environment?"; break;
4504 switch (format) {
4505 case 0x2:
4506 format_name = (from_env == 1) ? "windows metafile" : "mac pict";
4507 break;
4509 case 0xe: format_name = "'native format'"; break;
4510 default: format_name = "Unknown format?"; break;
4513 d (1, {
4514 FILE *f;
4515 static int count = 0;
4516 char *file_name = g_strdup_printf ("imdata%d", count++);
4517 g_printerr ("Picture from %s in %s format\n",
4518 from_name, format_name);
4520 f = g_fopen (file_name, "w");
4521 fwrite (q->data+8, 1, q->length-8, f);
4522 g_free (file_name);
4523 fclose (f);
4527 return pixbuf;
4530 static void
4531 excel_read_SELECTION (BiffQuery *q, ExcelReadSheet *esheet)
4533 GnmCellPos edit_pos;
4534 unsigned pane_number, i, j, num_refs;
4535 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
4536 GnmRange r;
4538 XL_CHECK_CONDITION (q->length >= 9);
4539 pane_number = GSF_LE_GET_GUINT8 (q->data);
4540 edit_pos.row = GSF_LE_GET_GUINT16 (q->data + 1);
4541 edit_pos.col = GSF_LE_GET_GUINT16 (q->data + 3);
4542 j = GSF_LE_GET_GUINT16 (q->data + 5);
4543 num_refs = GSF_LE_GET_GUINT16 (q->data + 7);
4544 XL_CHECK_CONDITION (q->length >= 9 + 6 * num_refs);
4546 if (pane_number != esheet->active_pane)
4547 return;
4549 /* FIXME: docs say that consequtive records with the same pane
4550 number should be treated as one. No file with this has been
4551 observed. */
4553 d (5, g_printerr ("Start selection in pane #%d\n", pane_number););
4554 d (5, g_printerr ("Cursor: %s in Ref #%d\n", cellpos_as_string (&edit_pos),
4555 j););
4557 g_return_if_fail (sv != NULL);
4559 sv_selection_reset (sv);
4560 for (i = 0; i <= num_refs ; i++) {
4561 GnmCellPos tmp;
4562 unsigned i0 = (i == num_refs) ? j : i;
4564 /* Skip the active; then read it last. */
4565 if (i == j || i0 >= num_refs)
4566 continue;
4568 xls_read_range8 (&r, q->data + 9 + 6 * i0);
4570 d (5, g_printerr ("Ref %d = %s\n", i, range_as_string (&r)););
4572 tmp = (i == num_refs) ? edit_pos : r.start;
4573 sv_selection_add_full (sv,
4574 tmp.col, tmp.row,
4575 r.start.col, r.start.row,
4576 r.end.col, r.end.row,
4577 GNM_SELECTION_MODE_ADD);
4580 if (sv->selections == NULL) {
4581 /* See bug 632050 */
4582 sv_selection_add_pos (sv, 0, 0,
4583 GNM_SELECTION_MODE_ADD);
4584 d (5, g_printerr ("No selection\n"););
4587 d (5, g_printerr ("Done selection\n"););
4590 static void
4591 excel_read_DEF_ROW_HEIGHT (BiffQuery *q, ExcelReadSheet *esheet)
4593 guint16 flags = 0;
4594 guint16 height = 0; /* must be 16 bit */
4595 double height_units;
4597 if (q->opcode != BIFF_DEFAULTROWHEIGHT_v0) {
4598 XL_CHECK_CONDITION (q->length >= 4);
4599 flags = GSF_LE_GET_GUINT16 (q->data);
4600 height = GSF_LE_GET_GUINT16 (q->data + 2);
4601 } else {
4602 XL_CHECK_CONDITION (q->length >= 2);
4603 height = GSF_LE_GET_GUINT16 (q->data);
4604 height &= 0x7fff; /* there seems to be a flag in the top bit */
4607 height_units = get_row_height_units (height);
4608 d (2, {
4609 g_printerr ("Default row height %3.3g;\n", height_units);
4610 if (flags & 0x04)
4611 g_printerr (" + extra space above;\n");
4612 if (flags & 0x08)
4613 g_printerr (" + extra space below;\n");
4616 sheet_row_set_default_size_pts (esheet->sheet, height_units);
4619 static void
4620 excel_read_DEF_COL_WIDTH (BiffQuery *q, ExcelReadSheet *esheet)
4622 guint16 charwidths;
4623 double scale;
4624 XL_font_width const *spec = xl_find_fontspec (esheet, &scale);
4626 XL_CHECK_CONDITION (q->length >= 2);
4627 charwidths = GSF_LE_GET_GUINT16 (q->data);
4628 d (0, g_printerr ("Default column width %hu characters\n", charwidths););
4630 /* According to the tooltip the default width is 8.43 character widths
4631 * and 64 pixels wide (Arial 10) which appears to include margins, and
4632 * the leading gridline That is saved as 8 char widths for
4633 * DEL_COL_WIDTH and 9.14 widths for COLINFO */
4634 sheet_col_set_default_size_pts (esheet->sheet,
4635 charwidths * spec->defcol_unit * scale * 72./96.);
4638 /* we could get this implicitly from the cols/rows
4639 * but this is faster
4641 static void
4642 excel_read_GUTS (BiffQuery *q, ExcelReadSheet *esheet)
4644 int col_gut, row_gut;
4646 XL_CHECK_CONDITION (q->length == 8);
4648 /* ignore the specification of how wide/tall the gutters are */
4649 row_gut = GSF_LE_GET_GUINT16 (q->data + 4);
4650 d (2, g_printerr ("row_gut = %d", row_gut););
4651 if (row_gut >= 1)
4652 row_gut--;
4653 col_gut = GSF_LE_GET_GUINT16 (q->data + 6);
4654 d (2, g_printerr ("col_gut = %d\n", col_gut););
4655 if (col_gut >= 1)
4656 col_gut--;
4657 sheet_colrow_gutter (esheet->sheet, TRUE, col_gut);
4658 sheet_colrow_gutter (esheet->sheet, FALSE, row_gut);
4661 static void
4662 excel_read_SETUP (BiffQuery *q, ExcelReadSheet *esheet)
4664 GnmPrintInformation *pi = esheet->sheet->print_info;
4665 guint16 flags;
4666 gboolean rotate_paper = FALSE;
4667 gboolean portrait_orientation = TRUE;
4669 XL_CHECK_CONDITION (q->length >= 12);
4671 flags = GSF_LE_GET_GUINT16 (q->data + 10);
4672 pi->print_across_then_down = (flags & 0x1) != 0;
4673 pi->print_black_and_white = (flags & 0x8) != 0;
4675 if (0 == (flags & 0x4)) {
4676 guint16 papersize = GSF_LE_GET_GUINT16 (q->data + 0);
4677 const char *paper_name =
4678 xls_paper_name (papersize, &rotate_paper);
4680 d (2, g_printerr ("Paper size %hu --> %s\n",
4681 papersize,
4682 paper_name ? paper_name : "-"););
4684 if (paper_name != NULL)
4685 print_info_set_paper (pi, paper_name);
4687 pi->scaling.percentage.x = pi->scaling.percentage.y =
4688 GSF_LE_GET_GUINT16 (q->data + 2);
4689 pi->start_page = GSF_LE_GET_GUINT16 (q->data + 4);
4690 pi->scaling.dim.cols = GSF_LE_GET_GUINT16 (q->data + 6);
4691 pi->scaling.dim.rows = GSF_LE_GET_GUINT16 (q->data + 8);
4692 if (pi->scaling.percentage.x < 1. || pi->scaling.percentage.x > 1000.) {
4693 if (pi->scaling.percentage.x != 0) {
4694 /* 0 seems to be 'auto' */
4695 g_warning ("setting invalid print scaling (%f) to 100%%",
4696 pi->scaling.percentage.x);
4698 pi->scaling.percentage.x = pi->scaling.percentage.y = 100.;
4701 if (esheet_ver (esheet) == MS_BIFF_V4 || 0 == (flags & 0x40))
4702 portrait_orientation = (flags & 0x2) != 0;
4703 if (rotate_paper)
4704 portrait_orientation = !portrait_orientation;
4706 print_info_set_paper_orientation (pi, portrait_orientation
4707 ? GTK_PAGE_ORIENTATION_PORTRAIT
4708 : GTK_PAGE_ORIENTATION_LANDSCAPE);
4711 if (esheet_ver (esheet) > MS_BIFF_V4) {
4712 XL_CHECK_CONDITION (q->length >= 34);
4714 pi->print_as_draft = (flags & 0x10) != 0;
4715 pi->comment_placement = (flags & 0x20)
4716 ? GNM_PRINT_COMMENTS_IN_PLACE : GNM_PRINT_COMMENTS_NONE;
4717 print_info_set_margin_header (pi,
4718 GO_IN_TO_PT (gsf_le_get_double (q->data + 16)));
4719 print_info_set_margin_footer (pi,
4720 GO_IN_TO_PT (gsf_le_get_double (q->data + 24)));
4721 if (0 == (flags & 0x4))
4722 pi->n_copies = GSF_LE_GET_GUINT16 (q->data + 32);
4723 d (2, g_printerr ("resolution %hu vert. res. %hu\n",
4724 GSF_LE_GET_GUINT16 (q->data + 12),
4725 GSF_LE_GET_GUINT16 (q->data + 14)););
4728 if (esheet_ver (esheet) >= MS_BIFF_V8) {
4729 if ((flags & 0x200) &&
4730 pi->comment_placement == GNM_PRINT_COMMENTS_IN_PLACE)
4731 pi->comment_placement = GNM_PRINT_COMMENTS_AT_END;
4732 switch ((flags >> 10) & 3) {
4733 case 0 : pi->error_display = GNM_PRINT_ERRORS_AS_DISPLAYED; break;
4734 case 1 : pi->error_display = GNM_PRINT_ERRORS_AS_BLANK; break;
4735 case 2 : pi->error_display = GNM_PRINT_ERRORS_AS_DASHES; break;
4736 case 3 : pi->error_display = GNM_PRINT_ERRORS_AS_NA; break;
4741 static void
4742 excel_read_MULRK (BiffQuery *q, ExcelReadSheet *esheet)
4744 guint32 col, row, lastcol;
4745 guint8 const *ptr = q->data;
4746 GnmValue *v;
4747 BiffXFData const *xf;
4748 GnmStyle *mstyle;
4750 XL_CHECK_CONDITION (q->length >= 4 + 6 + 2);
4752 row = GSF_LE_GET_GUINT16 (q->data);
4753 col = GSF_LE_GET_GUINT16 (q->data + 2);
4754 ptr += 4;
4755 lastcol = GSF_LE_GET_GUINT16 (q->data + q->length - 2);
4757 XL_CHECK_CONDITION (lastcol >= col);
4758 XL_CHECK_CONDITION (lastcol < (guint32)gnm_sheet_get_max_cols (esheet->sheet));
4760 if (q->length != 4 + 6 * (lastcol - col + 1) + 2) {
4761 int guess = col + (q->length - (4 + 2)) / 6 - 1;
4762 g_warning ("MULRK with strange size: %d vs %d", lastcol, guess);
4763 lastcol = MIN (lastcol, (guint32)MAX (guess, 0));
4766 for (; col <= lastcol ; col++) {
4767 GnmCell *cell;
4768 /* 2byte XF, 4 byte RK */
4769 v = biff_get_rk (ptr + 2);
4770 xf = excel_get_xf (esheet, GSF_LE_GET_GUINT16 (ptr));
4771 mstyle = excel_get_style_from_xf (esheet, xf);
4772 if (mstyle != NULL)
4773 sheet_style_set_pos (esheet->sheet, col, row, mstyle);
4774 if (xf && xf->is_simple_format)
4775 value_set_fmt (v, xf->style_format);
4776 cell = sheet_cell_fetch (esheet->sheet, col, row);
4777 if (cell)
4778 gnm_cell_set_value (cell, v);
4779 else
4780 value_release (v);
4781 ptr += 6;
4785 static void
4786 excel_read_MULBLANK (BiffQuery *q, ExcelReadSheet *esheet)
4788 /* This is an educated guess, docs are not terribly clear */
4789 int firstcol, lastcol, row;
4790 guint8 const *ptr = (q->data + q->length - 2);
4791 int i, range_end, prev_xf, xf_index;
4793 XL_CHECK_CONDITION (q->length >= 6);
4794 firstcol = XL_GETCOL (q);
4795 row = XL_GETROW (q);
4796 lastcol = GSF_LE_GET_GUINT16 (ptr);
4797 d (0, {
4798 g_printerr ("Cells in row %d are blank starting at col %s until col ",
4799 row + 1, col_name (firstcol));
4800 g_printerr ("%s;\n",
4801 col_name (lastcol));
4804 if (lastcol < firstcol) {
4805 int tmp = firstcol;
4806 firstcol = lastcol;
4807 lastcol = tmp;
4809 XL_CHECK_CONDITION (q->length >= 4u + 2u * (lastcol - firstcol + 1));
4811 range_end = i = lastcol;
4812 prev_xf = -1;
4813 do {
4814 ptr -= 2;
4815 xf_index = GSF_LE_GET_GUINT16 (ptr);
4816 d (2, {
4817 g_printerr (" xf (%s) = 0x%x", col_name (i), xf_index);
4818 if (i == firstcol)
4819 g_printerr ("\n");
4822 if (prev_xf != xf_index) {
4823 if (prev_xf >= 0)
4824 excel_set_xf_segment (esheet, i + 1, range_end,
4825 row, row, prev_xf);
4826 prev_xf = xf_index;
4827 range_end = i;
4829 } while (--i >= firstcol);
4830 excel_set_xf_segment (esheet, firstcol, range_end,
4831 row, row, prev_xf);
4832 d (2, g_printerr ("\n"););
4835 void
4836 xls_read_range32 (GnmRange *r, guint8 const *data)
4838 r->start.row = GSF_LE_GET_GUINT32 (data + 0);
4839 r->end.row = GSF_LE_GET_GUINT32 (data + 4);
4840 r->start.col = GSF_LE_GET_GUINT16 (data + 8);
4841 r->end.col = GSF_LE_GET_GUINT16 (data + 10);
4843 r->start.row = CLAMP (r->start.row, 0, GNM_MAX_ROWS - 1);
4844 r->end.row = CLAMP (r->end.row, 0, GNM_MAX_ROWS - 1);
4845 r->start.col = CLAMP (r->start.col, 0, GNM_MAX_COLS - 1);
4846 r->end.col = CLAMP (r->end.col, 0, GNM_MAX_COLS - 1);
4848 d (4, range_dump (r, ";\n"););
4851 void
4852 xls_read_range16 (GnmRange *r, guint8 const *data)
4854 r->start.row = GSF_LE_GET_GUINT16 (data + 0);
4855 r->end.row = GSF_LE_GET_GUINT16 (data + 2);
4856 r->start.col = GSF_LE_GET_GUINT16 (data + 4);
4857 r->end.col = GSF_LE_GET_GUINT16 (data + 6);
4859 r->start.row = CLAMP (r->start.row, 0, GNM_MAX_ROWS - 1);
4860 r->end.row = CLAMP (r->end.row, 0, GNM_MAX_ROWS - 1);
4861 r->start.col = CLAMP (r->start.col, 0, GNM_MAX_COLS - 1);
4862 r->end.col = CLAMP (r->end.col, 0, GNM_MAX_COLS - 1);
4864 d (4, range_dump (r, ";\n"););
4867 void
4868 xls_read_range8 (GnmRange *r, guint8 const *data)
4870 r->start.row = GSF_LE_GET_GUINT16 (data + 0);
4871 r->end.row = GSF_LE_GET_GUINT16 (data + 2);
4872 r->start.col = GSF_LE_GET_GUINT8 (data + 4);
4873 r->end.col = GSF_LE_GET_GUINT8 (data + 5);
4874 d (4, range_dump (r, ";\n"););
4878 * No documentation exists for this record, but this makes
4879 * sense given the other record formats.
4881 static void
4882 excel_read_MERGECELLS (BiffQuery *q, ExcelReadSheet *esheet)
4884 int num_merged;
4885 guint8 const *data = q->data + 2;
4886 GnmRange r;
4887 GSList *overlap;
4889 XL_CHECK_CONDITION (q->length >= 2);
4890 num_merged = GSF_LE_GET_GUINT16 (q->data);
4891 XL_CHECK_CONDITION (q->length == (unsigned int)(2 + 8 * num_merged));
4893 for (; num_merged-- > 0 ; data += 8) {
4894 xls_read_range16 (&r, data);
4895 overlap = gnm_sheet_merge_get_overlap (esheet->sheet, &r);
4896 if (overlap) {
4897 GnmRange *r2 = overlap->data;
4899 /* Unmerge r2, then merge (r U r2) */
4901 /* Do this early because the _remove can kill r2. */
4902 r = range_union (&r, r2);
4904 gnm_sheet_merge_remove (esheet->sheet, r2);
4905 g_slist_free (overlap);
4907 gnm_sheet_merge_add (esheet->sheet, &r, FALSE,
4908 GO_CMD_CONTEXT (esheet->container.importer->context));
4912 static void
4913 excel_read_DIMENSIONS (BiffQuery *q, ExcelReadSheet *esheet)
4915 GnmRange r;
4916 const char *key = "DIMENSION";
4918 if (!esheet)
4919 return;
4921 if (esheet_ver (esheet) >= MS_BIFF_V8) {
4922 XL_CHECK_CONDITION (q->length >= 12);
4923 xls_read_range32 (&r, q->data);
4924 } else {
4925 XL_CHECK_CONDITION (q->length >= 8);
4926 xls_read_range16 (&r, q->data);
4929 if (range_width (&r) <= 1 || range_height (&r) <= 1) {
4930 g_object_set_data (G_OBJECT (esheet->sheet), key, NULL);
4931 d (1, g_printerr ("Dimension = -\n"););
4932 } else {
4933 r.end.col--;
4934 r.end.row--;
4935 d (1, g_printerr ("Dimension = %s\n", range_as_string (&r)););
4937 /* Hack: we need to get this information out to
4938 table_cellregion_read */
4939 g_object_set_data_full (G_OBJECT (esheet->sheet),
4940 key, gnm_range_dup (&r),
4941 g_free);
4945 static MSContainer *
4946 sheet_container (ExcelReadSheet *esheet)
4948 ms_container_set_blips (&esheet->container, esheet->container.importer->container.blips);
4949 return &esheet->container;
4952 static gboolean
4953 excel_read_sheet_PROTECT (BiffQuery *q, ExcelReadSheet *esheet)
4955 gboolean is_protected = TRUE;
4957 /* MS Docs fail to mention that in some stream this
4958 * record can have size zero. I assume the in that
4959 * case its existence is the flag. */
4960 if (q->length >= 2)
4961 is_protected = (1 == GSF_LE_GET_GUINT16 (q->data));
4963 esheet->sheet->is_protected = is_protected;
4965 return is_protected;
4968 static gboolean
4969 excel_read_workbook_PROTECT (BiffQuery *q, WorkbookView *wb_view)
4971 gboolean is_protected = TRUE;
4973 /* MS Docs fail to mention that in some stream this
4974 * record can have size zero. I assume the in that
4975 * case its existence is the flag. */
4976 if (q->length >= 2)
4977 is_protected = (1 == GSF_LE_GET_GUINT16 (q->data));
4979 wb_view->is_protected = is_protected;
4981 return is_protected;
4984 static void
4985 excel_read_WSBOOL (BiffQuery *q, ExcelReadSheet *esheet)
4987 guint16 options;
4989 XL_CHECK_CONDITION (q->length == 2);
4991 options = GSF_LE_GET_GUINT16 (q->data);
4992 /* 0x0001 automatic page breaks are visible */
4993 /* 0x0010 the sheet is a dialog sheet */
4994 /* 0x0020 automatic styles are not applied to an outline */
4995 esheet->sheet->outline_symbols_below = 0 != (options & 0x040);
4996 esheet->sheet->outline_symbols_right = 0 != (options & 0x080);
4997 if (NULL != esheet->sheet->print_info)
4998 esheet->sheet->print_info->scaling.type =
4999 (options & 0x100) ? PRINT_SCALE_FIT_PAGES : PRINT_SCALE_PERCENTAGE;
5001 /* 0x0200 biff 3-4 0 == save external linked values, 1 == do not save */
5002 /* XL docs wrong 0xc00 no 0x600, OOo docs wrong no distinct row vs col */
5003 esheet->sheet->display_outlines = 0 != (options & 0xc00);
5005 /* Biff4 0x3000 window arrangement
5006 * 0b == tiled
5007 * 1b == arrange horiz
5008 * 10b == arrange vert
5009 * 11b == cascade */
5011 /* biff 4-8 0x4000, 0 == std expr eval, 1 == alt expr eval ? */
5012 /* biff 4-8 0x8000, 0 == std fmla entry, 1 == alt fmla entry ? */
5015 static void
5016 excel_read_CALCCOUNT (BiffQuery *q, GnmXLImporter *importer)
5018 guint16 count;
5020 XL_CHECK_CONDITION (q->length == 2);
5022 count = GSF_LE_GET_GUINT16 (q->data);
5024 workbook_iteration_max_number (importer->wb, count);
5027 static void
5028 excel_read_CALCMODE (BiffQuery *q, GnmXLImporter *importer)
5030 XL_CHECK_CONDITION (q->length == 2);
5031 workbook_set_recalcmode (importer->wb,
5032 GSF_LE_GET_GUINT16 (q->data) != 0);
5035 static void
5036 excel_read_DELTA (BiffQuery *q, GnmXLImporter *importer)
5038 double tolerance;
5040 XL_CHECK_CONDITION (q->length == 8);
5042 tolerance = gsf_le_get_double (q->data);
5043 XL_CHECK_CONDITION (tolerance >= 0);
5045 workbook_iteration_tolerance (importer->wb, tolerance);
5048 static void
5049 excel_read_ITERATION (BiffQuery *q, GnmXLImporter *importer)
5051 guint16 enabled;
5053 XL_CHECK_CONDITION (q->length == 2);
5055 enabled = GSF_LE_GET_GUINT16 (q->data);
5056 workbook_iteration_enabled (importer->wb, enabled != 0);
5059 static void
5060 excel_read_PANE (BiffQuery *q, ExcelReadSheet *esheet, WorkbookView *wb_view)
5062 XL_CHECK_CONDITION (q->length == 10);
5063 if (esheet->freeze_panes) {
5064 guint16 x = GSF_LE_GET_GUINT16 (q->data + 0);
5065 guint16 y = GSF_LE_GET_GUINT16 (q->data + 2);
5066 guint16 rwTop = GSF_LE_GET_GUINT16 (q->data + 4);
5067 guint16 colLeft = GSF_LE_GET_GUINT16 (q->data + 6);
5068 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
5069 GnmCellPos frozen, unfrozen;
5071 esheet->active_pane = GSF_LE_GET_GUINT16 (q->data + 8);
5072 if (esheet->active_pane > 3) {
5073 g_warning ("Invalid pane '%u' selected", esheet->active_pane);
5074 esheet->active_pane = 3;
5077 g_return_if_fail (sv != NULL);
5079 frozen = unfrozen = sv->initial_top_left;
5080 if (x > 0)
5081 unfrozen.col += x;
5082 else
5083 colLeft = sv->initial_top_left.col;
5084 if (y > 0)
5085 unfrozen.row += y;
5086 else
5087 rwTop = sv->initial_top_left.row;
5088 gnm_sheet_view_freeze_panes (sv, &frozen, &unfrozen);
5089 gnm_sheet_view_set_initial_top_left (sv, colLeft, rwTop);
5090 } else {
5091 g_warning ("EXCEL : no support for split panes yet (%s)", esheet->sheet->name_unquoted);
5095 static void
5096 excel_read_WINDOW2 (BiffQuery *q, ExcelReadSheet *esheet, WorkbookView *wb_view)
5098 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
5099 guint16 top_row = 0;
5100 guint16 left_col = 0;
5101 guint32 biff_pat_col;
5102 gboolean set_grid_color;
5104 if (q->opcode == BIFF_WINDOW2_v2) {
5105 guint16 options;
5107 XL_CHECK_CONDITION (q->length >= 10);
5109 options = GSF_LE_GET_GUINT16 (q->data + 0);
5110 esheet->sheet->display_formulas = ((options & 0x0001) != 0);
5111 esheet->sheet->hide_grid = ((options & 0x0002) == 0);
5112 esheet->sheet->hide_col_header =
5113 esheet->sheet->hide_row_header = ((options & 0x0004) == 0);
5114 esheet->freeze_panes = ((options & 0x0008) != 0);
5115 esheet->sheet->hide_zero = ((options & 0x0010) == 0);
5116 set_grid_color = (options & 0x0020) == 0;
5117 g_object_set (esheet->sheet, "text-is-rtl", (options & 0x0040) != 0, NULL);
5119 top_row = GSF_LE_GET_GUINT16 (q->data + 2);
5120 left_col = GSF_LE_GET_GUINT16 (q->data + 4);
5121 biff_pat_col = GSF_LE_GET_GUINT32 (q->data + 6);
5123 d (0, if (options & 0x0200) g_printerr ("Sheet flag selected\n"););
5124 if (options & 0x0400)
5125 wb_view_sheet_focus (wb_view, esheet->sheet);
5127 if (esheet_ver (esheet) >= MS_BIFF_V8 && q->length >= 14) {
5128 d (2, {
5129 guint16 const pageBreakZoom = GSF_LE_GET_GUINT16 (q->data + 10);
5130 guint16 const normalZoom = GSF_LE_GET_GUINT16 (q->data + 12);
5131 g_printerr ("%hx %hx\n", normalZoom, pageBreakZoom);
5134 } else {
5135 XL_CHECK_CONDITION (q->length >= 14);
5137 esheet->sheet->display_formulas = (q->data[0] != 0);
5138 esheet->sheet->hide_grid = (q->data[1] == 0);
5139 esheet->sheet->hide_col_header =
5140 esheet->sheet->hide_row_header = (q->data[2] == 0);
5141 esheet->freeze_panes = (q->data[3] != 0);
5142 esheet->sheet->hide_zero = (q->data[4] == 0);
5143 set_grid_color = (q->data[9] == 0);
5145 top_row = GSF_LE_GET_GUINT16 (q->data + 5);
5146 left_col = GSF_LE_GET_GUINT16 (q->data + 7);
5147 biff_pat_col = GSF_LE_GET_GUINT32 (q->data + 10);
5150 if (set_grid_color) {
5151 GnmColor *pattern_color;
5152 if (esheet_ver (esheet) >= MS_BIFF_V8) {
5153 /* Get style color from palette*/
5154 pattern_color = excel_palette_get (
5155 esheet->container.importer,
5156 biff_pat_col & 0x7f);
5157 } else {
5158 guint8 r, g, b;
5160 r = (guint8) biff_pat_col;
5161 g = (guint8) (biff_pat_col >> 8);
5162 b = (guint8) (biff_pat_col >> 16);
5163 pattern_color = gnm_color_new_rgb8 (r, g, b);
5165 d (2, g_printerr ("auto pattern color "
5166 "0x%08x\n",
5167 pattern_color->go_color););
5168 sheet_style_set_auto_pattern_color (
5169 esheet->sheet, pattern_color);
5172 g_return_if_fail (sv != NULL);
5174 /* until we import multiple views unfreeze just in case a previous view
5175 * had frozen */
5176 gnm_sheet_view_freeze_panes (sv, NULL, NULL);
5178 /* NOTE : This is top left of screen even if frozen, modify when
5179 * we read PANE */
5180 gnm_sheet_view_set_initial_top_left (sv, left_col, top_row);
5183 static void
5184 excel_read_CF_border (GnmStyle *style, ExcelReadSheet *esheet,
5185 GnmStyleBorderLocation type,
5186 unsigned xl_pat_index, unsigned xl_color_index)
5188 GnmStyleElement elem = GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (type);
5189 gnm_style_set_border (style, elem,
5190 gnm_style_border_fetch (biff_xf_map_border (xl_pat_index),
5191 excel_palette_get (esheet->container.importer,
5192 xl_color_index),
5193 gnm_style_border_get_orientation (type)));
5196 static void
5197 excel_read_CF (BiffQuery *q, ExcelReadSheet *esheet, GnmStyleConditions *sc,
5198 GnmXLImporter *importer)
5200 guint8 type, op;
5201 guint16 expr0_len,expr1_len;
5202 guint32 flags;
5203 guint16 flags2;
5204 unsigned offset;
5205 GnmStyleCond *cond = NULL;
5206 GnmStyleCondOp cop;
5207 GnmStyle *overlay = NULL;
5209 XL_CHECK_CONDITION (q->length >= 12);
5211 type = GSF_LE_GET_GUINT8 (q->data + 0);
5212 op = GSF_LE_GET_GUINT8 (q->data + 1);
5213 expr0_len = GSF_LE_GET_GUINT16 (q->data + 2);
5214 expr1_len = GSF_LE_GET_GUINT16 (q->data + 4);
5215 flags = GSF_LE_GET_GUINT32 (q->data + 6);
5216 flags2 = GSF_LE_GET_GUINT16 (q->data + 10);
5218 XL_CHECK_CONDITION (q->length >= 10u + expr0_len + expr1_len);
5220 d (1, {
5221 gsf_mem_dump (q->data+6, 6);
5222 g_printerr ("cond type = %d, op type = %d, flags = 0x%08x\n", (int)type, (int)op, flags);
5224 switch (type) {
5225 case 1 :
5226 switch (op) {
5227 case 0x01: cop = GNM_STYLE_COND_BETWEEN; break;
5228 case 0x02: cop = GNM_STYLE_COND_NOT_BETWEEN; break;
5229 case 0x03: cop = GNM_STYLE_COND_EQUAL; break;
5230 case 0x04: cop = GNM_STYLE_COND_NOT_EQUAL; break;
5231 case 0x05: cop = GNM_STYLE_COND_GT; break;
5232 case 0x06: cop = GNM_STYLE_COND_LT; break;
5233 case 0x07: cop = GNM_STYLE_COND_GTE; break;
5234 case 0x08: cop = GNM_STYLE_COND_LTE; break;
5235 default:
5236 g_warning ("EXCEL : Unknown condition (%d) for conditional format in sheet %s.",
5237 op, esheet->sheet->name_unquoted);
5238 return;
5240 break;
5241 case 2:
5242 cop = GNM_STYLE_COND_CUSTOM;
5243 break;
5245 default:
5246 g_warning ("EXCEL : Unknown condition type (%d) for format in sheet %s.",
5247 (int)type, esheet->sheet->name_unquoted);
5248 return;
5251 cond = gnm_style_cond_new (cop, esheet->sheet);
5253 if (expr0_len > 0) {
5254 GnmExprTop const *texpr =
5255 ms_sheet_parse_expr_internal
5256 (esheet,
5257 q->data + q->length - expr0_len - expr1_len,
5258 expr0_len);
5259 gnm_style_cond_set_expr (cond, texpr, 0);
5260 gnm_expr_top_unref (texpr);
5262 if (expr1_len > 0) {
5263 GnmExprTop const *texpr =
5264 ms_sheet_parse_expr_internal
5265 (esheet,
5266 q->data + q->length - expr1_len,
5267 expr1_len);
5268 gnm_style_cond_set_expr (cond, texpr, 1);
5269 gnm_expr_top_unref (texpr);
5272 /* Reverse the alternate-expression treatment on save. */
5273 gnm_style_cond_canonicalize (cond);
5275 /* UNDOCUMENTED : the format of the conditional format
5276 * is unspecified.
5278 * header == 6
5279 * 0xff : I'll guess fonts
5280 * uint8 : 0xff = no border
5281 * 0xf7 = R
5282 * 0xfb = L
5283 * 0xef = T
5284 * 0xdf = B
5285 * 0xc3 == T,L,B,R
5286 * uint8 : 0x3f == no background elements,
5287 * 0x3b == background
5288 * 0x3a == background & pattern
5289 * 0x38 == background & pattern & pattern colour
5290 * uint8 : 0x04 = font | 0x10 = border | 0x20 = colour
5291 * 0x02 : ?
5292 * 0x00 : ?
5294 * font == 118
5295 * border == 8
5296 * colour == 4
5297 * Similar to XF from biff7
5300 overlay = gnm_style_new ();
5302 offset = 6 /* CF record header */ + 6; /* format header */
5304 if (flags & 0x02000000) { /* number format */
5305 gboolean ignore = (flags & 0x00080000) != 0;
5307 XL_CHECK_CONDITION_FULL (q->length >= offset + 2, goto fail;);
5309 if (flags2 & 1) {
5310 /* Format as string */
5311 guint bytes = GSF_LE_GET_GUINT16 (q->data + offset);
5312 if (!ignore) {
5313 char *xlfmt = excel_biff_text_2 (importer, q, offset + 2);
5314 GOFormat *fmt = go_format_new_from_XL (xlfmt);
5315 gnm_style_set_format (overlay, fmt);
5316 go_format_unref (fmt);
5317 g_free (xlfmt);
5319 offset += bytes;
5320 } else {
5321 /* Format as index */
5322 offset += 2;
5326 if (flags & 0x04000000) { /* font */
5327 guint32 size, colour;
5328 guint8 tmp8, font_flags;
5329 guint8 const *data = q->data + offset;
5331 XL_CHECK_CONDITION_FULL (q->length >= offset + 64 + 54, goto fail;);
5333 if (data[0] && GSF_LE_GET_GUINT16 (data + 116) > 0) {
5334 char *font = excel_biff_text_1
5335 (importer, q, offset);
5336 gnm_style_set_font_name (overlay, font);
5337 g_free (font);
5340 data += 64;
5342 if (0xFFFFFFFF != (size = GSF_LE_GET_GUINT32 (data)))
5343 gnm_style_set_font_size (overlay, size / 20.);
5344 if (0xFFFFFFFF != (colour = GSF_LE_GET_GUINT32 (data + 16)))
5345 gnm_style_set_font_color (overlay,
5346 excel_palette_get (esheet->container.importer,
5347 colour));
5349 if (0 == GSF_LE_GET_GUINT8 (data + 36)) {
5350 gnm_style_set_font_bold (overlay,
5351 GSF_LE_GET_GUINT16 (data + 8) >= 0x2bc);
5354 tmp8 = GSF_LE_GET_GUINT8 (data + 4);
5355 font_flags = GSF_LE_GET_GUINT8 (data + 24);
5356 if (0 == (font_flags & 2))
5357 gnm_style_set_font_italic (overlay, 0 != (tmp8 & 2));
5359 if (0 == (font_flags & 0x80))
5360 gnm_style_set_font_strike (overlay, 0 != (tmp8 & 0x80));
5362 if (0 == GSF_LE_GET_GUINT8 (data + 28)) {
5363 switch (GSF_LE_GET_GUINT8 (data + 10)) {
5364 default : g_printerr ("Unknown script %d\n", GSF_LE_GET_GUINT8 (data));
5365 /* fall through */
5366 case 0: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_STANDARD); break;
5367 case 1: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_SUPER); break;
5368 case 2: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_SUB); break;
5371 if (0 == GSF_LE_GET_GUINT8 (data + 32)) {
5372 MsBiffFontUnderline mul;
5373 switch (GSF_LE_GET_GUINT8 (data + 12)) {
5374 default :
5375 case 0:
5376 mul = XLS_ULINE_NONE;
5377 break;
5378 case 1:
5379 mul = XLS_ULINE_SINGLE;
5380 break;
5381 case 2:
5382 mul = XLS_ULINE_DOUBLE;
5383 break;
5384 case 0x21:
5385 mul = XLS_ULINE_SINGLE_ACC;
5386 break;
5387 case 0x22:
5388 mul = XLS_ULINE_DOUBLE_ACC;
5389 break;
5391 gnm_style_set_font_uline
5392 (overlay,
5393 xls_uline_to_gnm_underline (mul));
5396 d (3, {
5397 g_printerr ("%s\n", "Font");
5398 gsf_mem_dump (data, 54);
5401 offset += 118;
5404 if (flags & 0x08000000) { /* alignment block */
5405 guint16 d1, d2;
5407 XL_CHECK_CONDITION_FULL (q->length >= offset + 8, goto fail;);
5408 d1 = GSF_LE_GET_GUINT16 (q->data + offset);
5409 d2 = GSF_LE_GET_GUINT16 (q->data + offset + 2);
5411 if (0 == (flags & 0x1))
5412 gnm_style_set_align_h (overlay,
5413 halign_from_excel ((d1 >> 0) & 7));
5415 if (0 == (flags & 0x2))
5416 gnm_style_set_align_v (overlay,
5417 valign_from_excel ((d1 >> 4) & 7));
5419 if (0 == (flags & 0x4))
5420 gnm_style_set_wrap_text (overlay, ((d1 >> 3) & 1));
5422 if (0 == (flags & 0x8)) {
5423 int r = (esheet_ver (esheet) >= MS_BIFF_V8
5424 ? rotation_from_excel_v8 (d1 >> 8)
5425 : rotation_from_excel_v7 (d1 >> 8));
5426 gnm_style_set_rotation (overlay, r);
5429 if (0 == (flags & 0x20))
5430 gnm_style_set_indent (overlay, ((d2 >> 0) & 0xf));
5432 if (0 == (flags & 0x40))
5433 gnm_style_set_shrink_to_fit (overlay, ((d2 >> 4) & 1));
5435 offset += 8;
5438 if (flags & 0x10000000) { /* borders */
5439 guint32 d0, d1;
5441 XL_CHECK_CONDITION_FULL (q->length >= offset + 8, goto fail;);
5442 d0 = GSF_LE_GET_GUINT32 (q->data + offset);
5443 d1 = GSF_LE_GET_GUINT32 (q->data + offset + 4);
5445 if (0 == (flags & 0x0400))
5446 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_LEFT,
5447 (d0 >> 0) & 0xf,
5448 (d0 >> 16) & 0x7f);
5449 if (0 == (flags & 0x0800))
5450 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_RIGHT,
5451 (d0 >> 4) & 0xf,
5452 (d0 >> 23) & 0x7f);
5453 if (0 == (flags & 0x1000))
5454 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_TOP,
5455 (d0 >> 8) & 0xf,
5456 (d1 >> 0) & 0x7f);
5457 if (0 == (flags & 0x2000))
5458 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_BOTTOM,
5459 (d0 >> 12) & 0xf,
5460 (d1 >> 7) & 0x7f);
5461 if (0 == (flags & 0x4000) && (d0 & 0x80000000)) {
5462 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_DIAG,
5463 (d1 >> 21) & 0xf,
5464 (d1 >> 14) & 0x7f);
5466 if (0 == (flags & 0x8000) && (d0 & 0x40000000))
5467 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_REV_DIAG,
5468 (d1 >> 21) & 0xf,
5469 (d1 >> 14) & 0x7f);
5471 offset += 8;
5474 if (flags & 0x20000000) { /* pattern */
5475 guint32 background_flags;
5476 int pattern = 0;
5478 XL_CHECK_CONDITION_FULL (q->length >= offset + 4, goto fail;);
5479 background_flags = GSF_LE_GET_GUINT32 (q->data + offset);
5481 if (0 == (flags & 0x10000))
5482 gnm_style_set_pattern (overlay,
5483 pattern = excel_map_pattern_index_from_excel (
5484 (background_flags >> 10) & 0x3F));
5485 if (0 == (flags & 0x20000))
5486 gnm_style_set_pattern_color (overlay,
5487 excel_palette_get (esheet->container.importer,
5488 (background_flags >> 16) & 0x7F));
5489 if (0 == (flags & 0x40000))
5490 gnm_style_set_back_color (overlay,
5491 excel_palette_get (esheet->container.importer,
5492 (background_flags >> 23) & 0x7F));
5494 offset += 4;
5497 if (flags & 0x40000000) { /* protection */
5498 offset += 2;
5501 XL_CHECK_CONDITION_FULL (q->length == offset + expr0_len + expr1_len, goto fail;);
5503 d (1, gnm_style_dump (overlay););
5505 gnm_style_cond_set_overlay (cond, overlay);
5506 gnm_style_unref (overlay);
5507 gnm_style_conditions_insert (sc, cond, -1);
5508 gnm_style_cond_free (cond);
5509 return;
5511 fail:
5512 if (cond)
5513 gnm_style_cond_free (cond);
5514 if (overlay)
5515 gnm_style_unref (overlay);
5518 static void
5519 excel_read_CONDFMT (BiffQuery *q, ExcelReadSheet *esheet,
5520 GnmXLImporter *importer)
5522 guint16 num_fmts, num_areas;
5523 unsigned i;
5524 guint8 const *data;
5525 GnmStyleConditions *sc;
5526 GnmStyle *style;
5527 GnmRange region;
5528 GSList *ptr, *regions = NULL;
5530 XL_CHECK_CONDITION (q->length >= 14);
5532 num_fmts = GSF_LE_GET_GUINT16 (q->data + 0);
5533 num_areas = GSF_LE_GET_GUINT16 (q->data + 12);
5535 d (1, g_printerr ("Num areas == %hu\n", num_areas););
5536 #if 0
5537 /* The bounding box or the region containing all conditional formats.
5538 * It seems like this region is 0,0 -> 0xffff,0xffff when there are no
5539 * regions.
5541 if (num_areas > 0)
5542 xls_read_range16 (&region, q->data+4);
5543 #endif
5545 data = q->data + 14;
5546 for (i = 0 ; i < num_areas && (data+8) <= (q->data + q->length) ; i++, data += 8) {
5547 xls_read_range16 (&region, data);
5548 regions = g_slist_prepend (regions, gnm_range_dup (&region));
5551 XL_CHECK_CONDITION (data == q->data + q->length);
5553 sc = gnm_style_conditions_new (esheet->sheet);
5554 for (i = 0 ; i < num_fmts ; i++) {
5555 guint16 next;
5556 if (!ms_biff_query_peek_next (q, &next) || next != BIFF_CF) {
5557 g_object_unref (sc);
5558 g_slist_free_full (regions, g_free);
5559 g_warning ("EXCEL: missing CF record");
5560 return;
5562 ms_biff_query_next (q);
5563 excel_read_CF (q, esheet, sc, importer);
5566 style = gnm_style_new ();
5567 gnm_style_set_conditions (style, sc);
5568 for (ptr = regions ; ptr != NULL ; ptr = ptr->next) {
5569 gnm_style_ref (style);
5570 sheet_style_apply_range (esheet->sheet, ptr->data, style);
5571 g_free (ptr->data);
5573 gnm_style_unref (style);
5574 g_slist_free (regions);
5577 static void
5578 excel_read_DV (BiffQuery *q, ExcelReadSheet *esheet)
5580 GnmExprTop const *texpr1 = NULL;
5581 GnmExprTop const *texpr2 = NULL;
5582 int expr1_len, expr2_len;
5583 char *input_msg, *error_msg, *input_title, *error_title;
5584 guint32 options, len;
5585 guint8 const *data, *expr1_dat, *expr2_dat;
5586 guint8 const *end = q->data + q->length;
5587 int i, col, row;
5588 GnmRange r;
5589 ValidationStyle style;
5590 ValidationType type;
5591 ValidationOp op;
5592 GSList *ptr, *ranges = NULL;
5593 GnmStyle *mstyle;
5595 XL_CHECK_CONDITION (q->length >= 4);
5596 options = GSF_LE_GET_GUINT32 (q->data);
5597 data = q->data + 4;
5599 XL_CHECK_CONDITION (data+3 <= end);
5600 input_title = excel_get_text (esheet->container.importer, data + 2,
5601 GSF_LE_GET_GUINT16 (data), &len, NULL,
5602 end - (data + 2));
5603 data += len + 2;
5605 XL_CHECK_CONDITION (data+3 <= end);
5606 error_title = excel_get_text (esheet->container.importer, data + 2,
5607 GSF_LE_GET_GUINT16 (data), &len, NULL,
5608 end - (data + 2));
5609 data += len + 2;
5611 XL_CHECK_CONDITION (data+3 <= end);
5612 input_msg = excel_get_text (esheet->container.importer, data + 2,
5613 GSF_LE_GET_GUINT16 (data), &len, NULL,
5614 end - (data + 2));
5615 data += len + 2;
5617 XL_CHECK_CONDITION (data+3 <= end);
5618 error_msg = excel_get_text (esheet->container.importer, data + 2,
5619 GSF_LE_GET_GUINT16 (data), &len, NULL,
5620 end - (data + 2));
5621 data += len + 2;
5623 d (1, {
5624 g_printerr ("Input Title : '%s'\n", input_title);
5625 g_printerr ("Input Msg : '%s'\n", input_msg);
5626 g_printerr ("Error Title : '%s'\n", error_title);
5627 g_printerr ("Error Msg : '%s'\n", error_msg);
5630 XL_CHECK_CONDITION (data+4 <= end);
5631 expr1_len = GSF_LE_GET_GUINT16 (data);
5632 d (5, g_printerr ("Unknown1 = %hx\n", GSF_LE_GET_GUINT16 (data+2)););
5633 expr1_dat = data + 4; /* TODO : What are the missing 2 bytes ? */
5634 data += expr1_len + 4;
5636 XL_CHECK_CONDITION (data+4 <= end);
5637 expr2_len = GSF_LE_GET_GUINT16 (data);
5638 d (5, g_printerr ("Unknown2 = %hx\n", GSF_LE_GET_GUINT16 (data+2)););
5639 expr2_dat = data + 4; /* TODO : What are the missing 2 bytes ? */
5640 data += expr2_len + 4;
5642 XL_CHECK_CONDITION (data+2 < end);
5643 i = GSF_LE_GET_GUINT16 (data);
5644 data += 2;
5645 XL_CHECK_CONDITION ((end - data) / 8 >= i);
5647 for (; i-- > 0 ; data += 8) {
5648 xls_read_range16 (&r, data);
5649 ranges = g_slist_prepend (ranges, gnm_range_dup (&r));
5652 /* these enums align, but lets be explicit so that the filter
5653 * is easier to read.
5655 switch (options & 0x0f) {
5656 case 0 : type = GNM_VALIDATION_TYPE_ANY; break;
5657 case 1 : type = GNM_VALIDATION_TYPE_AS_INT; break;
5658 case 2 : type = GNM_VALIDATION_TYPE_AS_NUMBER; break;
5659 case 3 : type = GNM_VALIDATION_TYPE_IN_LIST; break;
5660 case 4 : type = GNM_VALIDATION_TYPE_AS_DATE; break;
5661 case 5 : type = GNM_VALIDATION_TYPE_AS_TIME; break;
5662 case 6 : type = GNM_VALIDATION_TYPE_TEXT_LENGTH; break;
5663 case 7 : type = GNM_VALIDATION_TYPE_CUSTOM; break;
5664 default :
5665 g_warning ("EXCEL : Unknown constraint type %d", options & 0x0f);
5666 return;
5669 switch ((options >> 4) & 0x07) {
5670 case 0 : style = GNM_VALIDATION_STYLE_STOP; break;
5671 case 1 : style = GNM_VALIDATION_STYLE_WARNING; break;
5672 case 2 : style = GNM_VALIDATION_STYLE_INFO; break;
5673 default :
5674 g_warning ("EXCEL : Unknown validation style %d",
5675 (options >> 4) & 0x07);
5676 return;
5678 if (!(options & 0x80000))
5679 style = GNM_VALIDATION_STYLE_NONE;
5681 if (type == GNM_VALIDATION_TYPE_CUSTOM || type == GNM_VALIDATION_TYPE_IN_LIST)
5682 op = GNM_VALIDATION_OP_NONE;
5683 else
5684 switch ((options >> 20) & 0x0f) {
5685 case 0: op = GNM_VALIDATION_OP_BETWEEN; break;
5686 case 1: op = GNM_VALIDATION_OP_NOT_BETWEEN; break;
5687 case 2: op = GNM_VALIDATION_OP_EQUAL; break;
5688 case 3: op = GNM_VALIDATION_OP_NOT_EQUAL; break;
5689 case 4: op = GNM_VALIDATION_OP_GT; break;
5690 case 5: op = GNM_VALIDATION_OP_LT; break;
5691 case 6: op = GNM_VALIDATION_OP_GTE; break;
5692 case 7: op = GNM_VALIDATION_OP_LTE; break;
5693 default :
5694 g_warning ("EXCEL : Unknown constraint operator %d",
5695 (options >> 20) & 0x0f);
5696 return;
5699 if (ranges != NULL) {
5700 GnmRange const *r = ranges->data;
5701 col = r->start.col;
5702 row = r->start.row;
5703 } else
5704 col = row = 0;
5706 if (expr1_len > 0)
5707 texpr1 = excel_parse_formula (&esheet->container, esheet,
5708 col, row,
5709 expr1_dat, expr1_len, 0 /* FIXME */,
5710 TRUE, NULL);
5712 if (expr2_len > 0)
5713 texpr2 = excel_parse_formula (&esheet->container, esheet,
5714 col, row,
5715 expr2_dat, expr2_len, 0 /* FIXME */,
5716 TRUE, NULL);
5718 d (1, g_printerr ("style = %d, type = %d, op = %d\n",
5719 style, type, op););
5721 mstyle = gnm_style_new ();
5722 gnm_style_set_validation
5723 (mstyle,
5724 gnm_validation_new (style, type, op,
5725 esheet->sheet,
5726 error_title, error_msg,
5727 texpr1,
5728 texpr2,
5729 options & 0x0100, 0 == (options & 0x0200)));
5730 if (options & 0x40000)
5731 gnm_style_set_input_msg (mstyle,
5732 gnm_input_msg_new (input_msg, input_title));
5734 for (ptr = ranges; ptr != NULL ; ptr = ptr->next) {
5735 GnmRange *r = ptr->data;
5736 gnm_style_ref (mstyle);
5737 sheet_style_apply_range (esheet->sheet, r, mstyle);
5738 d (1, range_dump (r, "\n"););
5739 g_free (r);
5741 g_slist_free (ranges);
5742 gnm_style_unref (mstyle);
5743 g_free (input_msg);
5744 g_free (error_msg);
5745 g_free (input_title);
5746 g_free (error_title);
5749 static void
5750 excel_read_DVAL (BiffQuery *q, ExcelReadSheet *esheet)
5752 guint16 options;
5753 guint32 input_coord_x, input_coord_y, drop_down_id, dv_count;
5754 unsigned i;
5756 XL_CHECK_CONDITION (q->length == 18);
5758 options = GSF_LE_GET_GUINT16 (q->data + 0);
5759 input_coord_x = GSF_LE_GET_GUINT32 (q->data + 2);
5760 input_coord_y = GSF_LE_GET_GUINT32 (q->data + 6);
5761 drop_down_id = GSF_LE_GET_GUINT32 (q->data + 10);
5762 dv_count = GSF_LE_GET_GUINT32 (q->data + 14);
5764 d (5, if (options & 0x1) g_printerr ("DV input window is closed\n"););
5765 d (5, if (options & 0x2) g_printerr ("DV input window is pinned\n"););
5766 d (5, if (options & 0x4) g_printerr ("DV info has been cached ??\n"););
5768 for (i = 0 ; i < dv_count ; i++) {
5769 guint16 next;
5770 if (!ms_biff_query_peek_next (q, &next) || next != BIFF_DV) {
5771 g_warning ("EXCEL: missing DV record");
5772 return;
5774 ms_biff_query_next (q);
5775 excel_read_DV (q, esheet);
5779 static void
5780 excel_read_SHEETPROTECTION (BiffQuery *q, Sheet *sheet)
5782 guint16 flags;
5784 g_return_if_fail (sheet != NULL);
5786 /* 2003/2007 == 23
5787 * XP == 19 */
5788 XL_CHECK_CONDITION (q->length >= 19);
5790 if (q->length >= 23) {
5791 flags = GSF_LE_GET_GUINT16 (q->data + 19);
5792 sheet->protected_allow.edit_objects = (flags & (1 << 0)) != 0;
5793 sheet->protected_allow.edit_scenarios = (flags & (1 << 1)) != 0;
5794 sheet->protected_allow.cell_formatting = (flags & (1 << 2)) != 0;
5795 sheet->protected_allow.column_formatting = (flags & (1 << 3)) != 0;
5796 sheet->protected_allow.row_formatting = (flags & (1 << 4)) != 0;
5797 sheet->protected_allow.insert_columns = (flags & (1 << 5)) != 0;
5798 sheet->protected_allow.insert_rows = (flags & (1 << 6)) != 0;
5799 sheet->protected_allow.insert_hyperlinks = (flags & (1 << 7)) != 0;
5800 sheet->protected_allow.delete_columns = (flags & (1 << 8)) != 0;
5801 sheet->protected_allow.delete_rows = (flags & (1 << 9)) != 0;
5802 sheet->protected_allow.select_locked_cells = (flags & (1 << 10)) != 0;
5803 sheet->protected_allow.sort_ranges = (flags & (1 << 11)) != 0;
5804 sheet->protected_allow.edit_auto_filters = (flags & (1 << 12)) != 0;
5805 sheet->protected_allow.edit_pivottable = (flags & (1 << 13)) != 0;
5806 sheet->protected_allow.select_unlocked_cells = (flags & (1 << 14)) != 0;
5810 /***********************************************************************/
5812 static guchar *
5813 read_utf16_str (int word_len, guint8 const *data)
5815 int i;
5816 gunichar2 *uni_text = g_alloca (word_len * sizeof (gunichar2));
5818 /* be wary about endianness */
5819 for (i = 0 ; i < word_len ; i++, data += 2)
5820 uni_text [i] = GSF_LE_GET_GUINT16 (data);
5822 return (guchar *)g_utf16_to_utf8 (uni_text, word_len, NULL, NULL, NULL);
5826 * XL (at least XL 2000) stores URLs exactly as input by the user. No
5827 * quoting, no mime encoding of email headers. If cgi parameters are
5828 * separated by '&', '&' is stored, not '&amp;'. An email subject in
5829 * cyrillic characters is stored as cyrillic characters, not as an
5830 * RFC 2047 MIME encoded header.
5832 static void
5833 excel_read_HLINK (BiffQuery *q, ExcelReadSheet *esheet)
5835 static guint8 const stdlink_guid[] = {
5836 0xd0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
5837 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
5838 /* unknown */
5839 0x02, 0x00, 0x00, 0x00
5841 static guint8 const url_guid[] = {
5842 0xe0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
5843 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
5845 static guint8 const file_guid[] = {
5846 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5847 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
5849 GnmRange r;
5850 guint32 options, len;
5851 guint16 next_opcode;
5852 guint8 const *data = q->data;
5853 guchar *label = NULL;
5854 guchar *target_base = NULL;
5855 guchar *mark = NULL;
5856 guchar *tip = NULL;
5857 GnmHLink *link = NULL;
5859 XL_CHECK_CONDITION (q->length > 32);
5861 xls_read_range16 (&r, data);
5862 options = GSF_LE_GET_GUINT32 (data + 28);
5864 XL_CHECK_CONDITION (!memcmp (data + 8, stdlink_guid, sizeof (stdlink_guid)));
5866 data += 32;
5868 d (1, {
5869 range_dump (&r, "");
5870 g_printerr (" = hlink options(0x%04x)\n", options);
5873 if ((options & 0x14) == 0x14) { /* label */
5874 XL_NEED_ITEMS (1, 4);
5875 len = GSF_LE_GET_GUINT32 (data);
5876 data += 4;
5877 XL_NEED_ITEMS (len, 2);
5878 label = read_utf16_str (len, data);
5879 data += len*2;
5880 d (1, g_printerr ("label = %s\n", label););
5883 if (options & 0x80) { /* target_base */
5884 XL_NEED_ITEMS (1, 4);
5885 len = GSF_LE_GET_GUINT32 (data);
5886 data += 4;
5887 XL_NEED_ITEMS (len, 2);
5888 target_base = read_utf16_str (len, data);
5889 data += len*2;
5890 d (1, g_printerr ("target_base = %s\n", target_base););
5893 if (options & 0x8) { /* 'text mark' */
5894 XL_NEED_ITEMS (1, 4);
5895 len = GSF_LE_GET_GUINT32 (data);
5896 data += 4;
5898 XL_NEED_ITEMS (len, 2);
5899 mark = read_utf16_str (len, data);
5900 data += len*2;
5901 d (1, g_printerr ("mark = %s\n", mark););
5904 if ((options & 0x163) == 0x003 && !memcmp (data, url_guid, sizeof (url_guid))) {
5905 guchar *url;
5907 data += sizeof (url_guid);
5908 XL_NEED_ITEMS (1, 4);
5909 len = GSF_LE_GET_GUINT32 (data);
5910 data += 4;
5912 XL_NEED_BYTES (len);
5913 url = read_utf16_str (len/2, data);
5914 if (NULL != url && 0 == g_ascii_strncasecmp (url, "mailto:", 7))
5915 link = gnm_hlink_new (gnm_hlink_email_get_type (), esheet->sheet);
5916 else
5917 link = gnm_hlink_new (gnm_hlink_url_get_type (), esheet->sheet);
5918 gnm_hlink_set_target (link, url);
5919 g_free (url);
5921 /* File link */
5922 } else if ((options & 0x1e1) == 0x001 && !memcmp (data, file_guid, sizeof (file_guid))) {
5923 guchar *path;
5924 GString *accum;
5925 int up;
5927 data += sizeof (file_guid);
5929 XL_NEED_BYTES (6);
5930 up = GSF_LE_GET_GUINT16 (data + 0);
5931 len = GSF_LE_GET_GUINT32 (data + 2);
5932 d (1, g_printerr ("# leading ../ %d len 0x%04x\n",
5933 up, len););
5934 data += 6;
5936 XL_NEED_BYTES (len);
5937 data += len;
5939 XL_NEED_BYTES (16 + 12 + 6);
5940 data += 16 + 12;
5941 len = GSF_LE_GET_GUINT32 (data);
5942 data += 6;
5944 XL_NEED_BYTES (len);
5945 path = read_utf16_str (len/2, data);
5946 accum = g_string_new (NULL);
5947 while (up-- > 0)
5948 g_string_append (accum, "..\\");
5949 g_string_append (accum, path);
5950 g_free (path);
5951 link = gnm_hlink_new (gnm_hlink_external_get_type (), esheet->sheet);
5952 gnm_hlink_set_target (link, accum->str);
5953 g_string_free (accum, TRUE);
5955 /* UNC File link */
5956 } else if ((options & 0x1e3) == 0x103) {
5957 guchar *path;
5959 XL_NEED_ITEMS (1, 4);
5960 len = GSF_LE_GET_GUINT32 (data);
5961 data += 4;
5963 XL_NEED_BYTES (len);
5964 path = read_utf16_str (len/2, data);
5965 link = gnm_hlink_new (gnm_hlink_external_get_type (), esheet->sheet);
5966 gnm_hlink_set_target (link, path);
5967 g_free (path);
5969 } else if ((options & 0x1eb) == 0x008) {
5970 link = gnm_hlink_new (gnm_hlink_cur_wb_get_type (), esheet->sheet);
5971 gnm_hlink_set_target (link, mark);
5972 } else {
5973 g_warning ("Unknown hlink type 0x%x", options);
5976 if (ms_biff_query_peek_next (q, &next_opcode) &&
5977 next_opcode == BIFF_LINK_TIP) {
5978 ms_biff_query_next (q);
5979 /* according to OO the bytes 2..10 are the range for the tip */
5980 XL_CHECK_CONDITION (q->length > 10);
5981 tip = read_utf16_str ((q->length - 10)/ 2, q->data + 10);
5984 if (link != NULL) {
5985 GnmStyle *style = gnm_style_new ();
5986 gnm_hlink_set_tip (link, tip);
5987 gnm_style_set_hlink (style, link);
5988 sheet_style_apply_range (esheet->sheet, &r, style);
5991 g_free (tip);
5992 g_free (mark);
5993 g_free (target_base);
5994 g_free (label);
5997 static void
5998 excel_read_CODENAME (BiffQuery *q, GnmXLImporter *importer, ExcelReadSheet *esheet)
6000 char *codename;
6001 GObject *obj;
6003 XL_CHECK_CONDITION (q->length >= 2);
6005 codename = excel_biff_text_2 (importer, q, 0);
6006 obj = esheet ? G_OBJECT (esheet->sheet) : G_OBJECT (importer->wb);
6007 g_object_set_data_full (obj, CODENAME_KEY, codename, g_free);
6010 static void
6011 excel_read_BG_PIC (BiffQuery *q,
6012 ExcelReadSheet *esheet)
6014 /* undocumented, looks similar to IMDATA */
6015 GdkPixbuf *background = excel_read_IMDATA (q, TRUE);
6016 if (background != NULL)
6017 g_object_unref (background);
6020 static GnmValue *
6021 read_DOPER (guint8 const *doper, gboolean is_equal,
6022 unsigned *str_len, GnmFilterOp *op)
6024 static GnmFilterOp const ops [] = {
6025 GNM_FILTER_OP_LT,
6026 GNM_FILTER_OP_EQUAL,
6027 GNM_FILTER_OP_LTE,
6028 GNM_FILTER_OP_GT,
6029 GNM_FILTER_OP_NOT_EQUAL,
6030 GNM_FILTER_OP_GTE
6032 GnmValue *res = NULL;
6034 *str_len = 0;
6035 *op = GNM_FILTER_UNUSED;
6036 switch (doper[0]) {
6037 case 0: return NULL; /* ignore */
6039 case 2: res = biff_get_rk (doper + 2);
6040 break;
6041 case 4: res = value_new_float (GSF_LE_GET_DOUBLE (doper+2));
6042 break;
6043 case 6: *str_len = doper[6];
6044 break;
6046 case 8: if (doper[2])
6047 res = xls_value_new_err (NULL, doper[3]);
6048 else
6049 res = value_new_bool (doper[3] ? TRUE : FALSE);
6050 break;
6052 case 0xC: *op = GNM_FILTER_OP_BLANKS;
6053 return NULL;
6054 case 0xE: *op = GNM_FILTER_OP_NON_BLANKS;
6055 return NULL;
6058 g_return_val_if_fail (doper[1] > 0 && doper[1] <=6, NULL);
6059 *op = ops [doper[1] - 1];
6061 return res;
6064 static void
6065 excel_read_AUTOFILTER (BiffQuery *q, ExcelReadSheet *esheet)
6067 guint16 flags;
6068 GnmFilterCondition *cond = NULL;
6069 GnmFilter *filter;
6071 /* XL only supports 1 filter per sheet */
6072 g_return_if_fail (esheet->sheet->filters != NULL);
6073 g_return_if_fail (esheet->sheet->filters->data != NULL);
6074 g_return_if_fail (esheet->sheet->filters->next == NULL);
6076 XL_CHECK_CONDITION (q->length >= 4);
6077 flags = GSF_LE_GET_GUINT16 (q->data + 2);
6079 filter = esheet->sheet->filters->data;
6081 if (esheet_ver (esheet) >= MS_BIFF_V8 && flags & 0x10)
6082 /* it's a top/bottom n */
6083 cond = gnm_filter_condition_new_bucket (
6084 (flags & 0x20) ? TRUE : FALSE,
6085 (flags & 0x40) ? FALSE : TRUE,
6086 FALSE,
6087 (flags >> 7) & 0x1ff);
6089 if (cond == NULL) {
6090 unsigned len0, len1;
6091 GnmFilterOp op0, op1;
6092 guint8 const *data;
6093 guint8 const *end = q->data + q->length;
6094 GnmValue *v0, *v1;
6096 XL_CHECK_CONDITION (q->length >= 24);
6097 v0 = read_DOPER (q->data + 4, flags & 4, &len0, &op0);
6098 v1 = read_DOPER (q->data + 14, flags & 8, &len1, &op1);
6100 data = q->data + 24;
6101 if (len0 > 0) {
6102 v0 = value_new_string_nocopy (
6103 excel_get_text (esheet->container.importer, data, len0, NULL, NULL, end - data));
6104 data += len0;
6106 if (len1 > 0)
6107 v1 = value_new_string_nocopy (
6108 excel_get_text (esheet->container.importer, data, len1, NULL, NULL, end - data));
6110 /* Survive fuzzed files. */
6111 if (op0 == GNM_FILTER_UNUSED)
6112 op0 = GNM_FILTER_OP_BLANKS;
6114 if (op1 == GNM_FILTER_UNUSED) {
6115 cond = gnm_filter_condition_new_single (op0, v0);
6116 value_release (v1); /* paranoia */
6117 } else {
6118 /* NOTE : Docs are backwards */
6119 cond = gnm_filter_condition_new_double
6120 (op0, v0, (flags & 3) ? FALSE : TRUE, op1, v1);
6124 gnm_filter_set_condition (filter,
6125 GSF_LE_GET_GUINT16 (q->data), cond, FALSE);
6128 void
6129 excel_read_SCL (BiffQuery *q, Sheet *sheet)
6131 unsigned num, denom;
6133 XL_CHECK_CONDITION (q->length == 4);
6135 num = GSF_LE_GET_GUINT16 (q->data);
6136 denom = GSF_LE_GET_GUINT16 (q->data + 2);
6138 XL_CHECK_CONDITION (denom != 0);
6140 g_object_set (sheet, "zoom-factor", num / (double)denom, NULL);
6144 * excel_externsheet_v8 :
6146 * WARNING WARNING WARNING
6148 * This function can and will return intentionally INVALID pointers in some
6149 * cases. You need to check for XL_EXTERNSHEET_MAGIC_SELFREF
6150 * It should only happen for external names to deal with 'reference self'
6151 * supbook entries. However, you never know.
6153 * you also need to check for XL_EXTERNSHEET_MAGIC_DELETED which indicates deleted
6155 * WARNING WARNING WARNING
6157 ExcelExternSheetV8 const *
6158 excel_externsheet_v8 (GnmXLImporter const *importer, guint16 i)
6160 d (2, g_printerr ("externv8 %hd\n", i););
6162 g_return_val_if_fail (importer->v8.externsheet != NULL, NULL);
6164 if (i >= importer->v8.externsheet->len) {
6165 g_warning ("%hd >= %u\n", i, importer->v8.externsheet->len);
6166 return NULL;
6169 return &(g_array_index (importer->v8.externsheet, ExcelExternSheetV8, i));
6172 static Sheet *
6173 supbook_get_sheet (GnmXLImporter *importer, gint16 sup_index, unsigned i)
6175 if (sup_index < 0) {
6176 g_warning ("external references not supported yet.");
6177 return NULL;
6180 /* 0xffff == deleted */
6181 if (i >= 0xffff)
6182 return XL_EXTERNSHEET_MAGIC_DELETED; /* magic value */
6184 /* WARNING : 0xfffe record for local names kludge a solution */
6185 if (i == 0xfffe)
6186 return XL_EXTERNSHEET_MAGIC_SELFREF; /* magic value */
6188 g_return_val_if_fail ((unsigned)sup_index < importer->v8.supbook->len, NULL);
6190 switch (g_array_index (importer->v8.supbook, ExcelSupBook, sup_index).type) {
6191 case EXCEL_SUP_BOOK_SELFREF: {
6192 Sheet *sheet;
6193 g_return_val_if_fail (i < importer->boundsheet_sheet_by_index->len, NULL);
6194 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, i);
6195 g_return_val_if_fail (IS_SHEET (sheet), NULL);
6196 return sheet;
6198 case EXCEL_SUP_BOOK_STD:
6199 g_warning ("external references not supported yet.");
6200 break;
6201 case EXCEL_SUP_BOOK_PLUGIN:
6202 g_warning ("strange external reference.");
6203 break;
6206 return XL_EXTERNSHEET_MAGIC_DELETED; /* pretend "deleted" */
6209 static void
6210 excel_read_EXTERNSHEET_v8 (BiffQuery const *q, GnmXLImporter *importer)
6212 ExcelExternSheetV8 *v8;
6213 gint16 sup_index;
6214 unsigned i, num, first, last;
6216 XL_CHECK_CONDITION (importer->ver >= MS_BIFF_V8);
6217 g_return_if_fail (importer->v8.externsheet == NULL);
6219 XL_CHECK_CONDITION (q->length >= 2);
6220 num = GSF_LE_GET_GUINT16 (q->data);
6221 XL_CHECK_CONDITION (q->length >= 2 + num * 6);
6223 d (2, g_printerr ("ExternSheet (%d entries)\n", num););
6224 d (10, gsf_mem_dump (q->data, q->length););
6226 importer->v8.externsheet = g_array_set_size (
6227 g_array_new (FALSE, FALSE, sizeof (ExcelExternSheetV8)), num);
6229 for (i = 0; i < num; i++) {
6230 sup_index = GSF_LE_GET_GINT16 (q->data + 2 + i * 6 + 0);
6231 first = GSF_LE_GET_GUINT16 (q->data + 2 + i * 6 + 2);
6232 last = GSF_LE_GET_GUINT16 (q->data + 2 + i * 6 + 4);
6234 d (2, g_printerr ("ExternSheet: sup = %hd First sheet 0x%x, Last sheet 0x%x\n",
6235 sup_index, first, last););
6237 v8 = &g_array_index(importer->v8.externsheet, ExcelExternSheetV8, i);
6238 v8->supbook = sup_index;
6239 v8->first = supbook_get_sheet (importer, sup_index, first);
6240 v8->last = supbook_get_sheet (importer, sup_index, last);
6241 d (2, g_printerr ("\tFirst sheet %p, Last sheet %p\n",
6242 v8->first, v8->last););
6247 * excel_externsheet_v7 :
6250 Sheet *
6251 excel_externsheet_v7 (MSContainer const *container, gint16 idx)
6253 GPtrArray const *externsheets;
6255 d (2, g_printerr ("externv7 %hd\n", idx););
6257 externsheets = container->v7.externsheets;
6258 g_return_val_if_fail (externsheets != NULL, NULL);
6259 g_return_val_if_fail (idx > 0, NULL);
6260 g_return_val_if_fail (idx <= (int)externsheets->len, NULL);
6262 return g_ptr_array_index (externsheets, idx-1);
6265 void
6266 excel_read_EXTERNSHEET_v7 (BiffQuery const *q, MSContainer *container)
6268 Sheet *sheet = NULL;
6269 guint8 type;
6271 XL_CHECK_CONDITION (q->length >= 2);
6273 type = GSF_LE_GET_GUINT8 (q->data + 1);
6275 d (1, {
6276 g_printerr ("extern v7 %p\n", container);
6277 gsf_mem_dump (q->data, q->length); });
6279 switch (type) {
6280 case 2: sheet = ms_container_sheet (container);
6281 if (sheet == NULL)
6282 g_warning ("What does this mean ?");
6283 break;
6285 /* Type 3 is undocumented magic. It is used to forward declare sheet
6286 * names in the current workbook */
6287 case 3: {
6288 unsigned len = GSF_LE_GET_GUINT8 (q->data);
6289 char *name;
6291 /* opencalc screws up its export, overstating
6292 * the length by 1 */
6293 if (len + 2 > q->length)
6294 len = q->length - 2;
6296 name = excel_biff_text (container->importer, q, 2, len);
6298 if (name != NULL) {
6299 sheet = workbook_sheet_by_name (container->importer->wb, name);
6300 if (sheet == NULL) {
6301 /* There was a bug in 1.0.x export that spewed the quoted name
6302 * includling internal backquoting */
6303 if (name[0] == '\'') {
6304 GString *fixed = g_string_new (NULL);
6305 if (NULL != go_strunescape (fixed, name) &&
6306 NULL != (sheet = workbook_sheet_by_name (container->importer->wb, fixed->str))) {
6307 g_free (name);
6308 name = g_string_free (fixed, FALSE);
6309 } else
6310 g_string_free (fixed, TRUE);
6313 if (sheet == NULL) {
6314 sheet = sheet_new (container->importer->wb,
6315 name,
6316 XLS_MaxCol,
6317 XLS_MaxRow_V7);
6318 workbook_sheet_attach (container->importer->wb, sheet);
6321 g_free (name);
6323 break;
6325 case 4: /* undocumented. Seems to be used as a placeholder for names */
6326 sheet = XL_EXTERNSHEET_MAGIC_SELFREF;
6327 break;
6329 case 0x3a : /* undocumented magic. seems to indicate the sheet for an
6330 * addin with functions. 01 3a
6331 * the same as SUPBOOK
6333 if (*q->data == 1 && q->length == 2)
6334 break;
6336 default:
6337 /* Fix when we get placeholders to external workbooks */
6338 d (1, gsf_mem_dump (q->data, q->length););
6339 go_io_warning_unsupported_feature (container->importer->context,
6340 _("external references"));
6343 if (container->v7.externsheets == NULL)
6344 container->v7.externsheets = g_ptr_array_new ();
6345 g_ptr_array_add (container->v7.externsheets, sheet);
6348 static void
6349 excel_read_EXTERNSHEET (BiffQuery const *q, GnmXLImporter *importer,
6350 const MsBiffBofData *ver)
6352 XL_CHECK_CONDITION (ver != NULL);
6354 if (ver->version >= MS_BIFF_V8)
6355 excel_read_EXTERNSHEET_v8 (q, importer);
6356 else
6357 excel_read_EXTERNSHEET_v7 (q, &importer->container);
6361 /* FILEPASS, ask the user for a password if necessary
6362 * return value is an error string, or NULL for success
6364 static const char *
6365 excel_read_FILEPASS (BiffQuery *q, GnmXLImporter *importer)
6367 /* files with workbook protection are encrypted using a
6368 * static password (why ?? ). */
6369 if (ms_biff_query_set_decrypt(q, importer->ver, "VelvetSweatshop"))
6370 return NULL;
6372 while (TRUE) {
6373 guint8 *passwd = go_cmd_context_get_password (
6374 GO_CMD_CONTEXT (importer->context),
6375 go_doc_get_uri (GO_DOC (importer->wb)));
6376 gboolean ok;
6378 if (passwd == NULL)
6379 return _("No password supplied");
6381 ok = ms_biff_query_set_decrypt (q, importer->ver, passwd);
6382 go_destroy_password (passwd);
6383 g_free (passwd);
6384 if (ok)
6385 return NULL;
6389 static void
6390 excel_read_LABEL (BiffQuery *q, ExcelReadSheet *esheet, gboolean has_markup)
6392 GnmValue *v;
6393 guint in_len, str_len;
6394 gchar *txt;
6395 BiffXFData const *xf;
6396 ExcelFont const *fd;
6397 GnmCell *cell = excel_cell_fetch (q, esheet);
6399 if (!cell)
6400 return;
6402 XL_CHECK_CONDITION (q->length >= 8);
6403 in_len = (q->opcode == BIFF_LABEL_v0)
6404 ? GSF_LE_GET_GUINT8 (q->data + 7)
6405 : GSF_LE_GET_GUINT16 (q->data + 6);
6406 XL_CHECK_CONDITION (q->length - 8 >= in_len);
6408 xf = excel_set_xf (esheet, q);
6409 if (!xf)
6410 return;
6411 fd = excel_font_get (esheet->container.importer, xf->font_idx);
6413 txt = excel_get_text (esheet->container.importer, q->data + 8,
6414 in_len, &str_len, fd ? &fd->codepage : NULL,
6415 q->length - 8);
6417 d (0, g_printerr ("%s in %s;\n",
6418 has_markup ? "formatted string" : "string",
6419 cell_name (cell)););
6421 if (txt != NULL) {
6422 GOFormat *fmt = NULL;
6423 if (has_markup)
6424 fmt = excel_read_LABEL_markup (q, esheet, txt, str_len);
6426 /* might free txt, do not do this until after parsing markup */
6427 v = value_new_string_nocopy (txt);
6428 if (fmt != NULL) {
6429 value_set_fmt (v, fmt);
6430 go_format_unref (fmt);
6432 gnm_cell_set_value (cell, v);
6436 static void
6437 excel_read_BOOLERR (BiffQuery *q, ExcelReadSheet *esheet)
6439 unsigned base = (q->opcode == BIFF_BOOLERR_v0) ? 7 : 6;
6440 GnmValue *v;
6442 XL_CHECK_CONDITION (q->length >= base + 2);
6444 if (GSF_LE_GET_GUINT8 (q->data + base + 1)) {
6445 GnmEvalPos ep;
6446 eval_pos_init (&ep, esheet->sheet, XL_GETCOL (q), XL_GETROW (q));
6447 v = xls_value_new_err (&ep, GSF_LE_GET_GUINT8 (q->data + base));
6448 } else
6449 v = value_new_bool (GSF_LE_GET_GUINT8 (q->data + base));
6450 excel_sheet_insert_val (esheet, q, v);
6453 static void
6454 excel_read_HEADER_FOOTER (GnmXLImporter const *importer,
6455 BiffQuery *q, ExcelReadSheet *esheet,
6456 gboolean is_header)
6458 GnmPrintInformation *pi = esheet->sheet->print_info;
6460 if (q->length) {
6461 char *str;
6463 if (importer->ver >= MS_BIFF_V8)
6464 str = excel_biff_text_2 (importer, q, 0);
6465 else
6466 str = excel_biff_text_1 (importer, q, 0);
6468 d (2, g_printerr ("%s == '%s'\n", is_header ? "header" : "footer", str););
6470 xls_header_footer_import (is_header ? &pi->header : &pi->footer,
6471 str);
6473 g_free (str);
6477 static void
6478 excel_read_REFMODE (BiffQuery *q, ExcelReadSheet *esheet)
6480 guint16 mode;
6482 XL_CHECK_CONDITION (q->length >= 2);
6483 mode = GSF_LE_GET_GUINT16 (q->data);
6484 g_object_set (esheet->sheet, "use-r1c1", mode == 0, NULL);
6487 static void
6488 excel_read_PAGE_BREAK (BiffQuery *q, ExcelReadSheet *esheet, gboolean is_vert)
6490 unsigned i;
6491 unsigned step = (esheet_ver (esheet) >= MS_BIFF_V8) ? 6 : 2;
6492 guint16 count;
6493 GnmPageBreaks *breaks;
6495 XL_CHECK_CONDITION (q->length >= 2);
6496 count = GSF_LE_GET_GUINT16 (q->data);
6497 XL_CHECK_CONDITION (q->length >= 2 + count * step);
6498 breaks = gnm_page_breaks_new (is_vert);
6500 /* 1) Ignore the first/last info for >= biff8
6501 * 2) Assume breaks are manual in the absence of any information */
6502 for (i = 0; i < count ; i++) {
6503 gnm_page_breaks_append_break (breaks,
6504 GSF_LE_GET_GUINT16 (q->data + 2 + i*step), GNM_PAGE_BREAK_MANUAL);
6505 #if 0
6506 g_printerr ("%d %d:%d\n",
6507 GSF_LE_GET_GUINT16 (q->data + 2 + i*step),
6508 GSF_LE_GET_GUINT16 (q->data + 2 + i*step + 2),
6509 GSF_LE_GET_GUINT16 (q->data + 2 + i*step + 4));
6510 #endif
6512 print_info_set_breaks (esheet->sheet->print_info, breaks);
6515 static void
6516 excel_read_INTEGER (BiffQuery *q, ExcelReadSheet *esheet)
6518 XL_CHECK_CONDITION (q->length >= 7 + 2);
6519 excel_sheet_insert_val
6520 (esheet, q,
6521 value_new_int (GSF_LE_GET_GUINT16 (q->data + 7)));
6524 static void
6525 excel_read_NUMBER (BiffQuery *q, ExcelReadSheet *esheet, size_t ofs)
6527 XL_CHECK_CONDITION (q->length >= ofs + 8);
6528 excel_sheet_insert_val
6529 (esheet, q,
6530 value_new_float (gsf_le_get_double (q->data + ofs)));
6533 static void
6534 excel_read_MARGIN (BiffQuery *q, ExcelReadSheet *esheet)
6536 double m;
6537 GnmPrintInformation *pi = esheet->sheet->print_info;
6539 XL_CHECK_CONDITION (q->length >= 8);
6540 m = GO_IN_TO_PT (gsf_le_get_double (q->data));
6542 switch (q->opcode) {
6543 case BIFF_LEFT_MARGIN: print_info_set_margin_left (pi, m); break;
6544 case BIFF_RIGHT_MARGIN: print_info_set_margin_right (pi, m); break;
6545 case BIFF_TOP_MARGIN: print_info_set_edge_to_below_header (pi, m); break;
6546 case BIFF_BOTTOM_MARGIN: print_info_set_edge_to_above_footer (pi, m); break;
6547 default: g_assert_not_reached ();
6552 static void
6553 excel_read_PRINTHEADERS (BiffQuery *q, ExcelReadSheet *esheet)
6555 XL_CHECK_CONDITION (q->length >= 2);
6556 esheet->sheet->print_info->print_titles =
6557 (GSF_LE_GET_GUINT16 (q->data) == 1);
6560 static void
6561 excel_read_PRINTGRIDLINES (BiffQuery *q, ExcelReadSheet *esheet)
6563 XL_CHECK_CONDITION (q->length >= 2);
6564 esheet->sheet->print_info->print_grid_lines =
6565 (GSF_LE_GET_GUINT16 (q->data) == 1);
6568 static void
6569 excel_read_HCENTER (BiffQuery *q, ExcelReadSheet *esheet)
6571 XL_CHECK_CONDITION (q->length >= 2);
6572 esheet->sheet->print_info->center_horizontally =
6573 GSF_LE_GET_GUINT16 (q->data) == 0x1;
6576 static void
6577 excel_read_VCENTER (BiffQuery *q, ExcelReadSheet *esheet)
6579 XL_CHECK_CONDITION (q->length >= 2);
6580 esheet->sheet->print_info->center_vertically =
6581 GSF_LE_GET_GUINT16 (q->data) == 0x1;
6584 static void
6585 excel_read_PLS (BiffQuery *q, ExcelReadSheet *esheet)
6587 XL_CHECK_CONDITION (q->length >= 2);
6588 if (GSF_LE_GET_GUINT16 (q->data) == 0x00) {
6590 * q->data + 2 -> q->data + q->length
6591 * map to a DEVMODE structure see MS' SDK.
6593 } else if (GSF_LE_GET_GUINT16 (q->data) == 0x01) {
6595 * q's data maps to a TPrint structure
6596 * see Inside Macintosh Vol II p 149.
6601 static void
6602 excel_read_RK (BiffQuery *q, ExcelReadSheet *esheet)
6604 XL_CHECK_CONDITION (q->length >= 6 + 4);
6605 excel_sheet_insert_val (esheet, q,
6606 biff_get_rk (q->data + 6));
6609 static void
6610 excel_read_LABELSST (BiffQuery *q, ExcelReadSheet *esheet)
6612 unsigned i;
6614 XL_CHECK_CONDITION (q->length >= 6 + 4);
6615 i = GSF_LE_GET_GUINT32 (q->data + 6);
6617 if (esheet->container.importer->sst && i < esheet->container.importer->sst_len) {
6618 GnmValue *v;
6619 GOString *str = esheet->container.importer->sst[i].content;
6620 /* ? Why would there be a NULL ? */
6621 if (NULL != str) {
6622 go_string_ref (str);
6623 v = value_new_string_str (str);
6624 d (2, g_printerr ("str=%s\n", str->str););
6625 } else
6626 v = value_new_string ("");
6627 if (esheet->container.importer->sst[i].markup != NULL)
6628 value_set_fmt (v, esheet->container.importer->sst[i].markup);
6629 excel_sheet_insert_val (esheet, q, v);
6630 } else
6631 g_warning ("string index 0x%u >= 0x%x\n",
6632 i, esheet->container.importer->sst_len);
6635 static gboolean
6636 excel_read_sheet (BiffQuery *q, GnmXLImporter *importer,
6637 WorkbookView *wb_view, ExcelReadSheet *esheet)
6639 MsBiffVersion const ver = importer->ver;
6641 g_return_val_if_fail (importer != NULL, FALSE);
6642 g_return_val_if_fail (esheet != NULL, FALSE);
6643 g_return_val_if_fail (esheet->sheet != NULL, FALSE);
6644 g_return_val_if_fail (esheet->sheet->print_info != NULL, FALSE);
6646 d (1, g_printerr ("----------------- '%s' -------------\n",
6647 esheet->sheet->name_unquoted););
6649 if (ver <= MS_BIFF_V4) {
6650 /* Style is per-sheet in early Excel - default TODO */
6651 excel_workbook_reset_style (importer);
6652 } else {
6653 /* Apply the default style */
6654 GnmStyle *mstyle = excel_get_style_from_xf (esheet,
6655 excel_get_xf (esheet, 15));
6656 if (mstyle != NULL) {
6657 GnmRange r;
6658 range_init_full_sheet (&r, esheet->sheet);
6659 sheet_style_set_range (esheet->sheet, &r, mstyle);
6663 for (; ms_biff_query_next (q) ;
6664 go_io_value_progress_update (importer->context, q->streamPos)) {
6666 d (5, {
6667 const char *opname = biff_opcode_name (q->opcode);
6668 g_printerr ("Opcode: 0x%x %s\n",
6669 q->opcode,
6670 opname ? opname : "unknown");
6673 switch (q->opcode) {
6674 case BIFF_DIMENSIONS_v0: break; /* ignore ancient XL2 variant */
6675 case BIFF_DIMENSIONS_v2: excel_read_DIMENSIONS (q, esheet); break;
6677 case BIFF_BLANK_v0:
6678 case BIFF_BLANK_v2:
6679 (void)excel_set_xf (esheet, q);
6680 break;
6682 case BIFF_INTEGER:
6683 excel_read_INTEGER (q, esheet);
6684 break;
6685 case BIFF_NUMBER_v0:
6686 excel_read_NUMBER (q, esheet, 7);
6687 break;
6688 case BIFF_NUMBER_v2:
6689 excel_read_NUMBER (q, esheet, 6);
6690 break;
6692 case BIFF_LABEL_v0:
6693 case BIFF_LABEL_v2:
6694 excel_read_LABEL (q, esheet, FALSE);
6695 break;
6697 case BIFF_BOOLERR_v0:
6698 case BIFF_BOOLERR_v2:
6699 excel_read_BOOLERR (q, esheet);
6700 break;
6702 case BIFF_FORMULA_v0:
6703 case BIFF_FORMULA_v2:
6704 case BIFF_FORMULA_v4: excel_read_FORMULA (q, esheet); break;
6705 /* case STRING : is handled elsewhere since it always follows FORMULA */
6706 case BIFF_ROW_v0:
6707 case BIFF_ROW_v2: excel_read_ROW (q, esheet); break;
6708 case BIFF_EOF: goto success;
6710 /* NOTE : bytes 12 & 16 appear to require the non decrypted data */
6711 case BIFF_INDEX_v0:
6712 case BIFF_INDEX_v2: break;
6714 case BIFF_CALCCOUNT: excel_read_CALCCOUNT (q, importer); break;
6715 case BIFF_CALCMODE: excel_read_CALCMODE (q,importer); break;
6717 case BIFF_PRECISION :
6718 #if 0
6720 /* FIXME: implement in gnumeric */
6721 /* state of 'Precision as Displayed' option */
6722 guint16 const data = GSF_LE_GET_GUINT16 (q->data);
6723 gboolean const prec_as_displayed = (data == 0);
6725 #endif
6726 break;
6728 case BIFF_REFMODE: excel_read_REFMODE (q, esheet); break;
6729 case BIFF_DELTA: excel_read_DELTA (q, importer); break;
6730 case BIFF_ITERATION: excel_read_ITERATION (q, importer); break;
6731 case BIFF_OBJPROTECT:
6732 case BIFF_PROTECT: excel_read_sheet_PROTECT (q, esheet); break;
6734 case BIFF_PASSWORD:
6735 if (q->length == 2) {
6736 d (2, g_printerr ("sheet password '%hx'\n",
6737 GSF_LE_GET_GUINT16 (q->data)););
6739 break;
6743 case BIFF_HEADER: excel_read_HEADER_FOOTER (importer, q, esheet, TRUE); break;
6744 case BIFF_FOOTER: excel_read_HEADER_FOOTER (importer, q, esheet, FALSE); break;
6746 case BIFF_EXTERNCOUNT: /* ignore */ break;
6747 case BIFF_EXTERNSHEET: /* These cannot be biff8 */
6748 excel_read_EXTERNSHEET_v7 (q, &esheet->container);
6749 break;
6751 case BIFF_VERTICALPAGEBREAKS: excel_read_PAGE_BREAK (q, esheet, TRUE); break;
6752 case BIFF_HORIZONTALPAGEBREAKS: excel_read_PAGE_BREAK (q, esheet, FALSE); break;
6754 case BIFF_NOTE: excel_read_NOTE (q, esheet); break;
6755 case BIFF_SELECTION: excel_read_SELECTION (q, esheet); break;
6756 case BIFF_EXTERNNAME_v0:
6757 case BIFF_EXTERNNAME_v2:
6758 excel_read_EXTERNNAME (q, &esheet->container);
6759 break;
6760 case BIFF_DEFAULTROWHEIGHT_v0:
6761 case BIFF_DEFAULTROWHEIGHT_v2:
6762 excel_read_DEF_ROW_HEIGHT (q, esheet);
6763 break;
6765 case BIFF_LEFT_MARGIN:
6766 case BIFF_RIGHT_MARGIN:
6767 case BIFF_TOP_MARGIN:
6768 case BIFF_BOTTOM_MARGIN:
6769 excel_read_MARGIN (q, esheet);
6770 break;
6772 case BIFF_PRINTHEADERS:
6773 excel_read_PRINTHEADERS (q, esheet);
6774 break;
6776 case BIFF_PRINTGRIDLINES:
6777 excel_read_PRINTGRIDLINES (q, esheet);
6778 break;
6780 case BIFF_WINDOW1: break; /* what does this do for a sheet ? */
6781 case BIFF_WINDOW2_v0:
6782 case BIFF_WINDOW2_v2:
6783 excel_read_WINDOW2 (q, esheet, wb_view);
6784 break;
6785 case BIFF_BACKUP: break;
6786 case BIFF_PANE: excel_read_PANE (q, esheet, wb_view); break;
6788 case BIFF_PLS: excel_read_PLS (q, esheet); break;
6790 case BIFF_DEFCOLWIDTH: excel_read_DEF_COL_WIDTH (q, esheet); break;
6792 case BIFF_OBJ: ms_read_OBJ (q, sheet_container (esheet), NULL); break;
6794 case BIFF_SAVERECALC: break;
6795 case BIFF_TAB_COLOR: excel_read_TAB_COLOR (q, esheet); break;
6796 case BIFF_COLINFO: excel_read_COLINFO (q, esheet); break;
6798 case BIFF_RK: excel_read_RK (q, esheet); break;
6800 case BIFF_IMDATA: {
6801 GdkPixbuf *pixbuf = excel_read_IMDATA (q, FALSE);
6802 if (pixbuf)
6803 g_object_unref (pixbuf);
6804 break;
6806 case BIFF_GUTS: excel_read_GUTS (q, esheet); break;
6807 case BIFF_WSBOOL: excel_read_WSBOOL (q, esheet); break;
6808 case BIFF_GRIDSET: break;
6810 case BIFF_HCENTER: excel_read_HCENTER (q, esheet); break;
6811 case BIFF_VCENTER: excel_read_VCENTER (q, esheet); break;
6813 case BIFF_COUNTRY: break;
6814 case BIFF_STANDARDWIDTH: break; /* the 'standard width dialog' ? */
6815 case BIFF_FILTERMODE: break;
6816 case BIFF_AUTOFILTERINFO: break;
6817 case BIFF_AUTOFILTER: excel_read_AUTOFILTER (q, esheet); break;
6818 case BIFF_SCL: excel_read_SCL (q, esheet->sheet); break;
6819 case BIFF_SETUP: excel_read_SETUP (q, esheet); break;
6820 case BIFF_GCW: break;
6821 case BIFF_SCENMAN: break;
6822 case BIFF_SCENARIO: break;
6823 case BIFF_MULRK: excel_read_MULRK (q, esheet); break;
6824 case BIFF_MULBLANK: excel_read_MULBLANK (q, esheet); break;
6826 case BIFF_RSTRING: excel_read_LABEL (q, esheet, TRUE); break;
6828 case BIFF_DBCELL: break; /* Can be ignored on read side */
6830 case BIFF_BG_PIC: excel_read_BG_PIC (q, esheet); break;
6831 case BIFF_MERGECELLS: excel_read_MERGECELLS (q, esheet); break;
6833 case BIFF_MS_O_DRAWING:
6834 case BIFF_MS_O_DRAWING_GROUP:
6835 case BIFF_MS_O_DRAWING_SELECTION:
6836 ms_escher_parse (q, sheet_container (esheet), FALSE);
6837 break;
6838 case BIFF_PHONETIC: break;
6840 case BIFF_LABELSST:
6841 excel_read_LABELSST (q, esheet);
6842 break;
6844 case BIFF_XF_OLD_v0:
6845 case BIFF_XF_OLD_v2:
6846 case BIFF_XF_OLD_v4:
6847 excel_read_XF_OLD (q, importer);
6848 break;
6849 case BIFF_XF_INDEX:
6850 excel_read_XF_INDEX (q, esheet);
6851 break;
6853 case BIFF_NAME_v0:
6854 case BIFF_NAME_v2: excel_read_NAME (q, importer, esheet); break;
6855 case BIFF_FONT_v0:
6856 case BIFF_FONT_v2: excel_read_FONT (q, importer); break;
6857 case BIFF_FORMAT_v0:
6858 case BIFF_FORMAT_v4: excel_read_FORMAT (q, importer); break;
6859 case BIFF_STYLE: break;
6860 case BIFF_1904: excel_read_1904 (q, importer); break;
6861 case BIFF_FILEPASS: {
6862 const char *problem = excel_read_FILEPASS (q, importer);
6863 if (problem != NULL) {
6864 go_cmd_context_error_import (GO_CMD_CONTEXT (importer->context), problem);
6865 return FALSE;
6867 break;
6870 case BIFF_CONDFMT:
6871 excel_read_CONDFMT (q, esheet, importer);
6872 break;
6873 case BIFF_CF:
6874 g_warning ("Found a CF record without a CONDFMT ??");
6875 break;
6876 case BIFF_DVAL: excel_read_DVAL (q, esheet); break;
6877 case BIFF_HLINK: excel_read_HLINK (q, esheet); break;
6878 case BIFF_CODENAME: excel_read_CODENAME (q, importer, esheet); break;
6879 break;
6880 case BIFF_DV:
6881 g_warning ("Found a DV record without a DVal ??");
6882 excel_read_DV (q, esheet);
6883 break;
6885 case BIFF_SXVIEWEX9 :
6886 /* Seems to contain pivot table autoformat indicies, plus ?? */
6887 /* samples/excel/dbfuns.xls has as sample of this record
6888 * and I added code in OOo */
6889 break;
6891 case BIFF_SHEETPROTECTION:
6892 excel_read_SHEETPROTECTION (q, esheet->sheet);
6893 break;
6895 /* HACK: it seems that in older versions of XL the
6896 * charts did not have a wrapper object. the first
6897 * record in the sequence of chart records was a
6898 * CHART_UNITS followed by CHART_CHART. We play off of
6899 * that. When we encounter a CHART_units record we
6900 * jump to the chart handler which then starts parsing
6901 * at the NEXT record.
6903 case BIFF_CHART_units : {
6904 SheetObject *obj = sheet_object_graph_new (NULL);
6905 ms_excel_chart_read (q, sheet_container (esheet),
6906 obj, NULL);
6907 sheet_object_set_sheet (obj, esheet->sheet);
6908 g_object_unref (obj);
6909 break;
6911 case BIFF_SXVIEW: xls_read_SXVIEW (q, esheet); break;
6912 case BIFF_SXVD : xls_read_SXVD (q, esheet); break;
6913 case BIFF_SXVDEX : break; /* pulled in with SXVD */
6914 case BIFF_SXVI : break; /* pulled in with SXVD */
6915 case BIFF_SXIVD: xls_read_SXIVD (q, esheet); break;
6917 default:
6918 excel_unexpected_biff (q, "Sheet", ms_excel_read_debug);
6922 g_printerr ("Error, hit end without EOF\n");
6924 return FALSE;
6926 success :
6927 /* We need a sheet to extract styles, so store the workbook default as
6928 * soon as we parse a sheet. It is a kludge, but not terribly costly */
6929 g_object_set_data_full (G_OBJECT (importer->wb),
6930 "xls-default-style",
6931 excel_get_style_from_xf (esheet, excel_get_xf (esheet, 0)),
6932 (GDestroyNotify) gnm_style_unref);
6933 return TRUE;
6937 * NOTE : MS Docs are incorrect
6939 * unsigned short num_tabs;
6940 * unsigned short num_characters_in_book;
6941 * unsigned char flag_for_unicode; 0 or 1
6942 * var encoded string stored as 1 or 2 byte characters
6944 static void
6945 excel_read_SUPBOOK (BiffQuery *q, GnmXLImporter *importer)
6947 unsigned numTabs, len;
6948 unsigned i, t;
6949 guint32 byte_length, ofs;
6950 ExcelSupBook *new_supbook;
6951 char *bookname;
6953 XL_CHECK_CONDITION (q->length >= 4);
6954 numTabs = GSF_LE_GET_GUINT16 (q->data);
6955 len = GSF_LE_GET_GUINT16 (q->data + 2);
6957 i = importer->v8.supbook->len;
6958 g_array_set_size (importer->v8.supbook, i + 1);
6959 new_supbook = &g_array_index (importer->v8.supbook, ExcelSupBook, i);
6961 d (2, g_printerr ("supbook %d has %d sheets\n", i, numTabs););
6963 new_supbook->externname = g_ptr_array_new ();
6964 new_supbook->wb = NULL;
6966 if (q->length == 4 && len == 0x0401) {
6967 d (2, g_printerr ("\t is self referential\n"););
6968 new_supbook->type = EXCEL_SUP_BOOK_SELFREF;
6969 return;
6971 if (q->length == 4 && len == 0x3A01) {
6972 d (2, g_printerr ("\t is a plugin\n"););
6973 new_supbook->type = EXCEL_SUP_BOOK_PLUGIN;
6974 return;
6977 new_supbook->type = EXCEL_SUP_BOOK_STD;
6978 XL_CHECK_CONDITION (q->length >= 5);
6980 bookname = excel_get_text (importer, q->data + 4, len,
6981 &byte_length, NULL, q->length - 4);
6982 d (2, g_printerr ("\trefers to %s\n", bookname););
6984 * Bookname can be
6985 * (1) a single space -- "unused"
6986 * (2) a single nul -- self referencing
6987 * (3) an OLE-link VirtualPath
6988 * (4) any other VirtualPath -- external workbook
6990 if (len == 1 && *bookname == 0) {
6991 new_supbook->type = EXCEL_SUP_BOOK_SELFREF;
6992 } else if (len == 1 && *bookname == ' ') {
6993 /* what? */
6995 g_free (bookname);
6997 ofs = 4 + byte_length;
6998 XL_CHECK_CONDITION (ofs <= q->length);
7000 for (t = 0; t < numTabs; t++) {
7001 char *name;
7002 guint32 length;
7004 XL_CHECK_CONDITION (ofs + 2 <= q->length);
7006 length = GSF_LE_GET_GUINT16 (q->data + ofs);
7007 ofs += 2;
7008 name = excel_get_text (importer, q->data + ofs, length,
7009 &byte_length, NULL, q->length - ofs);
7010 d (2, g_printerr ("\tSheet %d -> %s\n", t, name););
7011 g_free (name);
7013 ofs += byte_length;
7016 #warning "create a workbook and sheets when we have a facility for merging things"
7019 static void
7020 excel_read_WINDOW1 (BiffQuery *q, WorkbookView *wb_view)
7022 if (q->length >= 16) {
7023 #if 0
7024 /* In 1/20ths of a point */
7025 guint16 const xPos = GSF_LE_GET_GUINT16 (q->data + 0);
7026 guint16 const yPos = GSF_LE_GET_GUINT16 (q->data + 2);
7027 #endif
7028 guint16 const width = GSF_LE_GET_GUINT16 (q->data + 4);
7029 guint16 const height = GSF_LE_GET_GUINT16 (q->data + 6);
7030 guint16 const options = GSF_LE_GET_GUINT16 (q->data + 8);
7031 #if 0
7032 /* duplicated in the WINDOW2 record */
7033 guint16 const selTab = GSF_LE_GET_GUINT16 (q->data + 10);
7035 guint16 const firstTab= GSF_LE_GET_GUINT16 (q->data + 12);
7036 guint16 const tabsSel = GSF_LE_GET_GUINT16 (q->data + 14);
7038 /* (width of tab)/(width of horizontal scroll bar) / 1000 */
7039 guint16 const ratio = GSF_LE_GET_GUINT16 (q->data + 16);
7040 #endif
7043 * We are sizing the window including the toolbars,
7044 * menus, and notbook tabs. Excel does not.
7046 * NOTE: This is the size of the MDI sub-window, not the size of
7047 * the containing excel window.
7049 wb_view_preferred_size (wb_view,
7050 .5 + width * gnm_app_display_dpi_get (TRUE) / (72. * 20.),
7051 .5 + height * gnm_app_display_dpi_get (FALSE) / (72. * 20.));
7053 if (options & 0x0001)
7054 g_printerr ("Unsupported: Hidden workbook\n");
7055 if (options & 0x0002)
7056 g_printerr ("Unsupported: Iconic workbook\n");
7058 g_object_set (G_OBJECT (wb_view),
7059 "show-horizontal-scrollbar", !!(options & 0x0008),
7060 "show-vertical-scrollbar", !!(options & 0x0010),
7061 "show-notebook-tabs", !!(options & 0x0020),
7062 NULL);
7066 static void
7067 excel_read_BOF (BiffQuery *q,
7068 GnmXLImporter *importer,
7069 WorkbookView *wb_view,
7070 GOIOContext *context,
7071 MsBiffBofData **version, unsigned *current_sheet)
7073 /* The first BOF seems to be OK, the rest lie ? */
7074 MsBiffVersion vv = MS_BIFF_V_UNKNOWN;
7075 MsBiffBofData *ver = *version;
7076 char const *version_desc = NULL;
7078 if (ver) {
7079 vv = ver->version;
7080 ms_biff_bof_data_destroy (ver);
7082 *version = ver = ms_biff_bof_data_new (q);
7083 if (vv != MS_BIFF_V_UNKNOWN)
7084 ver->version = vv;
7086 if (ver->type == MS_BIFF_TYPE_Workbook) {
7087 gnm_xl_importer_set_version (importer, ver->version);
7088 if (ver->version >= MS_BIFF_V8) {
7089 guint32 ver;
7090 XL_CHECK_CONDITION (q->length >= 8);
7091 ver = GSF_LE_GET_GUINT32 (q->data + 4);
7092 if (ver == 0x4107cd18)
7093 version_desc = "Excel 2000 ?";
7094 else
7095 version_desc = "Excel 97 +";
7096 } else if (ver->version >= MS_BIFF_V7)
7097 version_desc = "Excel 95";
7098 else if (ver->version >= MS_BIFF_V5)
7099 version_desc = "Excel 5.x";
7100 else if (ver->version >= MS_BIFF_V4)
7101 version_desc = "Excel 4.x";
7102 else if (ver->version >= MS_BIFF_V3)
7103 version_desc = "Excel 3.x - shouldn't happen";
7104 else if (ver->version >= MS_BIFF_V2)
7105 version_desc = "Excel 2.x - shouldn't happen";
7106 } else if (ver->type == MS_BIFF_TYPE_Worksheet ||
7107 ver->type == MS_BIFF_TYPE_Chart) {
7108 BiffBoundsheetData *bs = g_hash_table_lookup (
7109 importer->boundsheet_data_by_stream, GINT_TO_POINTER (q->streamPos));
7110 ExcelReadSheet *esheet;
7111 if (bs == NULL) {
7112 if (ver->version > MS_BIFF_V4) /* be anal */
7113 g_printerr ("Sheet offset in stream of 0x%lx not found in list\n",
7114 (long)q->streamPos);
7115 if (*current_sheet >= importer->excel_sheets->len) {
7116 esheet = excel_sheet_new (importer, "Worksheet", GNM_SHEET_DATA);
7117 /* Top level worksheets existed up to & including 4.x */
7118 gnm_xl_importer_set_version (importer, ver->version);
7119 if (ver->version >= MS_BIFF_V5)
7120 version_desc = ">= Excel 5 with no BOUNDSHEET ?? - shouldn't happen";
7121 else if (ver->version >= MS_BIFF_V4)
7122 version_desc = "Excel 4.x single worksheet";
7123 else if (ver->version >= MS_BIFF_V3)
7124 version_desc = "Excel 3.x single worksheet";
7125 else if (ver->version >= MS_BIFF_V2)
7126 version_desc = "Excel 2.x single worksheet";
7127 } else
7128 esheet = g_ptr_array_index (importer->excel_sheets, *current_sheet);
7129 } else
7130 esheet = bs->esheet;
7132 g_return_if_fail (esheet != NULL);
7133 (*current_sheet)++;
7135 if (ver->type == MS_BIFF_TYPE_Worksheet) {
7136 excel_read_sheet (q, importer, wb_view, esheet);
7137 ms_container_realize_objs (sheet_container (esheet));
7138 /* reverse the sheet objects satck order */
7139 esheet->sheet->sheet_objects = g_slist_reverse (esheet->sheet->sheet_objects);
7140 } else {
7141 SheetObject *obj = sheet_object_graph_new (NULL);
7142 ms_excel_chart_read (q, sheet_container (esheet),
7143 obj, esheet->sheet);
7144 sheet_object_set_sheet (obj, esheet->sheet);
7145 g_object_unref (obj);
7148 } else if (ver->type == MS_BIFF_TYPE_VBModule ||
7149 ver->type == MS_BIFF_TYPE_Macrosheet) {
7150 /* Skip contents of Module, or MacroSheet */
7151 if (ver->type != MS_BIFF_TYPE_Macrosheet)
7152 version_desc = "VB Module";
7153 else {
7154 (*current_sheet)++;
7155 version_desc = "XLM Macrosheet";
7158 while (ms_biff_query_next (q) && q->opcode != BIFF_EOF)
7159 d (5, ms_biff_query_dump (q););
7160 if (q->opcode != BIFF_EOF)
7161 g_warning ("EXCEL: file format error. Missing BIFF_EOF");
7162 } else if (ver->type == MS_BIFF_TYPE_Workspace) {
7163 /* Multiple sheets, XLW format from Excel 4.0 */
7164 version_desc = "Excel 4.x workbook";
7165 gnm_xl_importer_set_version (importer, ver->version);
7166 } else
7167 g_printerr ("Unknown BOF (%x)\n", ver->type);
7169 if (NULL != version_desc) {
7170 d (1, g_printerr ("%s\n", version_desc););
7174 static void
7175 excel_read_CODEPAGE (BiffQuery *q, GnmXLImporter *importer)
7177 /* This seems to appear within a workbook */
7178 /* MW: And on Excel seems to drive the display
7179 of currency amounts. */
7180 XL_CHECK_CONDITION (q->length >= 2);
7181 gnm_xl_importer_set_codepage (importer,
7182 GSF_LE_GET_GUINT16 (q->data));
7185 static void
7186 excel_read_RECALCID (BiffQuery *q, GnmXLImporter *importer)
7188 guint32 engine;
7190 XL_CHECK_CONDITION (q->length >= 8);
7191 engine = GSF_LE_GET_GUINT32 (q->data + 4);
7193 (void)engine;
7196 static void
7197 excel_read_UNCALCED (BiffQuery *q, GnmXLImporter *importer)
7201 void
7202 excel_read_workbook (GOIOContext *context, WorkbookView *wb_view,
7203 GsfInput *input,
7204 gboolean *is_double_stream_file,
7205 char const *opt_enc)
7207 GnmXLImporter *importer;
7208 BiffQuery *q;
7209 MsBiffBofData *ver = NULL;
7210 unsigned current_sheet = 0;
7211 const char *problem_loading = NULL;
7212 gboolean stop_loading = FALSE;
7213 gboolean prev_was_eof = FALSE;
7215 go_io_progress_message (context, _("Reading file..."));
7216 go_io_value_progress_set (context, gsf_input_size (input), N_BYTES_BETWEEN_PROGRESS_UPDATES);
7217 q = ms_biff_query_new (input);
7219 importer = gnm_xl_importer_new (context, wb_view, opt_enc);
7221 *is_double_stream_file = FALSE;
7222 if (ms_biff_query_next (q) &&
7223 (q->opcode == BIFF_BOF_v0 ||
7224 q->opcode == BIFF_BOF_v2 ||
7225 q->opcode == BIFF_BOF_v4 ||
7226 q->opcode == BIFF_BOF_v8))
7227 excel_read_BOF (q, importer, wb_view, context,
7228 &ver, &current_sheet);
7229 while (!stop_loading && /* we have not hit the end */
7230 problem_loading == NULL && /* there were no problems so far */
7231 ms_biff_query_next (q)) { /* we can load the record */
7233 d (5, {
7234 const char *opname = biff_opcode_name (q->opcode);
7235 g_printerr ("Opcode: 0x%x %s\n",
7236 q->opcode,
7237 opname ? opname : "unknown");
7240 switch (q->opcode) {
7241 case BIFF_BOF_v0:
7242 case BIFF_BOF_v2:
7243 case BIFF_BOF_v4:
7244 case BIFF_BOF_v8:
7245 excel_read_BOF (q, importer, wb_view, context, &ver, &current_sheet);
7246 break;
7248 case BIFF_EOF:
7249 prev_was_eof = TRUE;
7250 d (0, g_printerr ("End of worksheet spec.\n"););
7251 break;
7253 case BIFF_FONT_v0:
7254 case BIFF_FONT_v2: excel_read_FONT (q, importer); break;
7255 case BIFF_WINDOW1: excel_read_WINDOW1 (q, wb_view); break;
7256 case BIFF_BOUNDSHEET: excel_read_BOUNDSHEET (q, importer); break;
7257 case BIFF_PALETTE: excel_read_PALETTE (q, importer); break;
7259 case BIFF_XF_OLD_v0:
7260 case BIFF_XF_OLD_v2:
7261 case BIFF_XF_OLD_v4: excel_read_XF_OLD (q, importer); break;
7262 case BIFF_XF: excel_read_XF (q, importer); break;
7264 case BIFF_EXTERNCOUNT: /* ignore */ break;
7265 case BIFF_EXTERNSHEET:
7266 excel_read_EXTERNSHEET (q, importer, ver);
7267 break;
7269 case BIFF_PRECISION : {
7270 #if 0
7271 /* FIXME: implement in gnumeric */
7272 /* state of 'Precision as Displayed' option */
7273 guint16 const data = GSF_LE_GET_GUINT16 (q->data);
7274 gboolean const prec_as_displayed = (data == 0);
7275 #endif
7276 break;
7279 case BIFF_FORMAT_v0:
7280 case BIFF_FORMAT_v4: excel_read_FORMAT (q, importer); break;
7282 case BIFF_BACKUP: break;
7283 case BIFF_CODEPAGE: /* DUPLICATE 42 */
7284 excel_read_CODEPAGE (q, importer);
7285 break;
7287 case BIFF_OBJPROTECT:
7288 case BIFF_PROTECT:
7289 excel_read_workbook_PROTECT (q, wb_view);
7290 break;
7292 case BIFF_PASSWORD:
7293 break;
7295 case BIFF_FILEPASS: /* All records after this are encrypted */
7296 problem_loading = excel_read_FILEPASS(q, importer);
7297 break;
7299 case BIFF_STYLE:
7300 break;
7302 case BIFF_WINDOWPROTECT:
7303 break;
7305 case BIFF_EXTERNNAME_v0:
7306 case BIFF_EXTERNNAME_v2: excel_read_EXTERNNAME (q, &importer->container); break;
7307 case BIFF_NAME_v0:
7308 case BIFF_NAME_v2: excel_read_NAME (q, importer, NULL); break;
7309 case BIFF_XCT: excel_read_XCT (q, importer); break;
7311 case BIFF_WRITEACCESS:
7312 break;
7314 case BIFF_HIDEOBJ: break;
7315 case BIFF_FNGROUPCOUNT: break;
7316 case BIFF_MMS: break;
7318 /* Flags that the project has some VBA */
7319 case BIFF_OBPROJ: break;
7320 case BIFF_BOOKBOOL: break;
7321 case BIFF_COUNTRY: break;
7322 case BIFF_INTERFACEHDR: break;
7323 case BIFF_INTERFACEEND: break;
7324 case BIFF_TOOLBARHDR: break;
7325 case BIFF_TOOLBAREND: break;
7327 case BIFF_1904: excel_read_1904 (q, importer); break;
7329 case BIFF_SELECTION: /* 0, NOT 10 */
7330 break;
7332 case BIFF_DIMENSIONS_v0:
7333 /* Ignore files that pad the end with zeros */
7334 if (prev_was_eof) {
7335 stop_loading = TRUE;
7336 break;
7338 /* fall through */
7339 case BIFF_DIMENSIONS_v2:
7340 excel_read_DIMENSIONS (q, NULL);
7341 break;
7343 case BIFF_OBJ: ms_read_OBJ (q, &importer->container, NULL); break;
7344 case BIFF_SCL: break;
7345 case BIFF_TABIDCONF: break;
7346 case BIFF_MS_O_DRAWING:
7347 case BIFF_MS_O_DRAWING_GROUP:
7348 case BIFF_MS_O_DRAWING_SELECTION:
7349 ms_escher_parse (q, &importer->container, FALSE);
7350 break;
7352 case BIFF_ADDMENU:
7353 d (1, g_printerr ("%smenu with %d sub items",
7354 (GSF_LE_GET_GUINT8 (q->data + 6) == 1) ? "" : "Placeholder ",
7355 GSF_LE_GET_GUINT8 (q->data + 5)););
7356 break;
7358 case BIFF_SST: excel_read_SST (q, importer); break;
7359 case BIFF_EXTSST: excel_read_EXSST (q, importer); break;
7361 case BIFF_DSF: /* stored in the biff8 workbook */
7362 case BIFF_XL5MODIFY: /* stored in the biff5/7 book */
7364 gboolean dsf = (q->length >= 2 &&
7365 GSF_LE_GET_GUINT16 (q->data));
7366 d (0, g_printerr ("Double stream file : %d\n",
7367 dsf););
7368 if (dsf)
7369 *is_double_stream_file = TRUE;
7370 break;
7373 case BIFF_XL9FILE:
7374 d (0, g_printerr ("%s\n", "XL 2000 file"););
7375 break;
7377 case BIFF_RECALCID: excel_read_RECALCID (q, importer); break;
7378 case BIFF_UNCALCED: excel_read_UNCALCED (q, importer); break;
7379 case BIFF_REFRESHALL: break;
7380 case BIFF_CODENAME: excel_read_CODENAME (q, importer, NULL); break;
7381 case BIFF_PROT4REVPASS: break;
7383 case BIFF_USESELFS: break;
7384 case BIFF_TABID: break;
7385 case BIFF_PROT4REV:
7386 break;
7389 case BIFF_SUPBOOK: excel_read_SUPBOOK (q, importer); break;
7391 case BIFF_SXStreamID: xls_read_SXStreamID (importer, q, gsf_input_container (input)); break;
7393 default:
7394 excel_unexpected_biff (q, "Workbook", ms_excel_read_debug);
7395 break;
7397 /* check here in case any of the handlers read additional records */
7398 prev_was_eof = (q->opcode == BIFF_EOF);
7401 ms_biff_query_destroy (q);
7402 if (ver)
7403 ms_biff_bof_data_destroy (ver);
7404 go_io_progress_unset (context);
7406 d (1, g_printerr ("finished read\n"););
7408 gnm_xl_importer_free (importer);
7410 /* If we were forced to stop then the load failed */
7411 if (problem_loading != NULL)
7412 go_cmd_context_error_import (GO_CMD_CONTEXT (context), problem_loading);
7416 static GSList *formats;
7419 * These are special in that they appear to be saved as macros.
7420 * Excel will only recognize them when saved as macros: neither
7421 * externname as "IFERROR" nor "_xlfn.IFERROR" will work.
7423 static const ExcelFuncDesc excel97_func_desc[] = {
7424 { 0xff, "_xlfn.AVERAGEIF", -1, -1, XL_XLM },
7425 { 0xff, "_xlfn.AVERAGEIFS", -1, -1, XL_XLM },
7426 { 0xff, "_xlfn.CUBEKPIMEMBER", -1, -1, XL_XLM },
7427 { 0xff, "_xlfn.CUBEMEMBER", -1, -1, XL_XLM },
7428 { 0xff, "_xlfn.CUBEMEMBERPROPERTY", -1, -1, XL_XLM },
7429 { 0xff, "_xlfn.CUBERANKEDMEMBER", -1, -1, XL_XLM },
7430 { 0xff, "_xlfn.CUBESET", -1, -1, XL_XLM },
7431 { 0xff, "_xlfn.CUBESETCOUNT", -1, -1, XL_XLM },
7432 { 0xff, "_xlfn.CUBEVALUE", -1, -1, XL_XLM },
7433 { 0xff, "_xlfn.COUNTIFS", -1, -1, XL_XLM },
7434 { 0xff, "_xlfn.IFERROR", 2, 2, XL_XLM, 2, 'V', "VV" },
7435 { 0xff, "_xlfn.SUMIFS", -1, -1, XL_XLM }
7438 void
7439 excel_read_init (void)
7441 int i;
7442 int mbd = go_locale_month_before_day ();
7443 GOFormat *fmt;
7445 fmt = go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATE);
7446 formats = g_slist_prepend (formats, fmt);
7447 excel_builtin_formats[0x0e] = go_format_as_XL (fmt);
7449 fmt = go_format_new_magic (GO_FORMAT_MAGIC_MEDIUM_DATE);
7450 formats = g_slist_prepend (formats, fmt);
7451 excel_builtin_formats[0x0f] = go_format_as_XL (fmt);
7453 /* Doesn't seem to have a name. */
7454 excel_builtin_formats[0x10] = mbd ? "d-mmm" : "mmm-d";
7456 fmt = go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATETIME);
7457 formats = g_slist_prepend (formats, fmt);
7458 excel_builtin_formats[0x16] = go_format_as_XL (fmt);
7460 excel_func_by_name = g_hash_table_new (g_str_hash, g_str_equal);
7461 for (i = 0; i < excel_func_desc_size; i++) {
7462 const ExcelFuncDesc *efd = excel_func_desc + i;
7463 const char *name = efd->name;
7464 GnmFunc *func = gnm_func_lookup (name, NULL);
7466 /* Fix case. */
7467 if (func)
7468 name = gnm_func_get_name (func, FALSE);
7470 g_assert (g_hash_table_lookup (excel_func_by_name, name) ==
7471 NULL);
7472 g_hash_table_insert (excel_func_by_name,
7473 (gpointer)name,
7474 (gpointer)efd);
7477 for (i = 0; i < (int)G_N_ELEMENTS(excel97_func_desc); i++) {
7478 const ExcelFuncDesc *efd = excel97_func_desc + i;
7479 const char *excel_name = efd->name;
7480 const char *gnm_name = strchr (excel_name, '.') + 1;
7481 GnmFunc *func = gnm_func_lookup (gnm_name, NULL);
7483 /* Fix case. */
7484 if (func)
7485 gnm_name = gnm_func_get_name (func, FALSE);
7487 g_assert (g_hash_table_lookup (excel_func_by_name, gnm_name) ==
7488 NULL);
7489 g_hash_table_insert (excel_func_by_name,
7490 (gpointer)gnm_name,
7491 (gpointer)efd);
7494 empty_attr_list = pango_attr_list_new ();
7497 void
7498 excel_read_cleanup (void)
7500 g_hash_table_destroy (excel_func_by_name);
7501 excel_func_by_name = NULL;
7503 g_slist_free_full (formats, (GDestroyNotify)go_format_unref);
7504 formats = NULL;
7506 pango_attr_list_unref (empty_attr_list);
7507 empty_attr_list = NULL;