Introspection: updates.
[gnumeric.git] / plugins / excel / ms-excel-read.c
blobf802fdd92b23483a58ef706599aa85505525da8f
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /**
3 * ms-excel-read.c: MS Excel import
5 * Authors:
6 * Jody Goldberg (jody@gnome.org)
7 * Michael Meeks (michael@ximian.com)
9 * (C) 1998-2001 Michael Meeks
10 * (C) 2002-2008 Jody Goldberg
11 * (C) 2013-2013 Morten Welinder
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) version 3.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
26 * USA
27 **/
28 #include <gnumeric-config.h>
29 #include <gnumeric.h>
31 #include "boot.h"
32 #include "ms-formula-read.h"
33 #include "ms-excel-read.h"
34 #include "ms-obj.h"
35 #include "ms-chart.h"
36 #include "ms-escher.h"
37 #include "ms-excel-util.h"
38 #include "ms-excel-xf.h"
39 #include "formula-types.h"
41 #include <workbook.h>
42 #include <workbook-view.h>
43 #include <sheet.h>
44 #include <sheet-view.h>
45 #include <sheet-style.h>
46 #include <sheet-merge.h>
47 #include <sheet-filter.h>
48 #include <cell.h>
49 #include <func.h>
50 #include <style.h>
51 #include <style-conditions.h>
52 #include "style-font.h"
53 #include <gnm-format.h>
54 #include <print-info.h>
55 #include <selection.h>
56 #include <validation.h>
57 #include <input-msg.h>
58 #include <parse-util.h> /* for cell_name */
59 #include <ranges.h>
60 #include <expr.h>
61 #include <expr-name.h>
62 #include <value.h>
63 #include <hlink.h>
64 #include <application.h>
65 #include <command-context.h>
66 #include <sheet-object-cell-comment.h>
67 #include <sheet-object-widget.h>
68 #include <gnm-so-line.h>
69 #include <gnm-so-filled.h>
70 #include <gnm-so-polygon.h>
71 #include <sheet-object-graph.h>
72 #include <sheet-object-image.h>
73 #include <goffice/goffice.h>
75 #include <gsf/gsf-input.h>
76 #include <gsf/gsf-utils.h>
77 #include <gsf/gsf-msole-utils.h>
78 #include <glib/gi18n-lib.h>
79 #include <glib/gstdio.h>
80 #include <string.h>
81 #include <locale.h>
83 #undef G_LOG_DOMAIN
84 #define G_LOG_DOMAIN "gnumeric:read"
86 typedef struct {
87 ExcelReadSheet *esheet;
88 char *name;
89 guint32 streamStartPos;
90 unsigned index;
91 MsBiffFileType type;
92 GnmSheetType gnm_type;
93 GnmSheetVisibility visibility;
94 } BiffBoundsheetData;
96 #define N_BYTES_BETWEEN_PROGRESS_UPDATES 0x1000
97 #define BMP_HDR_SIZE 14
100 * Check whether the product of the first two arguments exceeds
101 * the third. The function should be overflow-proof.
103 static gboolean
104 product_gt (size_t count, size_t itemsize, size_t space)
106 return itemsize > 0 &&
107 (count > G_MAXUINT / itemsize || count * itemsize > space);
110 static void
111 record_size_barf (size_t count, size_t itemsize, size_t space,
112 const char *locus)
114 g_warning ("File is most likely corrupted.\n"
115 "(Requested %u*%u bytes, but only %u bytes left in record.\n"
116 "The problem occurred in %s.)",
117 (unsigned)count, (unsigned)itemsize,
118 (unsigned)space,
119 locus);
122 #define XL_NEED_BYTES(count) XL_NEED_ITEMS(count,1)
124 #define XL_NEED_ITEMS(count__,size__) \
125 do { \
126 size_t count_ = (count__); \
127 size_t size_ = (size__); \
128 size_t space_ = q->length - (data - q->data); \
129 if (G_UNLIKELY (product_gt (count_, size_, space_))) { \
130 record_size_barf (count_, size_, space_, G_STRFUNC); \
131 return; \
133 } while (0)
136 /* #define NO_DEBUG_EXCEL */
137 #ifndef NO_DEBUG_EXCEL
138 #define d(level, code) do { if (ms_excel_read_debug > level) { code } } while (0)
139 #else
140 #define d(level, code)
141 #endif
143 #define XL_GETROW(p) (GSF_LE_GET_GUINT16(p->data + 0))
144 #define XL_GETCOL(p) (GSF_LE_GET_GUINT16(p->data + 2))
146 char const *excel_builtin_formats[EXCEL_BUILTIN_FORMAT_LEN] = {
147 /* 0x00 */ "General",
148 /* 0x01 */ "0",
149 /* 0x02 */ "0.00",
150 /* 0x03 */ "#,##0",
151 /* 0x04 */ "#,##0.00",
152 /* 0x05 */ "$#,##0_);($#,##0)",
153 /* 0x06 */ "$#,##0_);[Red]($#,##0)",
154 /* 0x07 */ "$#,##0.00_);($#,##0.00)",
155 /* 0x08 */ "$#,##0.00_);[Red]($#,##0.00)",
156 /* 0x09 */ "0%",
157 /* 0x0a */ "0.00%",
158 /* 0x0b */ "0.00E+00",
159 /* 0x0c */ "# ?/?",
160 /* 0x0d */ "# ?" "?/?" "?", /* Don't accidentally use trigraph. */
161 /* 0x0e */ "m/d/yy",
162 /* 0x0f */ "d-mmm-yy",
163 /* 0x10 */ "d-mmm",
164 /* 0x11 */ "mmm-yy",
165 /* 0x12 */ "h:mm AM/PM",
166 /* 0x13 */ "h:mm:ss AM/PM",
167 /* 0x14 */ "h:mm",
168 /* 0x15 */ "h:mm:ss",
169 /* 0x16 */ "m/d/yy h:mm",
170 /* 0x17 */ NULL, /* 0x17-0x24 reserved for intl versions */
171 /* 0x18 */ NULL,
172 /* 0x19 */ NULL,
173 /* 0x1a */ NULL,
174 /* 0x1b */ NULL,
175 /* 0x1c */ NULL,
176 /* 0x1d */ NULL,
177 /* 0x1e */ NULL,
178 /* 0x1f */ NULL,
179 /* 0x20 */ NULL,
180 /* 0x21 */ NULL,
181 /* 0x22 */ NULL,
182 /* 0x23 */ NULL,
183 /* 0x24 */ NULL,
184 /* 0x25 */ "#,##0_);(#,##0)",
185 /* 0x26 */ "#,##0_);[Red](#,##0)",
186 /* 0x27 */ "#,##0.00_);(#,##0.00)",
187 /* 0x28 */ "#,##0.00_);[Red](#,##0.00)",
188 /* 0x29 */ "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
189 /* 0x2a */ "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)",
190 /* 0x2b */ "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
191 /* 0x2c */ "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)",
192 /* 0x2d */ "mm:ss",
193 /* 0x2e */ "[h]:mm:ss",
194 /* 0x2f */ "mm:ss.0",
195 /* 0x30 */ "##0.0E+0",
196 /* 0x31 */ "@"
199 static PangoAttrList *empty_attr_list;
201 static MsBiffVersion
202 esheet_ver (ExcelReadSheet const *esheet)
204 return esheet->container.importer->ver;
207 static void
208 gnm_xl_importer_set_version (GnmXLImporter *importer, MsBiffVersion ver)
210 g_return_if_fail (NULL != importer);
211 g_return_if_fail (MS_BIFF_V_UNKNOWN == importer->ver);
212 importer->ver = ver;
215 static void
216 gnm_xl_importer_set_codepage (GnmXLImporter *importer, int codepage)
218 GIConv str_iconv;
219 if (codepage == 1200 || codepage == 1201)
220 /* this is 'compressed' unicode. unicode characters 0000->00FF
221 * which looks the same as 8859-1. What does Little endian vs
222 * bigendian have to do with this. There is only 1 byte, and it would
223 * certainly not be useful to keep the low byte as 0.
225 str_iconv = g_iconv_open ("UTF-8", "ISO-8859-1");
226 else
227 str_iconv = gsf_msole_iconv_open_for_import (codepage);
229 if (str_iconv == (GIConv)(-1)) {
230 g_warning ("missing converter for codepage %u\n"
231 "falling back to 1252", codepage);
232 str_iconv = gsf_msole_iconv_open_for_import (1252);
235 if (importer->str_iconv != (GIConv)(-1))
236 gsf_iconv_close (importer->str_iconv);
237 importer->str_iconv = str_iconv;
239 /* Store the codepage to make export easier, might
240 * cause problems with double stream files because
241 * we'll lose the codepage in the biff8 version */
242 g_object_set_data (G_OBJECT (importer->wb), "excel-codepage",
243 GINT_TO_POINTER (codepage));
245 d (0, g_printerr ("%s\n", gsf_msole_language_for_lid (
246 gsf_msole_codepage_to_lid (codepage))););
249 static GOFormat *
250 excel_wb_get_fmt (GnmXLImporter *importer, unsigned idx)
252 char const *ans = NULL;
253 BiffFormatData const *d = g_hash_table_lookup (importer->format_table,
254 GUINT_TO_POINTER (idx));
256 if (d)
257 ans = d->name;
258 else if (idx <= 0x31) {
259 ans = excel_builtin_formats[idx];
260 if (!ans)
261 g_printerr ("Foreign undocumented format\n");
262 } else
263 g_printerr ("Unknown format: 0x%x\n", idx);
265 if (ans) {
266 GOFormat *fmt = gnm_format_import
267 (ans,
268 GNM_FORMAT_IMPORT_NULL_INVALID |
269 GNM_FORMAT_IMPORT_PATCHUP_INCOMPLETE);
270 if (!fmt) {
271 g_warning ("Ignoring invalid format [%s]", ans);
272 fmt = go_format_general ();
273 go_format_ref (fmt);
276 return fmt;
277 } else
278 return NULL;
281 static GnmCell *
282 excel_cell_fetch (BiffQuery *q, ExcelReadSheet *esheet)
284 guint16 col, row;
285 Sheet *sheet = esheet->sheet;
287 XL_CHECK_CONDITION_VAL (q->length >= 4, NULL);
289 col = XL_GETCOL (q);
290 row = XL_GETROW (q);
292 XL_CHECK_CONDITION_VAL (col < gnm_sheet_get_max_cols (sheet), NULL);
293 XL_CHECK_CONDITION_VAL (row < gnm_sheet_get_max_rows (sheet), NULL);
295 return sheet_cell_fetch (sheet, col, row);
298 static GnmExprTop const *
299 ms_sheet_parse_expr_internal (ExcelReadSheet *esheet, guint8 const *data, int length)
301 GnmExprTop const *texpr;
303 g_return_val_if_fail (length > 0, NULL);
305 texpr = excel_parse_formula (&esheet->container, esheet, 0, 0,
306 data, length, 0 /* FIXME */,
307 FALSE, NULL);
308 if (ms_excel_read_debug > 8) {
309 char *tmp;
310 GnmParsePos pp;
311 Sheet *sheet = esheet->sheet;
312 Workbook *wb = (sheet == NULL) ? esheet->container.importer->wb : NULL;
314 tmp = gnm_expr_top_as_string (texpr,
315 parse_pos_init (&pp, wb, sheet, 0, 0),
316 gnm_conventions_default);
317 g_printerr ("%s\n", tmp ? tmp : "(null)");
318 g_free (tmp);
321 return texpr;
324 static GnmExprTop const *
325 ms_sheet_parse_expr (MSContainer *container, guint8 const *data, int length)
327 return ms_sheet_parse_expr_internal ((ExcelReadSheet *)container,
328 data, length);
331 static Sheet *
332 ms_sheet_get_sheet (MSContainer const *container)
334 return ((ExcelReadSheet const *)container)->sheet;
337 static GOFormat *
338 ms_sheet_get_fmt (MSContainer const *container, unsigned indx)
340 return excel_wb_get_fmt (container->importer, indx);
343 static GOColor
344 ms_sheet_map_color (ExcelReadSheet const *esheet, MSObj const *obj, MSObjAttrID id,
345 GOColor default_val, gboolean *pauto)
347 gushort r, g, b;
348 MSObjAttr *attr = ms_obj_attr_bag_lookup (obj->attrs, id);
350 if (attr == NULL) {
351 if (pauto) *pauto = TRUE;
352 return default_val;
355 if ((~0x7ffffff) & attr->v.v_uint) {
356 GnmColor *c = excel_palette_get (esheet->container.importer,
357 (0x7ffffff & attr->v.v_uint));
359 r = GO_COLOR_UINT_R (c->go_color);
360 g = GO_COLOR_UINT_G (c->go_color);
361 b = GO_COLOR_UINT_B (c->go_color);
362 style_color_unref (c);
363 } else {
364 r = (attr->v.v_uint) & 0xff;
365 g = (attr->v.v_uint >> 8) & 0xff;
366 b = (attr->v.v_uint >> 16) & 0xff;
369 if (pauto) *pauto = FALSE;
371 return GO_COLOR_FROM_RGBA (r,g,b,0xff);
375 * ms_sheet_obj_anchor_to_pos:
376 * @points Array which receives anchor coordinates in points
377 * @obj The object
378 * @sheet The sheet
380 * Converts anchor coordinates in Excel units to points. Anchor
381 * coordinates are x and y of upper left and lower right corner. Each
382 * is expressed as a pair: Row/cell number + position within cell as
383 * fraction of cell dimension.
385 * NOTE: According to docs, position within cell is expressed as
386 * 1/1024 of cell dimension. However, this doesn't seem to be true
387 * vertically, for Excel 97. We use 256 for >= XL97 and 1024 for
388 * preceding.
390 static gboolean
391 ms_sheet_obj_anchor_to_pos (Sheet const * sheet,
392 G_GNUC_UNUSED MsBiffVersion const ver,
393 guint8 const *raw_anchor,
394 GnmRange *range, double offset[4], GnmSOAnchorMode *mode)
396 /* NOTE :
397 * gnm_float const row_denominator = (ver >= MS_BIFF_V8) ? 256. : 1024.;
398 * damn damn damn
399 * chap03-1.xls suggests that XL95 uses 256 too
400 * Do we have any tests that confirm the docs contention of 1024 ?
402 int i;
404 d (0,
406 g_printerr ("anchored to %s\n", sheet->name_unquoted);
407 gsf_mem_dump (raw_anchor, 18);
410 switch (raw_anchor[0]) {
411 case 2:
412 *mode = GNM_SO_ANCHOR_ONE_CELL;
413 break;
414 case 3:
415 *mode = GNM_SO_ANCHOR_ABSOLUTE;
416 break;
417 default:
418 *mode = GNM_SO_ANCHOR_TWO_CELLS;
419 break;
421 /* Ignore the first 2 bytes. What are they ? */
422 /* Dec/1/2000 JEG: I have not researched it, but this may have some
423 * flags indicating whether or not the object is anchored to the cell
425 raw_anchor += 2;
427 /* Words 0, 4, 8, 12: The row/col of the corners */
428 /* Words 2, 6, 10, 14: distance from cell edge */
429 for (i = 0; i < 4; i++, raw_anchor += 4) {
430 int const pos = GSF_LE_GET_GUINT16 (raw_anchor);
431 int const nths = GSF_LE_GET_GUINT16 (raw_anchor + 2);
433 d (2, {
434 g_printerr ("%d/%d cell %s from ",
435 nths, (i & 1) ? 256 : 1024,
436 (i & 1) ? "widths" : "heights");
437 if (i & 1)
438 g_printerr ("row %d;\n", pos + 1);
439 else
440 g_printerr ("col %s (%d);\n", col_name (pos), pos);
443 if (i & 1) { /* odds are rows */
444 offset[i] = nths / 256.;
445 if (i == 1)
446 range->start.row = pos;
447 else
448 range->end.row = pos;
449 } else {
450 offset[i] = nths / 1024.;
451 if (i == 0)
452 range->start.col = pos;
453 else
454 range->end.col = pos;
458 return FALSE;
461 static void
462 handle_arrow_head (SheetObject *so, const char *prop_name,
463 double width,
464 MSObjAttrBag *attrs, MSObjAttrID typid,
465 MSObjAttrID wid, MSObjAttrID lid)
467 GOArrow arrow;
468 int w = ms_obj_attr_get_int (attrs, wid, 1);
469 int l = ms_obj_attr_get_int (attrs, lid, 1);
470 int typ = ms_obj_attr_get_int (attrs, typid, 0);
471 xls_arrow_from_xl (&arrow, width, typ, l, w);
472 g_object_set (so, prop_name, &arrow, NULL);
475 static void
476 excel_fill_bmp_header(guint8 *bmphdr, guint8 *data, guint32 len)
478 guint bpp;
479 guint offset;
481 bmphdr[0] = 'B';
482 bmphdr[1] = 'M';
483 GSF_LE_SET_GUINT32 (bmphdr + 2, len + BMP_HDR_SIZE);
484 GSF_LE_SET_GUINT16 (bmphdr + 6, 0);
485 GSF_LE_SET_GUINT16 (bmphdr + 8, 0);
486 bpp = len >= 20 ? GSF_LE_GET_GUINT16 (data + 18) : 1;
487 switch (bpp) {
488 case 24: offset = 0; break;
489 case 8: offset = 256 * 3; break;
490 case 4: offset = 16 * 3; break;
491 default: offset = 2 * 3; break;
493 offset += BMP_HDR_SIZE + 2;
494 GSF_LE_SET_GUINT32 (bmphdr + 10, offset);
497 static gboolean
498 ms_sheet_realize_obj (MSContainer *container, MSObj *obj)
500 double offsets[4];
501 PangoAttrList *markup;
502 GnmRange range;
503 ExcelReadSheet *esheet;
504 MSObjAttr *attr, *flip_h, *flip_v;
505 GODrawingAnchorDir direction;
506 SheetObjectAnchor anchor;
507 SheetObject *so;
508 GOStyle *style;
509 GnmSOAnchorMode mode = GNM_SO_ANCHOR_TWO_CELLS;
511 if (obj == NULL)
512 return TRUE;
513 if (obj->gnum_obj == NULL)
514 return FALSE;
515 so = obj->gnum_obj;
517 g_return_val_if_fail (container != NULL, TRUE);
518 esheet = (ExcelReadSheet *)container;
520 /* our comment object is too weak. This anchor is for the text box,
521 * we need to store the indicator */
522 if (obj->excel_type == 0x19 &&
523 obj->comment_pos.col >= 0 && obj->comment_pos.row >= 0) {
524 cell_comment_set_pos (GNM_CELL_COMMENT (obj->gnum_obj),
525 &obj->comment_pos);
526 } else {
527 attr = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_ANCHOR);
528 if (attr == NULL) {
529 g_printerr ("MISSING anchor for obj %p with id %d of type %s\n", (void *)obj, obj->id, obj->excel_type_name);
530 return TRUE;
533 if (ms_sheet_obj_anchor_to_pos (esheet->sheet, container->importer->ver,
534 attr->v.v_ptr, &range, offsets, &mode))
535 return TRUE;
537 flip_h = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_FLIP_H);
538 flip_v = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_FLIP_V);
539 direction =
540 ((flip_h == NULL) ? GOD_ANCHOR_DIR_RIGHT : 0) |
541 ((flip_v == NULL) ? GOD_ANCHOR_DIR_DOWN : 0);
543 sheet_object_anchor_init (&anchor, &range, offsets, direction, GNM_SO_ANCHOR_TWO_CELLS);
544 sheet_object_set_anchor (so, &anchor);
546 sheet_object_set_sheet (so, esheet->sheet);
547 if (mode != GNM_SO_ANCHOR_TWO_CELLS)
548 sheet_object_set_anchor_mode (so, &mode);
551 gpointer label;
552 if (ms_obj_attr_get_ptr (obj->attrs, MS_OBJ_ATTR_TEXT, &label, FALSE))
553 g_object_set (G_OBJECT (so), "text", label, NULL);
557 gpointer name;
558 if (ms_obj_attr_get_ptr (obj->attrs, MS_OBJ_ATTR_OBJ_NAME, &name, FALSE))
559 g_object_set (G_OBJECT (so), "name", name, NULL);
562 markup = ms_obj_attr_get_markup (obj->attrs, MS_OBJ_ATTR_MARKUP, NULL, FALSE);
563 if (markup != NULL)
564 g_object_set (so, "markup", markup, NULL);
566 switch (obj->excel_type) {
567 case 0x00:
568 break;
570 case MSOT_LINE:
571 case MSOT_ARC: {
572 double width;
573 style = go_style_new ();
574 style->line.color = ms_sheet_map_color
575 (esheet, obj, MS_OBJ_ATTR_OUTLINE_COLOR,
576 GO_COLOR_BLACK, &style->line.auto_color);
577 style->line.width = ms_obj_attr_get_uint (obj->attrs,
578 MS_OBJ_ATTR_OUTLINE_WIDTH, 0) / 256.;
579 style->line.auto_dash =
580 (ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_OUTLINE_HIDE) != NULL);
581 style->line.dash_type = style->line.auto_dash
582 ? GO_LINE_NONE
583 : ms_escher_xl_to_line_type (ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_OUTLINE_STYLE, 0));
585 width = style->line.auto_width ? 0 : style->line.width;
586 g_object_set (G_OBJECT (so), "style", style, NULL);
587 g_object_unref (style);
589 handle_arrow_head (so, "start-arrow", width,
590 obj->attrs,
591 MS_OBJ_ATTR_ARROW_START,
592 MS_OBJ_ATTR_ARROW_START_WIDTH,
593 MS_OBJ_ATTR_ARROW_START_LENGTH);
594 handle_arrow_head (so, "end-arrow", width,
595 obj->attrs,
596 MS_OBJ_ATTR_ARROW_END,
597 MS_OBJ_ATTR_ARROW_END_WIDTH,
598 MS_OBJ_ATTR_ARROW_END_LENGTH);
599 break;
602 case MSOT_POLYGON:
603 g_object_set (G_OBJECT (so), "points",
604 ms_obj_attr_get_array (obj->attrs, MS_OBJ_ATTR_POLYGON_COORDS, NULL, TRUE),
605 NULL);
606 /* fallthrough */
608 case MSOT_RECTANGLE:
609 case MSOT_OVAL:
610 case MSOT_TEXTBOX:
611 case MSOT_LABEL:
612 style = go_style_new ();
613 style->line.color = ms_sheet_map_color
614 (esheet, obj, MS_OBJ_ATTR_OUTLINE_COLOR,
615 GO_COLOR_BLACK, &style->line.auto_color);
616 style->line.width = ms_obj_attr_get_uint (obj->attrs,
617 MS_OBJ_ATTR_OUTLINE_WIDTH, 0) / 256.;
618 style->line.auto_dash = FALSE;
619 style->line.dash_type = (ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_OUTLINE_HIDE) != NULL)
620 ? GO_LINE_NONE
621 : ms_escher_xl_to_line_type (ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_OUTLINE_STYLE, 0));
622 style->fill.pattern.back = ms_sheet_map_color
623 (esheet, obj, MS_OBJ_ATTR_FILL_COLOR,
624 GO_COLOR_WHITE, &style->fill.auto_back);
625 style->fill.pattern.fore = ms_sheet_map_color
626 (esheet, obj, MS_OBJ_ATTR_FILL_BACKGROUND,
627 GO_COLOR_BLACK, &style->fill.auto_fore);
628 /* fill type needs work, we now suppot more than solid color */
629 style->fill.type = ms_obj_attr_bag_lookup (obj->attrs, MS_OBJ_ATTR_UNFILLED)
630 ? GO_STYLE_FILL_NONE : GO_STYLE_FILL_PATTERN;
631 if (style->fill.type != GO_STYLE_FILL_PATTERN)
632 style->fill.auto_type = FALSE;
634 g_object_set (G_OBJECT (so), "style", style, NULL);
635 g_object_unref (style);
636 break;
638 case MSOT_CHART:
639 /* NOTE : We should not need to do anything for charts */
640 break;
642 case MSOT_BUTTON:
643 break;
645 case MSOT_PICTURE: {
646 double crop_left = 0.0;
647 double crop_top = 0.0;
648 double crop_right = 0.0;
649 double crop_bottom = 0.0;
651 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
652 MS_OBJ_ATTR_BLIP_ID)) != NULL) {
653 MSEscherBlip *blip = ms_container_get_blip (container,
654 attr->v.v_uint - 1);
655 if (blip != NULL) {
656 if (blip->type && !strcmp (blip->type, "dib")) {
657 guint8 *data = g_malloc(blip->data_len + BMP_HDR_SIZE);
658 if (data) {
659 excel_fill_bmp_header(data, blip->data, blip->data_len);
660 memcpy(data + BMP_HDR_SIZE, blip->data, blip->data_len);
661 sheet_object_image_set_image (GNM_SO_IMAGE (so),
662 blip->type, data, blip->data_len + BMP_HDR_SIZE);
663 g_free (data);
665 } else {
666 sheet_object_image_set_image (GNM_SO_IMAGE (so),
667 blip->type, blip->data, blip->data_len);
670 } else if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
671 MS_OBJ_ATTR_IMDATA)) != NULL) {
672 GdkPixbuf *pixbuf = GDK_PIXBUF (attr->v.v_object);
674 if (pixbuf) {
675 gchar *buf = NULL;
676 gsize buf_size;
678 gdk_pixbuf_save_to_buffer
679 (pixbuf, &buf, &buf_size, "png",
680 NULL, NULL);
681 if (buf_size > 0) {
682 sheet_object_image_set_image
683 (GNM_SO_IMAGE (so),
684 "png", buf, buf_size);
686 g_free (buf);
689 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
690 MS_OBJ_ATTR_BLIP_CROP_LEFT)) != NULL)
691 crop_left = (double) attr->v.v_uint / 65536.;
692 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
693 MS_OBJ_ATTR_BLIP_CROP_RIGHT)) != NULL)
694 crop_right = (double) attr->v.v_uint / 65536.;
695 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
696 MS_OBJ_ATTR_BLIP_CROP_TOP)) != NULL)
697 crop_top = (double) attr->v.v_uint / 65536.;
698 if ((attr = ms_obj_attr_bag_lookup (obj->attrs,
699 MS_OBJ_ATTR_BLIP_CROP_BOTTOM)) != NULL)
700 crop_bottom = (double) attr->v.v_uint / 65536.;
702 sheet_object_image_set_crop (GNM_SO_IMAGE (so),
703 crop_left, crop_top, crop_right, crop_bottom);
704 break;
707 case MSOT_CHECKBOX:
708 case MSOT_TOGGLE:
709 sheet_widget_checkbox_set_link (obj->gnum_obj,
710 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE));
711 break;
713 case MSOT_OPTION:
714 sheet_widget_radio_button_set_link (obj->gnum_obj,
715 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE));
716 break;
718 case MSOT_SPINNER:
719 case MSOT_SCROLLBAR:
720 sheet_widget_adjustment_set_details (obj->gnum_obj,
721 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE),
722 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_VALUE, 0),
723 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_MIN, 0),
724 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_MAX, 100) - 1,
725 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_INC, 1),
726 ms_obj_attr_get_int (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_PAGE, 10));
727 sheet_widget_adjustment_set_horizontal (obj->gnum_obj,
728 ms_obj_attr_get_uint (obj->attrs, MS_OBJ_ATTR_SCROLLBAR_HORIZ, FALSE));
729 break;
731 case MSOT_LIST:
732 case MSOT_COMBO:
733 sheet_widget_list_base_set_links (obj->gnum_obj,
734 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_LINKED_TO_CELL, NULL, FALSE),
735 ms_obj_attr_get_expr (obj->attrs, MS_OBJ_ATTR_INPUT_FROM, NULL, FALSE));
736 break;
738 case MSOT_COMMENT: /* cell comment text box */
739 break;
741 default:
742 d (2, g_printerr ("EXCEL: unhandled excel object of type %s (0x%x) id = %d.",
743 obj->excel_type_name, obj->excel_type, obj->id););
744 return TRUE;
747 return FALSE;
750 static SheetObject *
751 ms_sheet_create_obj (MSContainer *container, MSObj *obj)
753 SheetObject *so = NULL;
755 if (obj == NULL)
756 return NULL;
758 g_return_val_if_fail (container != NULL, NULL);
760 switch (obj->excel_type) {
761 case MSOT_LINE:
762 case MSOT_ARC:
763 so = g_object_new (GNM_SO_LINE_TYPE, NULL);
764 break;
766 case 0x00: /* draw the group border */
767 case MSOT_RECTANGLE:
768 case MSOT_OVAL:
769 case MSOT_TEXTBOX:
770 case MSOT_LABEL:
771 so = g_object_new (GNM_SO_FILLED_TYPE,
772 "is-oval", obj->excel_type == 3,
773 NULL);
774 break;
776 case MSOT_CHART:
777 so = sheet_object_graph_new (NULL);
778 break;
780 /* Button */
781 case MSOT_BUTTON:
782 so = g_object_new (sheet_widget_button_get_type (), NULL);
783 break;
784 case MSOT_PICTURE:
785 so = g_object_new (GNM_SO_IMAGE_TYPE, NULL); /* Picture */
786 break;
787 case MSOT_POLYGON:
788 so = g_object_new (GNM_SO_POLYGON_TYPE, NULL);
789 break;
790 case MSOT_CHECKBOX:
791 so = g_object_new (sheet_widget_checkbox_get_type (), NULL);
792 break;
793 case MSOT_OPTION:
794 so = g_object_new (sheet_widget_radio_button_get_type (), NULL);
795 break;
796 case MSOT_SPINNER:
797 so = g_object_new (sheet_widget_spinbutton_get_type (), NULL);
798 break;
799 case MSOT_SCROLLBAR:
800 so = g_object_new (sheet_widget_scrollbar_get_type (), NULL);
801 break;
802 case MSOT_LIST:
803 so = g_object_new (sheet_widget_list_get_type (), NULL);
804 break;
806 /* ignore combos associateed with filters */
807 case MSOT_COMBO: {
808 ExcelReadSheet *esheet = (ExcelReadSheet *)container;
810 if (!obj->auto_combo)
811 so = g_object_new (sheet_widget_combo_get_type (), NULL);
813 /* ok, there are combos to go with the autofilter it can stay */
814 else if (esheet != NULL)
815 esheet->filter = NULL;
817 break;
819 case MSOT_COMMENT:
820 so = g_object_new (cell_comment_get_type (), NULL);
821 break;
823 /* Gnumeric specific addition to handle toggle button controls */
824 case MSOT_TOGGLE:
825 so = g_object_new (sheet_widget_toggle_button_get_type (), NULL);
826 break;
828 default:
829 g_warning ("EXCEL: unhandled excel object of type %s (0x%x) id = %d.",
830 obj->excel_type_name, obj->excel_type, obj->id);
831 return NULL;
834 return so;
838 * excel_init_margins
839 * @esheet ExcelReadSheet
841 * Excel only saves margins when any of the margins differs from the
842 * default. So we must initialize the margins to Excel's defaults, which
843 * are:
844 * Top, bottom: 1 in - 72 pt
845 * Left, right: 3/4 in - 48 pt
846 * Header, footer: 1/2 in - 36 pt
848 static void
849 excel_init_margins (ExcelReadSheet *esheet)
851 GnmPrintInformation *pi;
852 double points;
853 double short_points;
855 g_return_if_fail (esheet != NULL);
856 g_return_if_fail (esheet->sheet != NULL);
857 g_return_if_fail (esheet->sheet->print_info != NULL);
859 pi = esheet->sheet->print_info;
860 print_info_set_edge_to_below_header (pi,GO_IN_TO_PT (1.0));
861 print_info_set_edge_to_above_footer (pi,GO_IN_TO_PT (1.0));
863 points = GO_IN_TO_PT (0.75);
864 short_points = GO_IN_TO_PT (0.5);
865 print_info_set_margins (pi, short_points, short_points, points, points);
868 static void
869 excel_shared_formula_free (XLSharedFormula *sf)
871 if (sf != NULL) {
872 g_free (sf->data);
873 g_free (sf);
877 static ExcelReadSheet *
878 excel_sheet_new (GnmXLImporter *importer, char const *sheet_name, GnmSheetType type)
880 static MSContainerClass const vtbl = {
881 &ms_sheet_realize_obj,
882 &ms_sheet_create_obj,
883 &ms_sheet_parse_expr,
884 &ms_sheet_get_sheet,
885 &ms_sheet_get_fmt,
886 NULL
888 int rows = (importer->ver >= MS_BIFF_V8 ? XLS_MaxRow_V8 : XLS_MaxRow_V7);
889 ExcelReadSheet *esheet;
890 Sheet *sheet;
892 sheet = workbook_sheet_by_name (importer->wb, sheet_name);
893 if (sheet) {
894 unsigned ui;
896 for (ui = 0; ui < importer->excel_sheets->len; ui++) {
897 ExcelReadSheet *es = g_ptr_array_index (importer->excel_sheets, ui);
898 if (es->sheet == sheet) {
899 g_warning ("Duplicate definition of sheet %s\n", sheet_name);
900 return NULL;
903 } else {
904 sheet = sheet_new_with_type (importer->wb, sheet_name, type,
905 XLS_MaxCol, rows);
906 workbook_sheet_attach (importer->wb, sheet);
907 d (1, g_printerr ("Adding sheet '%s'\n", sheet_name););
910 /* Flag a respan here in case nothing else does */
911 sheet_flag_recompute_spans (sheet);
913 esheet = g_new (ExcelReadSheet, 1);
914 esheet->sheet = sheet;
915 esheet->filter = NULL;
916 esheet->freeze_panes = FALSE;
917 esheet->active_pane = 3; /* The default */
918 esheet->shared_formulae = g_hash_table_new_full
919 ((GHashFunc)&gnm_cellpos_hash,
920 (GCompareFunc)&gnm_cellpos_equal,
921 NULL, (GDestroyNotify) &excel_shared_formula_free);
922 esheet->tables = g_hash_table_new_full
923 ((GHashFunc)&gnm_cellpos_hash,
924 (GCompareFunc)&gnm_cellpos_equal,
925 NULL, (GDestroyNotify) g_free);
926 esheet->biff2_prev_xf_index = -1;
928 excel_init_margins (esheet);
929 ms_container_init (&esheet->container, &vtbl,
930 &importer->container, importer);
931 g_ptr_array_add (importer->excel_sheets, esheet);
933 return esheet;
936 void
937 excel_unexpected_biff (BiffQuery *q, char const *state,
938 int debug_level)
940 #ifndef NO_DEBUG_EXCEL
941 if (debug_level > 1) {
942 g_print ("Unexpected Opcode in %s: 0x%hx, length 0x%x\n",
943 state, q->opcode, q->length);
944 if (debug_level > 2)
945 gsf_mem_dump (q->data, q->length);
947 #endif
951 * excel_read_string_header :
952 * @data: a pointer to the start of the string header
953 * @maxlen: the length of the data area
954 * @use_utf16: Is the content in 8 or 16 bit chars
955 * @n_markup: number of trailing markup records
956 * @has_extended: Is there trailing extended string info (eg japanese PHONETIC)
957 * @post_data_len:
959 * returns the length of the header (in bytes)
961 static guint32
962 excel_read_string_header (guint8 const *data, guint32 maxlen,
963 gboolean *use_utf16,
964 unsigned *n_markup,
965 gboolean *has_extended,
966 unsigned *post_data_len)
968 guint8 header;
969 guint32 len;
971 if (G_UNLIKELY (maxlen < 1))
972 goto error;
974 header = GSF_LE_GET_GUINT8 (data);
975 if (((header & 0xf2) != 0))
976 goto error;
978 *use_utf16 = (header & 0x1) != 0;
980 if ((header & 0x8) != 0) {
981 if (G_UNLIKELY (maxlen < 3))
982 goto error;
983 *n_markup = GSF_LE_GET_GUINT16 (data + 1);
984 *post_data_len = *n_markup * 4; /* 4 bytes per */
985 len = 3;
986 } else {
987 *n_markup = 0;
988 *post_data_len = 0;
989 len = 1;
992 *has_extended = (header & 0x4) != 0;
993 if (*has_extended) {
994 guint32 len_ext_rst;
996 if (G_UNLIKELY (maxlen < len + 4))
997 goto error;
998 len_ext_rst = GSF_LE_GET_GUINT32 (data + len); /* A byte length */
999 *post_data_len += len_ext_rst;
1000 len += 4;
1002 d (4, g_printerr ("Extended string support unimplemented; "
1003 "ignoring %u bytes\n", len_ext_rst););
1006 return len;
1008 error:
1009 *use_utf16 = *has_extended = FALSE;
1010 *n_markup = 0;
1011 *post_data_len = 0;
1012 g_warning ("Invalid string record.");
1013 return 0;
1016 char *
1017 excel_get_chars (GnmXLImporter const *importer,
1018 guint8 const *ptr, size_t length, gboolean use_utf16, guint16 const *codepage)
1020 char* ans;
1021 size_t i;
1022 GIConv str_iconv = importer->str_iconv;
1024 if (use_utf16) {
1025 gunichar2 *uni_text = g_alloca (sizeof (gunichar2)*length);
1027 for (i = 0; i < length; i++, ptr += 2)
1028 uni_text [i] = GSF_LE_GET_GUINT16 (ptr);
1029 ans = g_utf16_to_utf8 (uni_text, length, NULL, NULL, NULL);
1030 } else {
1031 size_t outbytes = (length + 2) * 8;
1032 char *outbuf = g_new (char, outbytes + 1);
1033 char *ptr2 = (char *)ptr;
1035 ans = outbuf;
1036 if (NULL != codepage)
1037 str_iconv = gsf_msole_iconv_open_for_import (*codepage);
1038 g_iconv (str_iconv,
1039 &ptr2, &length, &outbuf, &outbytes);
1040 if (codepage)
1041 g_iconv_close (str_iconv);
1043 i = outbuf - ans;
1044 ans[i] = 0;
1045 ans = g_realloc (ans, i + 1);
1047 return ans;
1050 char *
1051 excel_get_text (GnmXLImporter const *importer,
1052 guint8 const *pos, guint32 length,
1053 guint32 *byte_length, guint16 const *codepage, guint32 maxlen)
1055 char *ans;
1056 guint8 const *ptr;
1057 unsigned byte_len, trailing_data_len, n_markup, str_len_bytes;
1058 gboolean use_utf16, has_extended;
1060 if (byte_length == NULL)
1061 byte_length = &byte_len;
1063 if (importer->ver >= MS_BIFF_V8) {
1064 *byte_length = 1; /* the header */
1065 if (length == 0)
1066 return NULL;
1067 ptr = pos + excel_read_string_header
1068 (pos, maxlen,
1069 &use_utf16, &n_markup, &has_extended,
1070 &trailing_data_len);
1071 *byte_length += trailing_data_len;
1072 } else {
1073 *byte_length = 0; /* no header */
1074 if (length == 0)
1075 return NULL;
1076 trailing_data_len = 0;
1077 use_utf16 = has_extended = FALSE;
1078 n_markup = 0;
1079 ptr = pos;
1082 str_len_bytes = (use_utf16 ? 2 : 1) * length;
1084 if (*byte_length > maxlen) {
1085 *byte_length = maxlen;
1086 length = 0;
1087 } else if (maxlen - *byte_length < str_len_bytes) {
1088 *byte_length = maxlen;
1089 length = (maxlen - *byte_length) / (use_utf16 ? 2 : 1);
1090 } else
1091 *byte_length += str_len_bytes;
1093 ans = excel_get_chars (importer, ptr, length, use_utf16, codepage);
1095 d (4, {
1096 g_printerr ("String len %d, byte length %d: %s %s %s:\n",
1097 length, *byte_length,
1098 (use_utf16 ? "UTF16" : "1byte"),
1099 ((n_markup > 0) ? "has markup" :""),
1100 (has_extended ? "has extended phonetic info" : ""));
1101 gsf_mem_dump (pos, *byte_length);
1104 return ans;
1107 static char *
1108 excel_biff_text (GnmXLImporter const *importer,
1109 const BiffQuery *q, guint32 ofs, guint32 length)
1111 XL_CHECK_CONDITION_VAL (q->length >= ofs, NULL);
1113 return excel_get_text (importer, q->data + ofs, length,
1114 NULL, NULL, q->length - ofs);
1117 char *
1118 excel_biff_text_1 (GnmXLImporter const *importer,
1119 const BiffQuery *q, guint32 ofs)
1121 guint32 length;
1123 XL_CHECK_CONDITION_VAL (q->length >= (ofs + 1), NULL);
1125 length = GSF_LE_GET_GUINT8 (q->data + ofs);
1126 ofs++;
1128 return excel_get_text (importer, q->data + ofs, length,
1129 NULL, NULL, q->length - ofs);
1132 char *
1133 excel_biff_text_2 (GnmXLImporter const *importer,
1134 const BiffQuery *q, guint32 ofs)
1136 guint32 length;
1138 XL_CHECK_CONDITION_VAL (q->length >= (ofs + 2), NULL);
1140 length = GSF_LE_GET_GUINT16 (q->data + ofs);
1141 ofs += 2;
1143 return excel_get_text (importer, q->data + ofs, length,
1144 NULL, NULL, q->length - ofs);
1147 typedef struct {
1148 unsigned first, last;
1149 PangoAttrList *accum;
1150 } TXORun;
1152 static gboolean
1153 append_markup (PangoAttribute *src, TXORun *run)
1155 if (run->last > run->first) {
1156 PangoAttribute *dst = pango_attribute_copy (src);
1157 dst->start_index = run->first; /* inclusive */
1158 dst->end_index = run->last; /* exclusive */
1159 pango_attr_list_change (run->accum, dst);
1161 return FALSE;
1164 static GOFormat *
1165 excel_read_LABEL_markup (BiffQuery *q, ExcelReadSheet *esheet,
1166 char const *str, unsigned str_len)
1168 guint8 const * const end = q->data + q->length;
1169 guint8 const *ptr = q->data + 8 + str_len;
1170 MSContainer const *c = &esheet->container;
1171 TXORun txo_run;
1172 unsigned n;
1173 unsigned int clen = g_utf8_strlen (str, -1);
1175 d (0, {
1176 g_printerr ("strlen=%d len=%d\n", str_len, (int)strlen (str));
1177 ms_biff_query_dump (q);
1180 txo_run.last = strlen (str);
1182 if (esheet_ver (esheet) >= MS_BIFF_V8) {
1183 XL_CHECK_CONDITION_VAL (ptr+2 <= end , NULL);
1184 n = 4 * GSF_LE_GET_GUINT16 (ptr);
1185 ptr += 2;
1187 XL_CHECK_CONDITION_VAL (ptr + n == end , NULL);
1189 txo_run.accum = pango_attr_list_new ();
1190 while (n > 0) {
1191 guint16 o,l;
1193 n -= 4;
1195 o = GSF_LE_GET_GUINT16 (ptr + n);
1196 l = GSF_LE_GET_GUINT16 (ptr + n + 2);
1197 XL_CHECK_CONDITION_VAL (o <= clen,
1198 go_format_new_markup (txo_run.accum, FALSE));
1200 txo_run.first = g_utf8_offset_to_pointer (str, o) - str;
1201 XL_CHECK_CONDITION_VAL (txo_run.first < txo_run.last,
1202 go_format_new_markup (txo_run.accum, FALSE));
1204 pango_attr_list_filter (ms_container_get_markup (c, l),
1205 (PangoAttrFilterFunc) append_markup,
1206 &txo_run);
1207 txo_run.last = txo_run.first;
1209 } else {
1210 XL_CHECK_CONDITION_VAL (ptr+1 <= end , NULL);
1211 n = 2 * GSF_LE_GET_GUINT8 (ptr);
1212 ptr += 1;
1214 XL_CHECK_CONDITION_VAL (ptr + n == end , NULL);
1216 txo_run.accum = pango_attr_list_new ();
1217 while (n > 0) {
1218 n -= 2;
1219 txo_run.first = g_utf8_offset_to_pointer (str,
1220 GSF_LE_GET_GUINT8 (ptr + n)) - str;
1221 pango_attr_list_filter (ms_container_get_markup (
1222 c, GSF_LE_GET_GUINT8 (ptr + n + 1)),
1223 (PangoAttrFilterFunc) append_markup, &txo_run);
1224 txo_run.last = txo_run.first;
1227 return go_format_new_markup (txo_run.accum, FALSE);
1231 * NB. Whilst the string proper is split, and whilst we get several headers,
1232 * it seems that the attributes appear in a single block after the end
1233 * of the string, which may also be split over continues.
1235 static guint32
1236 sst_read_string (BiffQuery *q, MSContainer const *c,
1237 ExcelStringEntry *res, guint32 offset)
1239 guint32 get_len, chars_left, total_len, total_end_len = 0;
1240 unsigned i, post_data_len, n_markup, total_n_markup = 0;
1241 gboolean use_utf16, has_extended;
1242 char *str, *old_res, *res_str = NULL;
1244 offset = ms_biff_query_bound_check (q, offset, 2);
1245 if (offset == (guint32)-1)
1246 return offset;
1247 XL_CHECK_CONDITION_VAL (offset < q->length, offset);
1248 total_len = GSF_LE_GET_GUINT16 (q->data + offset);
1249 offset += 2;
1250 do {
1251 offset = ms_biff_query_bound_check (q, offset, 1);
1252 if (offset == (guint32)-1) {
1253 g_free (res_str);
1254 return offset;
1256 offset += excel_read_string_header
1257 (q->data + offset, q->length - offset,
1258 &use_utf16, &n_markup, &has_extended,
1259 &post_data_len);
1260 total_end_len += post_data_len;
1261 total_n_markup += n_markup;
1262 chars_left = (q->length - offset) / (use_utf16 ? 2 : 1);
1263 get_len = (chars_left > total_len) ? total_len : chars_left;
1264 total_len -= get_len;
1266 str = excel_get_chars (c->importer,
1267 q->data + offset, get_len, use_utf16, NULL);
1268 offset += get_len * (use_utf16 ? 2 : 1);
1270 /* Handle corrupted string. */
1271 if (!str)
1272 str = g_strdup ("");
1274 if (res_str != NULL) {
1275 old_res = res_str;
1276 res_str = g_strconcat (old_res, str, NULL);
1277 g_free (str);
1278 g_free (old_res);
1279 } else
1280 res_str = str;
1281 } while (total_len > 0);
1283 if (total_n_markup > 0) {
1284 TXORun txo_run;
1285 PangoAttrList *prev_markup = NULL;
1287 txo_run.accum = pango_attr_list_new ();
1288 txo_run.first = 0;
1289 for (i = total_n_markup ; i-- > 0 ; offset += 4) {
1290 offset = ms_biff_query_bound_check (q, offset, 4);
1291 if (offset == (guint32)-1) {
1292 g_free (res_str);
1293 pango_attr_list_unref (txo_run.accum);
1294 return offset;
1296 if ((q->length >= offset + 4)) {
1297 guint16 o = GSF_LE_GET_GUINT16 (q->data + offset);
1298 size_t l = strlen (res_str);
1299 txo_run.last = g_utf8_offset_to_pointer (res_str, MIN (o, l)) - res_str;
1300 if (prev_markup != NULL)
1301 pango_attr_list_filter (prev_markup,
1302 (PangoAttrFilterFunc) append_markup, &txo_run);
1303 txo_run.first = txo_run.last;
1304 prev_markup = ms_container_get_markup (
1305 c, GSF_LE_GET_GUINT16 (q->data + offset + 2));
1306 } else
1307 g_warning ("A TXO entry is across CONTINUEs. We need to handle those properly");
1309 txo_run.last = G_MAXINT;
1310 pango_attr_list_filter (prev_markup,
1311 (PangoAttrFilterFunc) append_markup, &txo_run);
1312 res->markup = go_format_new_markup (txo_run.accum, FALSE);
1314 total_end_len -= 4*total_n_markup;
1317 res->content = go_string_new_nocopy (res_str);
1318 return offset + total_end_len;
1321 static void
1322 excel_read_SST (BiffQuery *q, GnmXLImporter *importer)
1324 guint32 offset;
1325 unsigned i, sst_len;
1327 XL_CHECK_CONDITION (q->length >= 8);
1329 d (4, {
1330 g_printerr ("SST total = %u, sst = %u\n",
1331 GSF_LE_GET_GUINT32 (q->data + 0),
1332 GSF_LE_GET_GUINT32 (q->data + 4));
1333 gsf_mem_dump (q->data, q->length);
1336 sst_len = GSF_LE_GET_GUINT32 (q->data + 4);
1337 XL_CHECK_CONDITION (sst_len < INT_MAX / sizeof (ExcelStringEntry));
1339 importer->sst_len = sst_len;
1340 importer->sst = g_new0 (ExcelStringEntry, importer->sst_len);
1342 offset = 8;
1343 for (i = 0; i < importer->sst_len; i++) {
1344 offset = sst_read_string (q, &importer->container, importer->sst + i, offset);
1345 if (offset == (guint32)-1)
1346 break;
1348 if (importer->sst[i].content == NULL)
1349 d (4, g_printerr ("Blank string in table at 0x%x.\n", i););
1350 #ifndef NO_DEBUG_EXCEL
1351 else if (ms_excel_read_debug > 4)
1352 g_printerr ("%s\n", importer->sst[i].content->str);
1353 #endif
1357 static void
1358 excel_read_EXSST (BiffQuery *q, GnmXLImporter *importer)
1360 XL_CHECK_CONDITION (q->length >= 2);
1361 d (10, g_printerr ("Bucketsize = %hu,\tnum buckets = %d\n",
1362 GSF_LE_GET_GUINT16 (q->data), (q->length - 2) / 8););
1365 static void
1366 excel_read_1904 (BiffQuery *q, GnmXLImporter *importer)
1368 XL_CHECK_CONDITION (q->length >= 2);
1369 if (GSF_LE_GET_GUINT16 (q->data) == 1)
1370 workbook_set_1904 (importer->wb, TRUE);
1373 GnmValue *
1374 xls_value_new_err (GnmEvalPos const *pos, guint8 err)
1376 switch (err) {
1377 case 0: return value_new_error_NULL (pos);
1378 case 7: return value_new_error_DIV0 (pos);
1379 case 15: return value_new_error_VALUE (pos);
1380 case 23: return value_new_error_REF (pos);
1381 case 29: return value_new_error_NAME (pos);
1382 case 36: return value_new_error_NUM (pos);
1383 case 42: return value_new_error_NA (pos);
1384 default: return value_new_error (pos, _("#UNKNOWN!"));
1388 MsBiffBofData *
1389 ms_biff_bof_data_new (BiffQuery *q)
1391 MsBiffBofData *ans = g_new (MsBiffBofData, 1);
1393 if (q->length >= 4) {
1395 /* Determine type from BOF */
1396 switch (q->opcode) {
1397 case BIFF_BOF_v0: ans->version = MS_BIFF_V2; break;
1398 case BIFF_BOF_v2: ans->version = MS_BIFF_V3; break;
1399 case BIFF_BOF_v4: ans->version = MS_BIFF_V4; break;
1400 case BIFF_BOF_v8:
1401 d (2, {
1402 g_printerr ("Complicated BIFF version 0x%x\n",
1403 GSF_LE_GET_GUINT16 (q->non_decrypted_data));
1404 gsf_mem_dump (q->non_decrypted_data, q->length);
1407 switch (GSF_LE_GET_GUINT16 (q->non_decrypted_data)) {
1408 case 0x0600:
1409 ans->version = MS_BIFF_V8;
1410 break;
1411 case 0x0500: /* * OR ebiff7: FIXME ? ! */
1412 ans->version = MS_BIFF_V7;
1413 break;
1414 /* The following are non-standard records written
1415 by buggy tools. Taken from OO docs. */
1416 case 0x0400:
1417 ans->version = MS_BIFF_V4;
1418 break;
1419 case 0x0300:
1420 ans->version = MS_BIFF_V3;
1421 break;
1422 case 0x0200:
1423 case 0x0007:
1424 case 0x0000:
1425 ans->version = MS_BIFF_V2;
1426 break;
1427 default:
1428 g_printerr ("Unknown BIFF sub-number 0x%X in BOF %x\n",
1429 GSF_LE_GET_GUINT16 (q->non_decrypted_data), q->opcode);
1430 ans->version = MS_BIFF_V_UNKNOWN;
1432 break;
1434 default:
1435 g_printerr ("Unknown BIFF number in BOF %x\n", q->opcode);
1436 ans->version = MS_BIFF_V_UNKNOWN;
1437 g_printerr ("Biff version %d\n", ans->version);
1439 switch (GSF_LE_GET_GUINT16 (q->non_decrypted_data + 2)) {
1440 case 0x0005: ans->type = MS_BIFF_TYPE_Workbook; break;
1441 case 0x0006: ans->type = MS_BIFF_TYPE_VBModule; break;
1442 case 0x0010: ans->type = MS_BIFF_TYPE_Worksheet; break;
1443 case 0x0020: ans->type = MS_BIFF_TYPE_Chart; break;
1444 case 0x0040: ans->type = MS_BIFF_TYPE_Macrosheet; break;
1445 case 0x0100: ans->type = MS_BIFF_TYPE_Workspace; break;
1446 default:
1447 ans->type = MS_BIFF_TYPE_Unknown;
1448 g_printerr ("Unknown BIFF type in BOF %x\n", GSF_LE_GET_GUINT16 (q->non_decrypted_data + 2));
1449 break;
1451 /* Now store in the directory array: */
1452 d (2, g_printerr ("BOF %x, %d == %d, %d\n", q->opcode, q->length,
1453 ans->version, ans->type););
1454 } else {
1455 g_printerr ("Not a BOF !\n");
1456 ans->version = MS_BIFF_V_UNKNOWN;
1457 ans->type = MS_BIFF_TYPE_Unknown;
1460 return ans;
1463 void
1464 ms_biff_bof_data_destroy (MsBiffBofData *data)
1466 g_free (data);
1469 static void
1470 excel_read_BOUNDSHEET (BiffQuery *q, GnmXLImporter *importer)
1472 BiffBoundsheetData *bs;
1473 char const *default_name = "Unknown%d";
1474 gboolean oldstyle = (importer->ver <= MS_BIFF_V4);
1476 XL_CHECK_CONDITION (q->length >= (oldstyle ? 1 : 6));
1478 bs = g_new0 (BiffBoundsheetData, 1);
1479 bs->gnm_type = GNM_SHEET_DATA;
1481 if (oldstyle) {
1482 bs->streamStartPos = 0; /* Excel 4 doesn't tell us */
1483 bs->type = MS_BIFF_TYPE_Worksheet;
1484 default_name = _("Sheet%d");
1485 bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1486 bs->name = excel_biff_text_1 (importer, q, 0);
1487 } else {
1488 if (importer->ver > MS_BIFF_V8)
1489 g_printerr ("Unknown BIFF Boundsheet spec. Assuming same as Biff7 FIXME\n");
1490 bs->streamStartPos = GSF_LE_GET_GUINT32 (q->non_decrypted_data);
1492 /* NOTE : MS Docs appear wrong. It is visiblity _then_ type */
1493 switch (GSF_LE_GET_GUINT8 (q->data + 5)) {
1494 case 0: bs->type = MS_BIFF_TYPE_Worksheet;
1495 default_name = _("Sheet%d");
1496 break;
1497 case 1: bs->type = MS_BIFF_TYPE_Macrosheet;
1498 bs->gnm_type = GNM_SHEET_XLM;
1499 default_name = _("Macro%d");
1500 break;
1501 case 2: bs->type = MS_BIFF_TYPE_Chart;
1502 bs->gnm_type = GNM_SHEET_OBJECT;
1503 default_name = _("Chart%d");
1504 break;
1505 case 6: bs->type = MS_BIFF_TYPE_VBModule;
1506 default_name = _("Module%d");
1507 break;
1508 default:
1509 g_printerr ("Unknown boundsheet type: %d\n", GSF_LE_GET_GUINT8 (q->data + 4));
1510 bs->type = MS_BIFF_TYPE_Unknown;
1512 switch ((GSF_LE_GET_GUINT8 (q->data + 4)) & 0x3) {
1513 case 0: bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1514 break;
1515 case 1: bs->visibility = GNM_SHEET_VISIBILITY_HIDDEN;
1516 break;
1517 case 2: bs->visibility = GNM_SHEET_VISIBILITY_VERY_HIDDEN;
1518 break;
1519 default:
1520 g_printerr ("Unknown sheet hiddenness %d\n", (GSF_LE_GET_GUINT8 (q->data + 4)) & 0x3);
1521 bs->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
1524 /* TODO: find some documentation on this.
1525 * Sample data and OpenCalc imply that the docs are incorrect. It
1526 * seems like the name length is 1 byte. Loading sample sheets in
1527 * other locales universally seem to treat the first byte as a length
1528 * and the second as the unicode flag header.
1530 bs->name = excel_biff_text_1 (importer, q, 6);
1533 /* TODO: find some documentation on this.
1534 * It appears that if the name is null it defaults to Sheet%d?
1535 * However, we have only one test case and no docs.
1537 if (bs->name == NULL || bs->name[0] == 0) {
1538 g_free (bs->name);
1539 bs->name = g_strdup_printf (default_name,
1540 importer->boundsheet_sheet_by_index->len + 1);
1543 switch (bs->type) {
1544 case MS_BIFF_TYPE_Worksheet :
1545 case MS_BIFF_TYPE_Macrosheet :
1546 case MS_BIFF_TYPE_Chart :
1547 bs->esheet = excel_sheet_new (importer, bs->name, bs->gnm_type);
1549 if (bs->esheet && bs->esheet->sheet)
1550 g_object_set (bs->esheet->sheet,
1551 "visibility", bs->visibility,
1552 NULL);
1553 break;
1554 default :
1555 bs->esheet = NULL;
1558 bs->index = importer->boundsheet_sheet_by_index->len;
1559 g_ptr_array_add (importer->boundsheet_sheet_by_index, bs->esheet ? bs->esheet->sheet : NULL);
1560 g_hash_table_insert (importer->boundsheet_data_by_stream,
1561 GUINT_TO_POINTER (bs->streamStartPos), bs);
1563 d (1, g_printerr ("Boundsheet: %d) '%s' %p, %d:%d\n", bs->index,
1564 bs->name, bs->esheet, bs->type, bs->visibility););
1567 static void
1568 biff_boundsheet_data_destroy (BiffBoundsheetData *d)
1570 g_free (d->name);
1571 g_free (d);
1574 static void
1575 excel_read_FORMAT (BiffQuery *q, GnmXLImporter *importer)
1577 MsBiffVersion const ver = importer->ver;
1578 BiffFormatData *d;
1580 if (ver >= MS_BIFF_V7) {
1581 XL_CHECK_CONDITION (q->length >= 4);
1583 d = g_new (BiffFormatData, 1);
1584 d->idx = GSF_LE_GET_GUINT16 (q->data);
1585 d->name = (ver >= MS_BIFF_V8)
1586 ? excel_biff_text_2 (importer, q, 2)
1587 : excel_biff_text_1 (importer, q, 2);
1588 } else {
1589 size_t minlen = (ver >= MS_BIFF_V4 ? 3 : 1);
1590 XL_CHECK_CONDITION (q->length >= minlen);
1592 d = g_new (BiffFormatData, 1);
1593 /* no usable index */
1594 d->idx = g_hash_table_size (importer->format_table);
1595 d->name = (ver >= MS_BIFF_V4)
1596 ? excel_biff_text_1 (importer, q, 2)
1597 : excel_biff_text_1 (importer, q, 0);
1600 d (3, g_printerr ("Format data: 0x%x == '%s'\n", d->idx, d->name););
1602 g_hash_table_insert (importer->format_table, GUINT_TO_POINTER (d->idx), d);
1605 static void
1606 excel_read_FONT (BiffQuery *q, GnmXLImporter *importer)
1608 MsBiffVersion const ver = importer->ver;
1609 ExcelFont *fd;
1610 guint16 data;
1611 guint8 data1;
1613 XL_CHECK_CONDITION (q->length >= 4);
1615 fd = g_new (ExcelFont, 1);
1616 fd->height = GSF_LE_GET_GUINT16 (q->data + 0);
1617 data = GSF_LE_GET_GUINT16 (q->data + 2);
1618 fd->italic = (data & 0x2) == 0x2;
1619 fd->struck_out = (data & 0x8) == 0x8;
1620 fd->script = GO_FONT_SCRIPT_STANDARD;
1621 fd->underline = XLS_ULINE_NONE;
1622 fd->codepage = 1252;
1624 if (ver <= MS_BIFF_V2) {
1625 int cp;
1626 guint16 opcode;
1627 fd->boldness = (data & 0x1) ? 0x2bc : 0x190;
1628 fd->underline = (data & 0x4) ? XLS_ULINE_SINGLE : XLS_ULINE_NONE;
1629 fd->fontname = excel_biff_text_1 (importer, q, 4);
1630 if (ms_biff_query_peek_next (q, &opcode) &&
1631 opcode == BIFF_FONT_COLOR) {
1632 ms_biff_query_next (q);
1633 XL_CHECK_CONDITION (q->length >= 2);
1634 fd->color_idx = GSF_LE_GET_GUINT16 (q->data);
1635 } else
1636 fd->color_idx = 0x7f;
1637 cp = gnm_font_override_codepage (fd->fontname);
1638 fd->codepage = (cp > 0 ? cp : 1252);
1639 } else if (ver <= MS_BIFF_V4) /* Guess */ {
1640 int cp;
1641 XL_CHECK_CONDITION (q->length >= 6);
1642 fd->color_idx = GSF_LE_GET_GUINT16 (q->data + 4);
1643 fd->boldness = (data & 0x1) ? 0x2bc : 0x190;
1644 fd->underline = (data & 0x4) ? XLS_ULINE_SINGLE : XLS_ULINE_NONE;
1645 fd->fontname = excel_biff_text_1 (importer, q, 6);
1646 cp = gnm_font_override_codepage (fd->fontname);
1647 fd->codepage = (cp > 0 ? cp : 1252);
1648 } else {
1649 XL_CHECK_CONDITION (q->length >= 13);
1651 fd->color_idx = GSF_LE_GET_GUINT16 (q->data + 4);
1652 fd->boldness = GSF_LE_GET_GUINT16 (q->data + 6);
1653 data = GSF_LE_GET_GUINT16 (q->data + 8);
1654 switch (data) {
1655 case 0: fd->script = GO_FONT_SCRIPT_STANDARD; break;
1656 case 1: fd->script = GO_FONT_SCRIPT_SUPER; break;
1657 case 2: fd->script = GO_FONT_SCRIPT_SUB; break;
1658 default:
1659 g_printerr ("Unknown script %d\n", data);
1660 break;
1663 data1 = GSF_LE_GET_GUINT8 (q->data + 10);
1664 switch (data1) {
1665 case 0: fd->underline = XLS_ULINE_NONE; break;
1666 case 1: fd->underline = XLS_ULINE_SINGLE; break;
1667 case 2: fd->underline = XLS_ULINE_DOUBLE; break;
1668 case 0x21: fd->underline = XLS_ULINE_SINGLE_ACC; break; /* single accounting */
1669 case 0x22: fd->underline = XLS_ULINE_DOUBLE_ACC; break; /* double accounting */
1670 default:
1671 g_printerr ("Unknown uline %#x\n", (int)data1);
1672 break;
1674 fd->fontname = excel_biff_text_1 (importer, q, 14);
1676 data1 = GSF_LE_GET_GUINT8 (q->data + 12);
1677 switch (data1) {
1678 case 0: {
1679 int cp = gnm_font_override_codepage (fd->fontname);
1680 if (cp >= 0) {
1681 fd->codepage = cp;
1682 break;
1684 if (importer->codepage_override > 0) {
1685 fd->codepage = importer->codepage_override;
1686 break;
1689 /* no break */
1690 case 1:
1691 case 255: fd->codepage = 1252; break; /* ANSI Latin, System Default, OEM Latin I */
1692 case 77: fd->codepage = 10000; break; /* Apple */
1693 case 128: fd->codepage = 932; break; /* Japanese Shift-JIS */
1694 case 129: fd->codepage = 949; break; /* Korean Hangul */
1695 case 130: fd->codepage = 1361; break; /* Korean Johab */
1696 case 134: fd->codepage = 936; break; /* Chinese Simplified */
1697 case 136: fd->codepage = 950; break; /* Chinese Traditional */
1698 case 161: fd->codepage = 1253; break; /* Greek */
1699 case 162: fd->codepage = 1254; break; /* Turkish */
1700 case 163: fd->codepage = 1258; break; /* Vietnamese */
1701 case 177: fd->codepage = 1255; break; /* Hebrew */
1702 case 178: fd->codepage = 1256; break; /* Arabic */
1703 case 186: fd->codepage = 1257; break; /* Baltic */
1704 case 204: fd->codepage = 1251; break; /* Russian */
1705 case 222: fd->codepage = 874; break; /* Thai */
1706 case 238: fd->codepage = 1250; break; /* Central European */
1707 default:
1708 g_printerr ("Unknown charset %#x\n", (int) data1);
1709 break;
1712 fd->color_idx &= 0x7f; /* Undocumented but a good idea */
1714 if (fd->fontname == NULL) {
1715 /* Note sure why -- see #418868. */
1716 fd->fontname = g_strdup ("Arial");
1719 fd->attrs = NULL;
1720 fd->go_font = NULL;
1722 fd->index = g_hash_table_size (importer->font_data);
1723 if (fd->index >= 4) /* Weird: for backwards compatibility */
1724 fd->index++;
1725 d (1, g_printerr ("Insert font '%s' (%d) size %d pts color %d\n",
1726 fd->fontname, fd->index, fd->height / 20, fd->color_idx););
1727 d (3, g_printerr ("Font color = 0x%x\n", fd->color_idx););
1729 g_hash_table_insert (importer->font_data,
1730 GINT_TO_POINTER (fd->index), fd);
1734 static void
1735 excel_font_free (ExcelFont *fd)
1737 if (NULL != fd->attrs) {
1738 pango_attr_list_unref (fd->attrs);
1739 fd->attrs = NULL;
1741 if (NULL != fd->go_font) {
1742 go_font_unref (fd->go_font);
1743 fd->go_font = NULL;
1745 g_free (fd->fontname);
1746 g_free (fd);
1749 static void
1750 biff_format_data_destroy (BiffFormatData *d)
1752 g_free (d->name);
1753 g_free (d);
1756 /** Default color table for BIFF5/BIFF7. */
1757 ExcelPaletteEntry const excel_default_palette_v7 [] = {
1758 { 0, 0, 0}, {255,255,255}, {255, 0, 0}, { 0,255, 0},
1759 { 0, 0,255}, {255,255, 0}, {255, 0,255}, { 0,255,255},
1761 {128, 0, 0}, { 0,128, 0}, { 0, 0,128}, {128,128, 0},
1762 {128, 0,128}, { 0,128,128}, {192,192,192}, {128,128,128},
1764 {128,128,255}, {128, 32, 96}, {255,255,192}, {160,224,224},
1765 { 96, 0,128}, {255,128,128}, { 0,128,192}, {192,192,255},
1767 { 0, 0,128}, {255, 0,255}, {255,255, 0}, { 0,255,255},
1768 {128, 0,128}, {128, 0, 0}, { 0,128,128}, { 0, 0,255},
1770 { 0,204,255}, {105,255,255}, {204,255,204}, {255,255,153},
1771 {166,202,240}, {204,156,204}, {204,153,255}, {227,227,227},
1773 { 51,102,255}, { 51,204,204}, { 51,153, 51}, {153,153, 51},
1774 {153,102, 51}, {153,102,102}, {102,102,153}, {150,150,150},
1776 { 51, 51,204}, { 51,102,102}, { 0, 51, 0}, { 51, 51, 0},
1777 {102, 51, 0}, {153, 51,102}, { 51, 51,153}, { 66, 66, 66}
1780 ExcelPaletteEntry const excel_default_palette_v8 [] = {
1781 { 0, 0, 0}, {255,255,255}, {255, 0, 0}, { 0,255, 0},
1782 { 0, 0,255}, {255,255, 0}, {255, 0,255}, { 0,255,255},
1784 {128, 0, 0}, { 0,128, 0}, { 0, 0,128}, {128,128, 0},
1785 {128, 0,128}, { 0,128,128}, {192,192,192}, {128,128,128},
1787 {153,153,255}, {153, 51,102}, {255,255,204}, {204,255,255},
1788 {102, 0,102}, {255,128,128}, { 0,102,204}, {204,204,255},
1790 { 0, 0,128}, {255, 0,255}, {255,255, 0}, { 0,255,255},
1791 {128, 0,128}, {128, 0, 0}, { 0,128,128}, { 0, 0,255},
1793 { 0,204,255}, {204,255,255}, {204,255,204}, {255,255,153},
1794 {153,204,255}, {255,153,204}, {204,153,255}, {255,204,153},
1796 { 51,102,255}, { 51,204,204}, {153,204, 0}, {255,204, 0},
1797 {255,153, 0}, {255,102, 0}, {102,102,153}, {150,150,150},
1799 { 0, 51,102}, { 51,153,102}, { 0, 51, 0}, { 51, 51, 0},
1800 {153, 51, 0}, {153, 51,102}, { 51, 51,153}, { 51, 51, 51}
1803 GnmColor *
1804 excel_palette_get (GnmXLImporter *importer, gint idx)
1806 ExcelPalette *pal;
1808 /* return black on failure */
1809 g_return_val_if_fail (importer != NULL, style_color_black ());
1811 if (NULL == (pal = importer->palette)) {
1812 int entries = EXCEL_DEF_PAL_LEN;
1813 ExcelPaletteEntry const *defaults = (importer->ver >= MS_BIFF_V8)
1814 ? excel_default_palette_v8 : excel_default_palette_v7;
1816 pal = importer->palette = g_new (ExcelPalette, 1);
1817 pal->length = entries;
1818 pal->red = g_new (int, entries);
1819 pal->green = g_new (int, entries);
1820 pal->blue = g_new (int, entries);
1821 pal->gnm_colors = g_new (GnmColor *, entries);
1823 while (--entries >= 0) {
1824 pal->red[entries] = defaults[entries].r;
1825 pal->green[entries] = defaults[entries].g;
1826 pal->blue[entries] = defaults[entries].b;
1827 pal->gnm_colors[entries] = NULL;
1831 /* NOTE: not documented but seems close
1832 * If you find a normative reference please forward it.
1834 * The color index field seems to use
1835 * 8-63 = Palette index 0-55
1836 * 64 = auto pattern, auto border
1837 * 65 = auto background
1838 * 127 = auto font
1840 * 65 is always white, and 127 always black. 64 is black
1841 * if the fDefaultHdr flag in WINDOW2 is unset, otherwise it's
1842 * the grid color from WINDOW2.
1845 d (4, g_printerr ("Color Index %d\n", idx););
1847 if (idx == 1 || idx == 65)
1848 return style_color_white ();
1849 switch (idx) {
1850 case 0: /* black */
1851 case 64: /* system text ? */
1852 case 81: /* tooltip text */
1853 case 0x7fff: /* system text ? */
1854 return style_color_black ();
1855 case 1: /* white */
1856 case 65: /* system back ? */
1857 return style_color_white ();
1859 case 80: /* tooltip background */
1860 return gnm_color_new_rgb8 (0xff, 0xff, 0xe0);
1862 case 2: return gnm_color_new_rgb8 (0xff, 0, 0); /* red */
1863 case 3: return gnm_color_new_rgb8 ( 0, 0xff, 0); /* green */
1864 case 4: return gnm_color_new_rgb8 ( 0, 0, 0xff); /* blue */
1865 case 5: return gnm_color_new_rgb8 (0xff, 0xff, 0); /* yellow */
1866 case 6: return gnm_color_new_rgb8 (0xff, 0, 0xff); /* magenta */
1867 case 7: return gnm_color_new_rgb8 ( 0, 0xff, 0xff); /* cyan */
1868 default:
1869 break;
1872 idx -= 8;
1873 if (idx < 0 || pal->length <= idx) {
1874 g_warning ("EXCEL: color index (%d) is out of range (8..%d). Defaulting to black",
1875 idx + 8, pal->length+8);
1876 return style_color_black ();
1879 if (pal->gnm_colors[idx] == NULL) {
1880 pal->gnm_colors[idx] =
1881 gnm_color_new_rgb8 (pal->red[idx],
1882 pal->green[idx],
1883 pal->blue[idx]);
1884 g_return_val_if_fail (pal->gnm_colors[idx],
1885 style_color_black ());
1886 d (5, {
1887 const GnmColor *c = pal->gnm_colors[idx];
1888 g_printerr ("New color in slot %d: RGBA = %x,%x,%x,%x\n",
1889 idx,
1890 GO_COLOR_UINT_R (c->go_color),
1891 GO_COLOR_UINT_G (c->go_color),
1892 GO_COLOR_UINT_B (c->go_color),
1893 GO_COLOR_UINT_A (c->go_color));
1897 style_color_ref (pal->gnm_colors[idx]);
1898 return pal->gnm_colors[idx];
1901 static void
1902 excel_palette_destroy (ExcelPalette *pal)
1904 guint16 lp;
1906 g_free (pal->red);
1907 g_free (pal->green);
1908 g_free (pal->blue);
1909 for (lp = 0; lp < pal->length; lp++)
1910 if (pal->gnm_colors[lp])
1911 style_color_unref (pal->gnm_colors[lp]);
1912 g_free (pal->gnm_colors);
1913 g_free (pal);
1916 static void
1917 excel_read_PALETTE (BiffQuery *q, GnmXLImporter *importer)
1919 int lp, len;
1920 ExcelPalette *pal;
1922 XL_CHECK_CONDITION (q->length >= 2);
1923 len = GSF_LE_GET_GUINT16 (q->data);
1924 XL_CHECK_CONDITION (q->length >= 2u + len * 4u);
1926 pal = g_new (ExcelPalette, 1);
1927 pal->length = len;
1928 pal->red = g_new (int, len);
1929 pal->green = g_new (int, len);
1930 pal->blue = g_new (int, len);
1931 pal->gnm_colors = g_new (GnmColor *, len);
1933 d (3, g_printerr ("New palette with %d entries\n", len););
1935 for (lp = 0; lp < len; lp++) {
1936 guint32 num = GSF_LE_GET_GUINT32 (q->data + 2 + lp * 4);
1938 /* NOTE the order of bytes is different from what one would
1939 * expect */
1940 pal->blue[lp] = (num & 0x00ff0000) >> 16;
1941 pal->green[lp] = (num & 0x0000ff00) >> 8;
1942 pal->red[lp] = (num & 0x000000ff) >> 0;
1943 d (5, g_printerr ("Colour %d: 0x%8x (%x,%x,%x)\n", lp,
1944 num, pal->red[lp], pal->green[lp], pal->blue[lp]););
1946 pal->gnm_colors[lp] = NULL;
1948 if (importer->palette)
1949 excel_palette_destroy (importer->palette);
1950 importer->palette = pal;
1955 * Search for a font record from its index in the workbooks font table
1956 * NB. index 4 is omitted supposedly for backwards compatiblity
1957 * Returns the font color if there is one.
1959 ExcelFont const *
1960 excel_font_get (GnmXLImporter const *importer, unsigned font_idx)
1962 ExcelFont const *fd =
1963 g_hash_table_lookup (importer->font_data,
1964 GINT_TO_POINTER (font_idx));
1965 if (!fd) {
1966 g_warning ("Invalid font index %d\n", font_idx);
1967 /* Try fallback. */
1968 fd = g_hash_table_lookup (importer->font_data,
1969 GINT_TO_POINTER (0));
1972 return fd;
1975 GOFont const *
1976 excel_font_get_gofont (ExcelFont const *efont)
1978 if (NULL == efont->go_font) {
1979 PangoFontDescription *desc = pango_font_description_new ();
1981 d (1, { g_printerr ("EFONT: %s %d %d %d\n",
1982 efont->fontname,
1983 efont->boldness,
1984 efont->italic,
1985 efont->height); });
1986 #warning FINISH when GOFont is smarter
1987 pango_font_description_set_family (desc, efont->fontname);
1988 pango_font_description_set_weight (desc, efont->boldness);
1989 pango_font_description_set_style (desc,
1990 efont->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1991 pango_font_description_set_size (desc,
1992 efont->height * PANGO_SCALE / 20);
1994 ((ExcelFont *)efont)->go_font = go_font_new_by_desc (desc);
1996 return efont->go_font;
1999 static BiffXFData const *
2000 excel_get_xf (ExcelReadSheet *esheet, unsigned xfidx)
2002 GPtrArray const * const p = esheet->container.importer->XF_cell_records;
2004 g_return_val_if_fail (p != NULL, NULL);
2006 if (esheet_ver (esheet) == MS_BIFF_V2) {
2007 /* ignore the replicated info that comes with the index
2008 * we've already parsed the XF record */
2009 xfidx &= 0x3f;
2010 if (xfidx == 0x3f) {
2011 if (esheet->biff2_prev_xf_index < 0) {
2012 g_warning ("extension xf with no preceding old_xf record, using default as fallback");
2013 xfidx = 15;
2014 } else
2015 xfidx = esheet->biff2_prev_xf_index;
2019 if (xfidx >= p->len) {
2020 XL_CHECK_CONDITION_VAL (p->len > 0, NULL);
2021 g_warning ("XL: Xf index 0x%X is not in the range[0..0x%X)", xfidx, p->len);
2022 xfidx = 0;
2024 /* FIXME: when we can handle styles too deal with this correctly */
2025 /* g_return_val_if_fail (xf->xftype == MS_BIFF_X_CELL, NULL); */
2026 return g_ptr_array_index (p, xfidx);
2029 static GnmUnderline
2030 xls_uline_to_gnm_underline (MsBiffFontUnderline mul)
2032 g_return_val_if_fail (mul >= XLS_ULINE_NONE, UNDERLINE_NONE);
2033 g_return_val_if_fail (mul <= XLS_ULINE_DOUBLE_ACC, UNDERLINE_NONE);
2035 switch (mul) {
2036 case XLS_ULINE_SINGLE:
2037 return UNDERLINE_SINGLE;
2038 case XLS_ULINE_DOUBLE:
2039 return UNDERLINE_DOUBLE;
2040 case XLS_ULINE_SINGLE_ACC:
2041 return UNDERLINE_SINGLE_LOW;
2042 case XLS_ULINE_DOUBLE_ACC:
2043 return UNDERLINE_DOUBLE_LOW;
2044 case XLS_ULINE_NONE:
2045 default:
2046 return UNDERLINE_NONE;
2051 /* Adds a ref the result */
2052 static GnmStyle *
2053 excel_get_style_from_xf (ExcelReadSheet *esheet, BiffXFData const *xf)
2055 ExcelFont const *fd;
2056 GnmColor *pattern_color, *back_color, *font_color;
2057 int pattern_index, back_index, font_index;
2058 GnmStyle *mstyle;
2059 int i;
2061 if (xf == NULL)
2062 return NULL;
2064 /* If we've already done the conversion use the cached style */
2065 if (xf->mstyle != NULL) {
2066 gnm_style_ref (xf->mstyle);
2067 return xf->mstyle;
2070 /* Create a new style and fill it in */
2071 mstyle = gnm_style_new_default ();
2073 /* Format */
2074 if (xf->style_format)
2075 gnm_style_set_format (mstyle, xf->style_format);
2077 /* protection */
2078 gnm_style_set_contents_locked (mstyle, xf->locked);
2079 gnm_style_set_contents_hidden (mstyle, xf->hidden);
2081 /* Alignment */
2082 gnm_style_set_align_v (mstyle, xf->valign);
2083 gnm_style_set_align_h (mstyle, xf->halign);
2084 gnm_style_set_wrap_text (mstyle, xf->wrap_text);
2085 gnm_style_set_shrink_to_fit (mstyle, xf->shrink_to_fit);
2086 gnm_style_set_indent (mstyle, xf->indent);
2087 gnm_style_set_rotation (mstyle, xf->rotation);
2088 gnm_style_set_text_dir (mstyle, xf->text_dir);
2090 /* Font */
2091 fd = excel_font_get (esheet->container.importer, xf->font_idx);
2092 if (fd != NULL) {
2093 gnm_style_set_font_name (mstyle, fd->fontname);
2094 gnm_style_set_font_size (mstyle, fd->height / 20.0);
2095 gnm_style_set_font_bold (mstyle, fd->boldness >= 0x2bc);
2096 gnm_style_set_font_italic (mstyle, fd->italic);
2097 gnm_style_set_font_strike (mstyle, fd->struck_out);
2098 gnm_style_set_font_script (mstyle, fd->script);
2099 gnm_style_set_font_uline
2100 (mstyle, xls_uline_to_gnm_underline (fd->underline));
2101 font_index = fd->color_idx;
2102 } else
2103 font_index = 127; /* Default to Black */
2105 /* Background */
2106 gnm_style_set_pattern (mstyle, xf->fill_pattern_idx);
2108 /* Solid patterns seem to reverse the meaning */
2109 if (xf->fill_pattern_idx == 1) {
2110 pattern_index = xf->pat_backgnd_col;
2111 back_index = xf->pat_foregnd_col;
2112 } else {
2113 pattern_index = xf->pat_foregnd_col;
2114 back_index = xf->pat_backgnd_col;
2117 d (4, g_printerr ("back = %d, pat = %d, font = %d, pat_style = %d\n",
2118 back_index, pattern_index, font_index, xf->fill_pattern_idx););
2120 if (font_index == 127)
2121 font_color = style_color_auto_font ();
2122 else
2123 font_color = excel_palette_get (esheet->container.importer, font_index);
2125 switch (back_index) {
2126 case 64:
2127 back_color = sheet_style_get_auto_pattern_color (esheet->sheet);
2128 break;
2129 case 65:
2130 back_color = style_color_auto_back ();
2131 break;
2132 default:
2133 back_color = excel_palette_get (esheet->container.importer, back_index);
2134 break;
2137 switch (pattern_index) {
2138 case 64: /* Normal case for auto pattern color */
2139 pattern_color = sheet_style_get_auto_pattern_color
2140 (esheet->sheet);
2141 break;
2142 case 65:
2143 /* Mutated form, also observed in the wild, but only for
2144 solid fill. I. e.: this color is not visible. */
2145 pattern_color = style_color_auto_back ();
2146 break;
2147 default:
2148 pattern_color = excel_palette_get (esheet->container.importer, pattern_index);
2149 break;
2152 g_return_val_if_fail (back_color && pattern_color && font_color, NULL);
2154 d (4, g_printerr ("back = #%02x%02x%02x, pat = #%02x%02x%02x, font = #%02x%02x%02x, pat_style = %d\n",
2155 GO_COLOR_UINT_R (back_color->go_color),
2156 GO_COLOR_UINT_G (back_color->go_color),
2157 GO_COLOR_UINT_B (back_color->go_color),
2158 GO_COLOR_UINT_R (pattern_color->go_color),
2159 GO_COLOR_UINT_G (pattern_color->go_color),
2160 GO_COLOR_UINT_B (pattern_color->go_color),
2161 GO_COLOR_UINT_R (font_color->go_color),
2162 GO_COLOR_UINT_G (font_color->go_color),
2163 GO_COLOR_UINT_B (font_color->go_color),
2164 xf->fill_pattern_idx););
2166 gnm_style_set_font_color (mstyle, font_color);
2167 gnm_style_set_back_color (mstyle, back_color);
2168 gnm_style_set_pattern_color (mstyle, pattern_color);
2170 /* Borders */
2171 for (i = 0; i < STYLE_ORIENT_MAX; i++) {
2172 GnmStyle *tmp = mstyle;
2173 GnmStyleElement const t = MSTYLE_BORDER_TOP + i;
2174 GnmStyleBorderLocation const sbl = GNM_STYLE_BORDER_TOP + i;
2175 int const color_index = xf->border_color[i];
2176 GnmColor *color;
2178 switch (color_index) {
2179 case 64:
2180 color = sheet_style_get_auto_pattern_color
2181 (esheet->sheet);
2182 d (4, g_printerr ("border with color_index=%d\n",
2183 color_index););
2184 break;
2185 case 65:
2186 color = style_color_auto_back ();
2187 /* We haven't seen this yet.
2188 We know that 64 and 127 occur in the wild */
2189 d (4, g_printerr ("border with color_index=%d\n",
2190 color_index););
2191 break;
2192 case 127:
2193 color = style_color_auto_font ();
2194 break;
2195 default:
2196 color = excel_palette_get (esheet->container.importer, color_index);
2197 break;
2199 gnm_style_set_border (tmp, t,
2200 gnm_style_border_fetch (xf->border_type[i],
2201 color,
2202 gnm_style_border_get_orientation (sbl)));
2205 /* Set the cache (const_cast) */
2206 ((BiffXFData *)xf)->mstyle = mstyle;
2207 gnm_style_ref (mstyle);
2208 return xf->mstyle;
2211 static GnmBorder *
2212 excel_choose_border (GnmBorder *b1, GnmBorder *b2)
2214 /* double > thick > medium > medium-dash > medium-dash-dot > slanted dash-dot >
2215 medium dash-dot-dot > thin > dashed > dotted > dash-dot > dash-dot-dot > hair */
2216 static int choice[GNM_STYLE_BORDER_SLANTED_DASH_DOT + 1]
2217 [GNM_STYLE_BORDER_SLANTED_DASH_DOT + 1]
2218 = { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_NONE */
2219 { 1,0,0,1,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_THIN */
2220 { 1,1,0,1,1,0,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM */
2221 { 1,0,0,0,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_DASHED */
2222 { 1,0,0,0,0,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_DOTTED */
2223 { 1,1,1,1,1,0,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_THICK */
2224 { 1,1,1,1,1,1,0,1,1,1,1,1,1,1 }, /* GNM_STYLE_BORDER_DOUBLE */
2225 { 1,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_HAIR */
2226 { 1,1,0,1,1,0,0,1,0,1,1,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM_DASH */
2227 { 1,0,0,0,0,0,0,1,0,0,0,1,0,0 }, /* GNM_STYLE_BORDER_DASH_DOT */
2228 { 1,1,0,1,1,0,0,1,0,1,0,1,1,1 }, /* GNM_STYLE_BORDER_MEDIUM_DASH_DOT */
2229 { 1,0,0,0,0,0,0,1,0,0,0,0,0,0 }, /* GNM_STYLE_BORDER_DASH_DOT_DOT */
2230 { 1,1,0,1,1,0,0,1,0,1,0,1,0,0 }, /* GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT */
2231 { 1,1,0,1,1,0,0,1,0,1,0,1,1,0 } /* GNM_STYLE_BORDER_SLANTED_DASH_DOT */
2234 return (choice[b1->line_type][b2->line_type]) ? b1 : b2;
2237 static BiffXFData const *
2238 excel_set_xf (ExcelReadSheet *esheet, BiffQuery *q)
2240 guint16 col, row;
2241 BiffXFData const *xf;
2242 GnmStyle *mstyle;
2243 Sheet *sheet = esheet->sheet;
2245 XL_CHECK_CONDITION_VAL (q->length >= 6, NULL);
2246 col = XL_GETCOL (q);
2247 row = XL_GETROW (q);
2248 xf = excel_get_xf (esheet, GSF_LE_GET_GUINT16 (q->data + 4));
2249 XL_CHECK_CONDITION_VAL (col < gnm_sheet_get_max_cols (sheet), xf);
2250 XL_CHECK_CONDITION_VAL (row < gnm_sheet_get_max_rows (sheet), xf);
2251 mstyle = excel_get_style_from_xf (esheet, xf);
2253 d (3, g_printerr ("%s!%s%d = xf(0x%hx) = style (%p) [LEN = %u]\n", sheet->name_unquoted,
2254 col_name (col), row + 1, GSF_LE_GET_GUINT16 (q->data + 4), mstyle, q->length););
2256 if (mstyle != NULL) {
2257 GnmBorder *top_b, *left_b;
2259 sheet_style_set_pos (sheet, col, row, mstyle);
2261 /* In Excel & Gnumeric generated xls-files we do not have a conflict */
2262 /* between borders of adjacent cells, but according to bug #660605 */
2263 /* there are xls files in the wild that have a conflict. We need to */
2264 /* resolve these conflicts to ensure consistent behaviour when we edit */
2265 /* borders and to provide the expected border appearance. */
2267 top_b = gnm_style_get_border (mstyle, MSTYLE_BORDER_TOP);
2268 left_b = gnm_style_get_border (mstyle, MSTYLE_BORDER_LEFT);
2270 if ((row > 0 && top_b != NULL && top_b->line_type != GNM_STYLE_BORDER_NONE) ||
2271 (col > 0 && left_b != NULL && left_b->line_type != GNM_STYLE_BORDER_NONE)) {
2272 GnmBorder **overlay = g_new0 (GnmBorder *, GNM_STYLE_BORDER_EDGE_MAX);
2273 GnmRange range;
2275 if (row > 0 &&
2276 top_b != NULL && top_b->line_type != GNM_STYLE_BORDER_NONE) {
2277 GnmStyle const *previous = sheet_style_get (sheet, col, row - 1);
2278 if (previous != NULL) {
2279 GnmBorder *prev_b = gnm_style_get_border
2280 (previous, MSTYLE_BORDER_BOTTOM);
2281 if (prev_b != NULL &&
2282 prev_b->line_type != GNM_STYLE_BORDER_NONE &&
2283 prev_b->line_type != top_b->line_type)
2284 overlay[GNM_STYLE_BORDER_TOP] =
2285 gnm_style_border_ref
2286 (excel_choose_border (top_b, prev_b));
2289 if (col > 0 &&
2290 left_b != NULL && left_b->line_type != GNM_STYLE_BORDER_NONE) {
2291 GnmStyle const *previous = sheet_style_get (sheet, col - 1, row);
2292 if (previous != NULL) {
2293 GnmBorder *prev_b = gnm_style_get_border
2294 (previous, MSTYLE_BORDER_RIGHT);
2295 if (prev_b != NULL &&
2296 prev_b->line_type != GNM_STYLE_BORDER_NONE &&
2297 prev_b->line_type != left_b->line_type)
2298 overlay[GNM_STYLE_BORDER_LEFT] =
2299 gnm_style_border_ref
2300 (excel_choose_border (left_b, prev_b));
2304 /* We are using sheet_style_apply_border rather than */
2305 /* sheet_style_apply_pos since it clears the appropriate */
2306 /* adjacent borders */
2307 range_init (&range, col, row, col, row);
2308 sheet_style_apply_border (sheet, &range, overlay);
2309 if (overlay[GNM_STYLE_BORDER_TOP])
2310 gnm_style_border_unref (overlay[GNM_STYLE_BORDER_TOP]);
2311 if (overlay[GNM_STYLE_BORDER_LEFT])
2312 gnm_style_border_unref (overlay[GNM_STYLE_BORDER_LEFT]);
2313 g_free (overlay);
2316 return xf;
2319 static void
2320 excel_set_xf_segment (ExcelReadSheet *esheet,
2321 int start_col, int end_col,
2322 int start_row, int end_row, unsigned xfidx)
2324 GnmRange range;
2325 GnmStyle *mstyle = excel_get_style_from_xf (esheet,
2326 excel_get_xf (esheet, xfidx));
2328 if (mstyle == NULL)
2329 return;
2331 range.start.col = start_col;
2332 range.start.row = start_row;
2333 range.end.col = end_col;
2334 range.end.row = end_row;
2335 sheet_style_set_range (esheet->sheet, &range, mstyle);
2337 d (3, {
2338 g_printerr ("%s!", esheet->sheet->name_unquoted);
2339 range_dump (&range, "");
2340 g_printerr (" = xf(%d)\n", xfidx);
2344 static GnmStyleBorderType
2345 biff_xf_map_border (int b)
2347 switch (b) {
2348 case 0: /* None */
2349 return GNM_STYLE_BORDER_NONE;
2350 case 1: /* Thin */
2351 return GNM_STYLE_BORDER_THIN;
2352 case 2: /* Medium */
2353 return GNM_STYLE_BORDER_MEDIUM;
2354 case 3: /* Dashed */
2355 return GNM_STYLE_BORDER_DASHED;
2356 case 4: /* Dotted */
2357 return GNM_STYLE_BORDER_DOTTED;
2358 case 5: /* Thick */
2359 return GNM_STYLE_BORDER_THICK;
2360 case 6: /* Double */
2361 return GNM_STYLE_BORDER_DOUBLE;
2362 case 7: /* Hair */
2363 return GNM_STYLE_BORDER_HAIR;
2364 case 8: /* Medium Dashed */
2365 return GNM_STYLE_BORDER_MEDIUM_DASH;
2366 case 9: /* Dash Dot */
2367 return GNM_STYLE_BORDER_DASH_DOT;
2368 case 10: /* Medium Dash Dot */
2369 return GNM_STYLE_BORDER_MEDIUM_DASH_DOT;
2370 case 11: /* Dash Dot Dot */
2371 return GNM_STYLE_BORDER_DASH_DOT_DOT;
2372 case 12: /* Medium Dash Dot Dot */
2373 return GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT;
2374 case 13: /* Slanted Dash Dot*/
2375 return GNM_STYLE_BORDER_SLANTED_DASH_DOT;
2377 g_printerr ("Unknown border style %d\n", b);
2378 return GNM_STYLE_BORDER_NONE;
2381 static int
2382 excel_map_pattern_index_from_excel (int const i)
2384 static int const map_from_excel[] = {
2386 1, 3, 2, 4, 7, 8,
2387 10, 9, 11, 12, 13, 14,
2388 15, 16, 17, 18, 5, 6
2391 /* Default to Solid if out of range */
2392 XL_CHECK_CONDITION_VAL (i >= 0 && i < (int)G_N_ELEMENTS (map_from_excel), 0);
2394 return map_from_excel[i];
2397 static GnmHAlign
2398 halign_from_excel (guint e)
2400 switch (e) {
2401 case 0: return GNM_HALIGN_GENERAL;
2402 case 1: return GNM_HALIGN_LEFT;
2403 case 2: return GNM_HALIGN_CENTER;
2404 case 3: return GNM_HALIGN_RIGHT;
2405 case 4: return GNM_HALIGN_FILL;
2406 case 5: return GNM_HALIGN_JUSTIFY;
2407 case 6: return GNM_HALIGN_CENTER_ACROSS_SELECTION;
2408 case 7: return GNM_HALIGN_DISTRIBUTED;
2410 default:
2411 g_printerr ("Unknown halign %d\n", e);
2412 return GNM_HALIGN_GENERAL;
2416 static GnmVAlign
2417 valign_from_excel (guint e)
2419 switch (e) {
2420 case 0: return GNM_VALIGN_TOP;
2421 case 1: return GNM_VALIGN_CENTER;
2422 case 2: return GNM_VALIGN_BOTTOM;
2423 case 3: return GNM_VALIGN_JUSTIFY;
2424 case 4: return GNM_VALIGN_DISTRIBUTED;
2425 default:
2426 g_printerr ("Unknown valign %d\n", e);
2427 return GNM_VALIGN_TOP;
2431 static int
2432 rotation_from_excel_v8 (guint e)
2434 if (e == 0xff)
2435 return -1;
2436 else if (e > 90)
2437 return 360 + 90 - e;
2438 else
2439 return e;
2442 static int
2443 rotation_from_excel_v7 (guint e)
2445 switch (e) {
2446 default:
2447 case 0: return 0;
2448 case 1: return -1;
2449 case 2: return 90;
2450 case 3: return 270;
2454 static void
2455 excel_read_XF_OLD (BiffQuery *q, GnmXLImporter *importer)
2457 BiffXFData *xf;
2458 guint16 data;
2459 guint8 subdata;
2461 d ( 2, g_printerr ("XF # %d\n", importer->XF_cell_records->len); );
2462 d ( 2, gsf_mem_dump (q->data, q->length); );
2464 XL_CHECK_CONDITION (q->length >= (importer->ver >= MS_BIFF_V3 ? 12 : 4));
2466 xf = g_new0 (BiffXFData, 1);
2467 xf->font_idx = q->data[0];
2468 xf->format_idx = (importer->ver >= MS_BIFF_V3)
2469 ? q->data[1] : (q->data[2] & 0x3f);
2470 xf->style_format = (xf->format_idx > 0)
2471 ? excel_wb_get_fmt (importer, xf->format_idx) : NULL;
2472 xf->is_simple_format = xf->style_format == NULL ||
2473 go_format_is_simple (xf->style_format);
2475 if (importer->ver >= MS_BIFF_V3) {
2476 xf->locked = (q->data[2] & 0x1) != 0;
2477 xf->hidden = (q->data[2] & 0x2) != 0;
2478 xf->xftype = (q->data[2] & 0x4)
2479 ? MS_BIFF_X_STYLE : MS_BIFF_X_CELL;
2480 } else {
2481 xf->locked = (q->data[1] & 0x40) != 0;
2482 xf->hidden = (q->data[1] & 0x80) != 0;
2483 xf->xftype = MS_BIFF_X_CELL;
2485 xf->parentstyle = 0; /* TODO extract for biff 3 and biff4 */
2486 xf->format = MS_BIFF_F_MS;
2487 xf->wrap_text = FALSE;
2488 xf->shrink_to_fit = FALSE;
2491 xf->halign = GNM_HALIGN_GENERAL;
2493 data = (importer->ver >= MS_BIFF_V3) ? q->data[4] : q->data[3];
2494 switch (data & 0x07) {
2495 default :
2496 case 0: xf->halign = GNM_HALIGN_GENERAL; break;
2497 case 1: xf->halign = GNM_HALIGN_LEFT; break;
2498 case 2: xf->halign = GNM_HALIGN_CENTER; break;
2499 case 3: xf->halign = GNM_HALIGN_RIGHT; break;
2500 case 4: xf->halign = GNM_HALIGN_FILL; break;
2501 case 5: xf->halign = GNM_HALIGN_JUSTIFY; break;
2502 case 6: xf->halign = GNM_HALIGN_CENTER_ACROSS_SELECTION; break;
2505 xf->valign = GNM_VALIGN_BOTTOM;
2506 xf->rotation = 0;
2507 xf->indent = 0;
2508 xf->differences = 0;
2509 xf->text_dir = GNM_TEXT_DIR_CONTEXT;
2511 if (importer->ver >= MS_BIFF_V4) {
2512 xf->wrap_text = (data & 0x0008) != 0;
2513 switch (data & 0x30) {
2514 case 0x00: xf->valign = GNM_VALIGN_TOP; break;
2515 case 0x10: xf->valign = GNM_VALIGN_CENTER; break;
2516 default :
2517 case 0x20: xf->valign = GNM_VALIGN_BOTTOM; break;
2519 switch (data & 0xc0) {
2520 case 0x00: xf->rotation = 0; break;
2521 case 0x40: xf->rotation = -1; break;
2522 case 0x80: xf->rotation = 90; break;
2523 case 0xc0: xf->rotation = 270; break;
2525 } else if (importer->ver >= MS_BIFF_V3) {
2526 xf->wrap_text = (data & 0x0008) != 0;
2527 if (xf->wrap_text)
2528 xf->valign = GNM_VALIGN_TOP;
2531 if (importer->ver >= MS_BIFF_V3) {
2532 data = GSF_LE_GET_GUINT16 (q->data + 6);
2533 xf->pat_backgnd_col = (data & 0xf800) >> 11;
2534 if (xf->pat_backgnd_col >= 24)
2535 xf->pat_backgnd_col += 40; /* Defaults */
2536 xf->pat_foregnd_col = (data & 0x07c0) >> 6;
2537 if (xf->pat_foregnd_col >= 24)
2538 xf->pat_foregnd_col += 40; /* Defaults */
2539 xf->fill_pattern_idx =
2540 excel_map_pattern_index_from_excel(data & 0x001f);
2542 data = GSF_LE_GET_GUINT8 (q->data + 10);
2543 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border(data & 0x07);
2544 subdata = data >> 3;
2545 xf->border_color[STYLE_BOTTOM] = (subdata==24) ? 64 : subdata;
2546 data = GSF_LE_GET_GUINT8 (q->data + 8);
2547 xf->border_type[STYLE_TOP] = biff_xf_map_border(data & 0x07);
2548 subdata = data >> 3;
2549 xf->border_color[STYLE_TOP] = (subdata==24) ? 64 : subdata;
2550 data = GSF_LE_GET_GUINT8 (q->data + 9);
2551 xf->border_type[STYLE_LEFT] = biff_xf_map_border(data & 0x07);
2552 subdata = data >> 3;
2553 xf->border_color[STYLE_LEFT] = (subdata==24) ? 64 : subdata;
2554 data = GSF_LE_GET_GUINT8 (q->data + 11);
2555 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (data & 0x07);
2556 subdata = data >> 3;
2557 xf->border_color[STYLE_RIGHT] = (subdata==24) ? 64 : subdata;
2558 } else /* MS_BIFF_V2 */ {
2559 xf->pat_foregnd_col = 0;
2560 xf->pat_backgnd_col = 1;
2562 data = q->data[3];
2563 xf->border_type[STYLE_LEFT] = (data & 0x08) ? 1 : 0;
2564 xf->border_color[STYLE_LEFT] = 0;
2565 xf->border_type[STYLE_RIGHT] = (data & 0x10) ? 1: 0;
2566 xf->border_color[STYLE_RIGHT] = 0;
2567 xf->border_type[STYLE_TOP] = (data & 0x20) ? 1: 0;
2568 xf->border_color[STYLE_TOP] = 0;
2569 xf->border_type[STYLE_BOTTOM] = (data & 0x40) ? 1: 0;
2570 xf->border_color[STYLE_BOTTOM] = 0;
2571 xf->fill_pattern_idx = (data & 0x80) ? 5: 0;
2574 xf->border_type[STYLE_DIAGONAL] = 0;
2575 xf->border_color[STYLE_DIAGONAL] = 0;
2576 xf->border_type[STYLE_REV_DIAGONAL] = 0;
2577 xf->border_color[STYLE_REV_DIAGONAL] = 0;
2579 /* Init the cache */
2580 xf->mstyle = NULL;
2582 g_ptr_array_add (importer->XF_cell_records, xf);
2585 static void
2586 excel_read_XF (BiffQuery *q, GnmXLImporter *importer)
2588 BiffXFData *xf;
2589 guint32 data, subdata;
2591 if (importer->ver >= MS_BIFF_V8)
2592 XL_CHECK_CONDITION (q->length >= 20);
2593 else
2594 XL_CHECK_CONDITION (q->length >= 16);
2596 xf = g_new (BiffXFData, 1);
2597 xf->font_idx = GSF_LE_GET_GUINT16 (q->data);
2598 xf->format_idx = GSF_LE_GET_GUINT16 (q->data + 2);
2599 xf->style_format = (xf->format_idx > 0)
2600 ? excel_wb_get_fmt (importer, xf->format_idx) : NULL;
2601 xf->is_simple_format = xf->style_format == NULL ||
2602 go_format_is_simple (xf->style_format);
2604 data = GSF_LE_GET_GUINT16 (q->data + 4);
2605 xf->locked = (data & 0x0001) != 0;
2606 xf->hidden = (data & 0x0002) != 0;
2607 xf->xftype = (data & 0x0004) ? MS_BIFF_X_STYLE : MS_BIFF_X_CELL;
2608 xf->format = (data & 0x0008) ? MS_BIFF_F_LOTUS : MS_BIFF_F_MS;
2609 xf->parentstyle = (data & 0xfff0) >> 4;
2611 if (xf->xftype == MS_BIFF_X_CELL && xf->parentstyle != 0) {
2612 /* TODO Add support for parent styles
2613 * XL implements a simple form of inheritance with styles.
2614 * If a style's parent changes a value and the child has not
2615 * overridden that value explicitly the child gets updated.
2619 data = GSF_LE_GET_GUINT16 (q->data + 6);
2620 xf->halign = halign_from_excel (data & 0x0007);
2621 xf->wrap_text = (data & 0x0008) != 0;
2622 xf->valign = valign_from_excel ((data & 0x0070) >> 4);
2623 xf->rotation = (importer->ver >= MS_BIFF_V8)
2624 ? rotation_from_excel_v8 (data >> 8)
2625 : rotation_from_excel_v7 ((data >> 8) & 3);
2627 if (importer->ver >= MS_BIFF_V8) {
2628 guint16 const data = GSF_LE_GET_GUINT16 (q->data + 8);
2630 /* FIXME: This code seems irrelevant for merging.
2631 * The undocumented record MERGECELLS appears to be the correct source.
2632 * Nothing seems to set the merge flags.
2634 /* gboolean const merge = (data & 0x20) ? TRUE : FALSE; */
2636 xf->indent = data & 0x0f;
2637 xf->shrink_to_fit = (data & 0x10) ? TRUE : FALSE;
2639 subdata = (data & 0x00C0) >> 10;
2640 switch (subdata) {
2641 default: g_warning ("Unknown text direction %d.", subdata);
2642 /* Fall through. */
2643 case 0: xf->text_dir = GNM_TEXT_DIR_CONTEXT; break;
2644 case 1: xf->text_dir = GNM_TEXT_DIR_LTR; break;
2645 case 2: xf->text_dir = GNM_TEXT_DIR_RTL; break;
2647 } else {
2648 xf->text_dir = GNM_TEXT_DIR_CONTEXT;
2649 xf->shrink_to_fit = FALSE;
2650 xf->indent = 0;
2653 xf->differences = data & 0xFC00;
2655 if (importer->ver >= MS_BIFF_V8) { /* Very different */
2656 int has_diagonals, diagonal_style;
2657 data = GSF_LE_GET_GUINT16 (q->data + 10);
2658 subdata = data;
2659 xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0xf);
2660 subdata = subdata >> 4;
2661 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0xf);
2662 subdata = subdata >> 4;
2663 xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0xf);
2664 subdata = subdata >> 4;
2665 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border (subdata & 0xf);
2666 subdata = subdata >> 4;
2668 data = GSF_LE_GET_GUINT16 (q->data + 12);
2669 subdata = data;
2670 xf->border_color[STYLE_LEFT] = (subdata & 0x7f);
2671 subdata = subdata >> 7;
2672 xf->border_color[STYLE_RIGHT] = (subdata & 0x7f);
2673 subdata = (data & 0xc000) >> 14;
2674 has_diagonals = subdata & 0x3;
2676 data = GSF_LE_GET_GUINT32 (q->data + 14);
2677 subdata = data;
2678 xf->border_color[STYLE_TOP] = (subdata & 0x7f);
2679 subdata = subdata >> 7;
2680 xf->border_color[STYLE_BOTTOM] = (subdata & 0x7f);
2681 subdata = subdata >> 7;
2683 /* Assign the colors whether we have a border or not. We will
2684 * handle that later */
2685 xf->border_color[STYLE_DIAGONAL] =
2686 xf->border_color[STYLE_REV_DIAGONAL] = (subdata & 0x7f);
2688 /* Ok. Now use the flag from above to assign borders */
2689 diagonal_style = biff_xf_map_border (((data & 0x01e00000) >> 21) & 0xf);
2690 xf->border_type[STYLE_DIAGONAL] = (has_diagonals & 0x2)
2691 ? diagonal_style : GNM_STYLE_BORDER_NONE;
2692 xf->border_type[STYLE_REV_DIAGONAL] = (has_diagonals & 0x1)
2693 ? diagonal_style : GNM_STYLE_BORDER_NONE;
2695 xf->fill_pattern_idx =
2696 excel_map_pattern_index_from_excel ((data>>26) & 0x3f);
2698 data = GSF_LE_GET_GUINT16 (q->data + 18);
2699 xf->pat_foregnd_col = (data & 0x007f);
2700 xf->pat_backgnd_col = (data & 0x3f80) >> 7;
2702 d (2, g_printerr ("Color f=0x%x b=0x%x pat=0x%x\n",
2703 xf->pat_foregnd_col,
2704 xf->pat_backgnd_col,
2705 xf->fill_pattern_idx););
2707 } else { /* Biff 7 */
2708 data = GSF_LE_GET_GUINT16 (q->data + 8);
2709 xf->pat_foregnd_col = (data & 0x007f);
2710 /* Documentation is wrong, background color is one bit more
2711 * than documented */
2712 xf->pat_backgnd_col = (data & 0x3f80) >> 7;
2714 data = GSF_LE_GET_GUINT16 (q->data + 10);
2715 xf->fill_pattern_idx =
2716 excel_map_pattern_index_from_excel (data & 0x3f);
2718 d (2, g_printerr ("Color f=0x%x b=0x%x pat=0x%x\n",
2719 xf->pat_foregnd_col,
2720 xf->pat_backgnd_col,
2721 xf->fill_pattern_idx););
2723 /* Luckily this maps nicely onto the new set. */
2724 xf->border_type[STYLE_BOTTOM] = biff_xf_map_border ((data & 0x1c0) >> 6);
2725 xf->border_color[STYLE_BOTTOM] = (data & 0xfe00) >> 9;
2727 data = GSF_LE_GET_GUINT16 (q->data + 12);
2728 subdata = data;
2729 xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0x07);
2730 subdata = subdata >> 3;
2731 xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0x07);
2732 subdata = subdata >> 3;
2733 xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0x07);
2735 subdata = subdata >> 3;
2736 xf->border_color[STYLE_TOP] = subdata;
2738 data = GSF_LE_GET_GUINT16 (q->data + 14);
2739 subdata = data;
2740 xf->border_color[STYLE_LEFT] = (subdata & 0x7f);
2741 subdata = subdata >> 7;
2742 xf->border_color[STYLE_RIGHT] = (subdata & 0x7f);
2744 /* Init the diagonals which were not availabile in Biff7 */
2745 xf->border_type[STYLE_DIAGONAL] =
2746 xf->border_type[STYLE_REV_DIAGONAL] = 0;
2747 xf->border_color[STYLE_DIAGONAL] =
2748 xf->border_color[STYLE_REV_DIAGONAL] = 127;
2751 /* Init the cache */
2752 xf->mstyle = NULL;
2754 g_ptr_array_add (importer->XF_cell_records, xf);
2755 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",
2756 importer->XF_cell_records->len - 1,
2757 xf->is_simple_format, xf->locked, xf->hidden, xf->format,
2758 xf->xftype,
2759 xf->font_idx,
2760 xf->format_idx,
2761 xf->pat_foregnd_col,
2762 xf->pat_backgnd_col,
2763 xf->fill_pattern_idx););
2766 static void
2767 excel_read_XF_INDEX (BiffQuery *q, ExcelReadSheet *esheet)
2769 XL_CHECK_CONDITION (q->length >= 2);
2770 esheet->biff2_prev_xf_index = GSF_LE_GET_GUINT16 (q->data);
2773 static void
2774 biff_xf_data_destroy (BiffXFData *xf)
2776 go_format_unref (xf->style_format);
2777 xf->style_format = NULL;
2779 if (xf->mstyle) {
2780 gnm_style_unref (xf->mstyle);
2781 xf->mstyle = NULL;
2783 g_free (xf);
2786 static GnmExprTop const *
2787 excel_formula_shared (BiffQuery *q, ExcelReadSheet *esheet, GnmCell *cell)
2789 guint16 opcode, data_len, data_ofs, array_data_len;
2790 GnmRange r;
2791 gboolean is_array;
2792 GnmExprTop const *texpr;
2793 guint8 const *data;
2795 if (!ms_biff_query_peek_next (q, &opcode) ||
2796 !(opcode == BIFF_SHRFMLA ||
2797 opcode == BIFF_ARRAY_v0 || opcode == BIFF_ARRAY_v2 ||
2798 opcode == BIFF_TABLE_v0 || opcode == BIFF_TABLE_v2)) {
2799 g_warning ("EXCEL: unexpected record '0x%x' after a formula in '%s'.",
2800 opcode, cell_name (cell));
2801 return NULL;
2803 ms_biff_query_next (q);
2805 XL_CHECK_CONDITION_VAL (q->length >= 6, NULL);
2806 xls_read_range8 (&r, q->data);
2808 if (opcode == BIFF_TABLE_v0 || opcode == BIFF_TABLE_v2) {
2809 XLDataTable *dt;
2810 GnmExprList *args = NULL;
2811 GnmCellRef ref;
2812 guint16 flags;
2814 XL_CHECK_CONDITION_VAL (q->length >= 16, NULL);
2816 flags = GSF_LE_GET_GUINT16 (q->data + 6);
2818 d (2, { range_dump (&r, " <-- contains data table\n");
2819 gsf_mem_dump (q->data, q->length); });
2821 dt = g_new0 (XLDataTable, 1);
2822 dt->table = r;
2823 dt->c_in.row = GSF_LE_GET_GUINT16 (q->data + 8);
2824 dt->c_in.col = GSF_LE_GET_GUINT16 (q->data + 10);
2825 dt->r_in.row = GSF_LE_GET_GUINT16 (q->data + 12);
2826 dt->r_in.col = GSF_LE_GET_GUINT16 (q->data + 14);
2827 g_hash_table_replace (esheet->tables, &dt->table.start, dt);
2829 args = gnm_expr_list_append
2830 (args,
2831 gnm_expr_new_cellref
2832 (gnm_cellref_init (&ref, NULL,
2833 dt->c_in.col - r.start.col,
2834 dt->c_in.row - r.start.row, TRUE)));
2835 if (flags & 0x8) {
2836 args = gnm_expr_list_append
2837 (args,
2838 gnm_expr_new_cellref
2839 (gnm_cellref_init (&ref, NULL,
2840 dt->r_in.col - r.start.col,
2841 dt->r_in.row - r.start.row, TRUE)));
2842 } else {
2843 GnmExpr const *missing = gnm_expr_new_constant (value_new_empty ());
2844 args = (flags & 4)
2845 ? gnm_expr_list_append (args, missing)
2846 : gnm_expr_list_prepend (args, missing);
2848 texpr = gnm_expr_top_new (gnm_expr_new_funcall (gnm_func_lookup ("table", NULL), args));
2849 gnm_cell_set_array (esheet->sheet, &r, texpr);
2850 gnm_expr_top_unref (texpr);
2851 return NULL;
2854 d (2, range_dump (&r, " <-- contains a shared formula\n"););
2856 is_array = (q->opcode != BIFF_SHRFMLA);
2858 data_ofs = (esheet_ver (esheet) > MS_BIFF_V4 && is_array) ? 14 : 10;
2859 XL_CHECK_CONDITION_VAL (q->length >= data_ofs, NULL);
2860 data = q->data + data_ofs;
2861 data_len = GSF_LE_GET_GUINT16 (q->data + (data_ofs - 2));
2862 XL_CHECK_CONDITION_VAL (q->length >= data_ofs + data_len, NULL);
2863 array_data_len = data_len > 0 ? q->length - (data_ofs + data_len) : 0;
2865 texpr = excel_parse_formula (&esheet->container, esheet,
2866 r.start.col, r.start.row,
2867 data, data_len, array_data_len,
2868 !is_array, NULL);
2870 if (g_hash_table_lookup (esheet->shared_formulae, &cell->pos)) {
2871 g_printerr ("Duplicate shared formula for cell %s\n", cell_name (cell));
2872 } else {
2873 XLSharedFormula *sf = g_new (XLSharedFormula, 1);
2875 /* WARNING: Do NOT use the upper left corner as the hashkey.
2876 * For some bizzare reason XL appears to sometimes not
2877 * flag the formula as shared until later.
2878 * Use the location of the cell we are reading as the key.
2880 sf->key = cell->pos;
2881 sf->is_array = is_array;
2882 sf->data = data_len > 0 ? g_memdup (data, data_len + array_data_len) : NULL;
2883 sf->data_len = data_len;
2884 sf->array_data_len = array_data_len;
2885 sf->being_parsed = FALSE;
2887 d (1, g_printerr ("Shared formula, extent %s\n", range_as_string (&r)););
2889 g_hash_table_insert (esheet->shared_formulae, &sf->key, sf);
2892 g_return_val_if_fail (texpr != NULL, NULL);
2894 if (is_array) {
2895 gnm_cell_set_array (esheet->sheet, &r, texpr);
2896 gnm_expr_top_unref (texpr);
2897 return NULL;
2898 } else
2899 return texpr;
2903 * NOTE: There must be _no_ path through this function that does not set the
2904 * cell value.
2906 static void
2907 excel_read_FORMULA (BiffQuery *q, ExcelReadSheet *esheet)
2909 /* Pre-retrieve incase this is a string */
2910 gboolean array_elem, is_string = FALSE;
2911 guint16 col, row, options;
2912 guint16 expr_length;
2913 guint offset;
2914 guint8 const *val_dat = q->data + 6;
2915 GnmExprTop const *texpr;
2916 GnmCell *cell;
2917 GnmValue *val = NULL;
2919 XL_CHECK_CONDITION (q->length >= 16);
2920 col = XL_GETCOL (q);
2921 row = XL_GETROW (q);
2922 options = GSF_LE_GET_GUINT16 (q->data + 14);
2924 excel_set_xf (esheet, q);
2926 cell = excel_cell_fetch (q, esheet);
2927 if (!cell)
2928 return;
2930 /* TODO TODO TODO: Wishlist
2931 * We should make an array of minimum sizes for each BIFF type
2932 * and have this checking done there.
2934 if (esheet_ver (esheet) >= MS_BIFF_V5) {
2935 XL_CHECK_CONDITION (q->length >= 22);
2936 expr_length = GSF_LE_GET_GUINT16 (q->data + 20);
2937 offset = 22;
2939 /* TODO : it would be nice to figure out how to allocate recalc tags.
2940 * that would avoid the scary
2941 * 'this file was calculated with a different version of XL'
2942 * warning when exiting without changing. */
2943 d (1, g_printerr ("Formula in %s!%s has recalc tag 0x%x;\n",
2944 esheet->sheet->name_quoted, cell_name (cell),
2945 GSF_LE_GET_GUINT32 (q->data + 16)););
2947 } else if (esheet_ver (esheet) >= MS_BIFF_V3) {
2948 XL_CHECK_CONDITION (q->length >= 18);
2949 expr_length = GSF_LE_GET_GUINT16 (q->data + 16);
2950 offset = 18;
2951 } else {
2952 XL_CHECK_CONDITION (q->length >= 17);
2953 expr_length = GSF_LE_GET_GUINT8 (q->data + 16);
2954 offset = 17;
2955 val_dat++; /* compensate for the 3 byte style */
2957 XL_CHECK_CONDITION (q->length >= offset + expr_length);
2959 /* Get the current value so that we can format, do this BEFORE handling
2960 * shared/array formulas or strings in case we need to go to the next
2961 * record */
2962 if (GSF_LE_GET_GUINT16 (val_dat + 6) != 0xffff) {
2963 val = value_new_float (gsf_le_get_double (val_dat));
2964 } else {
2965 guint8 const val_type = GSF_LE_GET_GUINT8 (val_dat);
2966 switch (val_type) {
2967 case 0: is_string = TRUE; break;
2968 case 1: val = value_new_bool (GSF_LE_GET_GUINT8 (val_dat + 2) != 0);
2969 break;
2970 case 2: val = xls_value_new_err (NULL, GSF_LE_GET_GUINT8 (val_dat + 2));
2971 break;
2972 case 3: val = value_new_empty (); /* Empty (Undocumented) */
2973 break;
2974 default:
2975 g_printerr ("Unknown type (%x) for cell's (%s) current val\n",
2976 val_type, cell_name (cell));
2980 texpr = excel_parse_formula (&esheet->container, esheet, col, row,
2981 q->data + offset, expr_length,
2982 q->length - (offset + expr_length),
2983 FALSE, &array_elem);
2984 #if 0
2985 /* dump the trailing array and natural language data */
2986 gsf_mem_dump (q->data + offset + expr_length,
2987 q->length - offset - expr_length);
2988 #endif
2990 /* Error was flaged by parse_formula */
2991 if (texpr == NULL && !array_elem)
2992 texpr = excel_formula_shared (q, esheet, cell);
2994 if (is_string) {
2995 guint16 opcode;
2996 if (ms_biff_query_peek_next (q, &opcode) &&
2997 (opcode == BIFF_STRING_v0 || opcode == BIFF_STRING_v2)) {
2998 char *v = NULL;
2999 if (ms_biff_query_next (q)) {
3001 * NOTE: the Excel developers kit docs are
3002 * WRONG. There is an article that
3003 * clarifies the behaviour to be the std
3004 * unicode format rather than the pure
3005 * length version the docs describe.
3007 * NOTE : Apparently some apps actually store a
3008 * 0 length string record for an empty.
3009 * DAMN! this was us! we were screwing
3010 * up when exporting ""
3012 guint16 const len = (q->length >= 2) ? GSF_LE_GET_GUINT16 (q->data) : 0;
3014 if (len > 0)
3015 v = excel_biff_text (esheet->container.importer, q, 2, len);
3016 else
3018 * Pre-Biff8 seems to use len=0
3019 * Should that be a string or an EMPTY?
3021 v = g_strdup ("");
3023 if (v) {
3024 val = value_new_string_nocopy (v);
3025 } else {
3026 GnmEvalPos ep;
3027 val = value_new_error (eval_pos_init_cell (&ep, cell),
3028 "INVALID STRING");
3029 g_warning ("EXCEL: invalid STRING record in %s",
3030 cell_name (cell));
3032 } else {
3033 /* There should be a STRING record here */
3034 GnmEvalPos ep;
3035 val = value_new_error (eval_pos_init_cell (&ep, cell),
3036 "MISSING STRING");
3037 g_warning ("EXCEL: missing STRING record for %s",
3038 cell_name (cell));
3042 /* We MUST have a value */
3043 if (val == NULL) {
3044 GnmEvalPos ep;
3045 val = value_new_error (eval_pos_init_cell (&ep, cell),
3046 "MISSING Value");
3047 g_warning ("EXCEL: Invalid state. Missing Value in %s?",
3048 cell_name (cell));
3051 if (gnm_cell_is_array (cell)) {
3052 /* Array expressions were already stored in the cells (without
3053 * recalc), and without a value. Handle either the first
3054 * instance or the followers.
3056 gnm_cell_assign_value (cell, val);
3057 } else if (!gnm_cell_has_expr (cell)) {
3058 /* Just in case things screwed up, at least save the value */
3059 if (texpr != NULL) {
3060 gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
3061 gnm_expr_top_unref (texpr);
3062 } else
3063 gnm_cell_assign_value (cell, val);
3064 } else {
3066 * NOTE: Only the expression is screwed.
3067 * The value and format can still be set.
3069 g_warning ("EXCEL: Multiple expressions for cell %s!%s",
3070 esheet->sheet->name_quoted, cell_name (cell));
3071 gnm_cell_set_value (cell, val);
3072 if (texpr)
3073 gnm_expr_top_unref (texpr);
3077 * 0x1 = AlwaysCalc
3078 * 0x2 = CalcOnLoad
3080 if (options & 0x3)
3081 cell_queue_recalc (cell);
3084 XLSharedFormula *
3085 excel_sheet_shared_formula (ExcelReadSheet const *esheet,
3086 GnmCellPos const *key)
3088 g_return_val_if_fail (esheet != NULL, NULL);
3090 d (5, g_printerr ("FIND SHARED: %s\n", cellpos_as_string (key)););
3092 return g_hash_table_lookup (esheet->shared_formulae, key);
3095 XLDataTable *
3096 excel_sheet_data_table (ExcelReadSheet const *esheet,
3097 GnmCellPos const *key)
3099 g_return_val_if_fail (esheet != NULL, NULL);
3101 d (5, g_printerr ("FIND DATA TABLE: %s\n", cellpos_as_string (key)););
3103 return g_hash_table_lookup (esheet->tables, key);
3106 static void
3107 excel_sheet_insert_val (ExcelReadSheet *esheet, BiffQuery *q, GnmValue *v)
3109 GnmCell *cell = excel_cell_fetch (q, esheet);
3111 if (cell) {
3112 (void)excel_set_xf (esheet, q);
3113 gnm_cell_set_value (cell, v);
3114 } else
3115 value_release (v);
3118 static void
3119 excel_read_NOTE (BiffQuery *q, ExcelReadSheet *esheet)
3121 GnmCellPos pos;
3122 Sheet *sheet = esheet->sheet;
3123 guint16 row, col;
3125 XL_CHECK_CONDITION (q->length >= 4);
3127 row = XL_GETROW (q);
3128 col = XL_GETCOL (q);
3129 XL_CHECK_CONDITION (col < gnm_sheet_get_max_cols (sheet));
3130 XL_CHECK_CONDITION (row < gnm_sheet_get_max_rows (sheet));
3131 pos.row = XL_GETROW (q);
3132 pos.col = XL_GETCOL (q);
3134 if (esheet_ver (esheet) >= MS_BIFF_V8) {
3135 guint16 options, obj_id;
3136 gboolean hidden;
3137 MSObj *obj;
3138 char *author;
3140 XL_CHECK_CONDITION (q->length >= 8);
3141 options = GSF_LE_GET_GUINT16 (q->data + 4);
3142 hidden = (options & 0x2)==0;
3143 obj_id = GSF_LE_GET_GUINT16 (q->data + 6);
3145 /* Docs claim that only 0x2 is valid, all other flags should
3146 * be 0 but we have seen examples with 0x100 'pusiuhendused juuli 2003.xls'
3148 * docs mention 0x002 == hidden
3149 * real life adds 0x080 == ???
3150 * real life adds 0x100 == no indicator visible ??? */
3151 if (options & 0xe7d)
3152 g_warning ("unknown flag on NOTE record %hx", options);
3154 author = excel_biff_text_2 (esheet->container.importer, q, 8);
3155 d (1, g_printerr ("Comment at %s%d id %d options"
3156 " 0x%x hidden %d by '%s'\n",
3157 col_name (pos.col), pos.row + 1,
3158 obj_id, options, hidden, author););
3160 obj = ms_container_get_obj (&esheet->container, obj_id);
3161 if (obj != NULL) {
3162 cell_comment_author_set (GNM_CELL_COMMENT (obj->gnum_obj), author);
3163 obj->comment_pos = pos;
3164 } else {
3165 /* hmm, how did this happen ? we should have seen
3166 * some escher records earlier */
3167 cell_set_comment (sheet, &pos, author, NULL, NULL);
3169 g_free (author);
3170 } else {
3171 guint len;
3172 GString *comment;
3174 XL_CHECK_CONDITION (q->length >= 6);
3175 len = GSF_LE_GET_GUINT16 (q->data + 4);
3176 comment = g_string_sized_new (len);
3178 for (; len > 2048 ; len -= 2048) {
3179 guint16 opcode;
3181 g_string_append (comment,
3182 excel_biff_text (esheet->container.importer,
3183 q, 6, 2048));
3185 if (!ms_biff_query_peek_next (q, &opcode) ||
3186 opcode != BIFF_NOTE || !ms_biff_query_next (q) ||
3187 q->length < 4 ||
3188 XL_GETROW (q) != 0xffff || XL_GETCOL (q) != 0) {
3189 g_warning ("Invalid Comment record");
3190 g_string_free (comment, TRUE);
3191 return;
3194 g_string_append (comment, excel_biff_text (esheet->container.importer, q, 6, len));
3196 d (2, g_printerr ("Comment in %s%d: '%s'\n",
3197 col_name (pos.col), pos.row + 1, comment->str););
3199 cell_set_comment (sheet, &pos, NULL, comment->str, NULL);
3200 g_string_free (comment, TRUE);
3204 static void
3205 excel_sheet_destroy (ExcelReadSheet *esheet)
3207 if (esheet == NULL)
3208 return;
3210 if (esheet->shared_formulae != NULL) {
3211 g_hash_table_destroy (esheet->shared_formulae);
3212 esheet->shared_formulae = NULL;
3214 if (esheet->tables != NULL) {
3215 g_hash_table_destroy (esheet->tables);
3216 esheet->tables = NULL;
3219 /* There appear to be workbooks like guai.xls that have a filter NAME
3220 * defined but no visible combos, so we remove a filter if it has no
3221 * objects */
3222 if (esheet->filter != NULL) {
3223 gnm_filter_remove (esheet->filter);
3224 gnm_filter_unref (esheet->filter);
3225 esheet->filter = NULL;
3228 ms_container_finalize (&esheet->container);
3230 g_free (esheet);
3233 static GnmExprTop const *
3234 ms_wb_parse_expr (MSContainer *container, guint8 const *data, int length)
3236 ExcelReadSheet dummy_sheet;
3237 memset (&dummy_sheet, 0, sizeof (dummy_sheet));
3238 dummy_sheet.container.importer = (GnmXLImporter *)container;
3239 return ms_sheet_parse_expr_internal (&dummy_sheet, data, length);
3242 static GOFormat *
3243 ms_wb_get_fmt (MSContainer const *container, unsigned indx)
3245 return excel_wb_get_fmt (((GnmXLImporter *)container), indx);
3248 static void
3249 add_attr (PangoAttrList *attr_list, PangoAttribute *attr)
3251 attr->start_index = 0;
3252 attr->end_index = 0;
3253 pango_attr_list_insert (attr_list, attr);
3256 static PangoAttrList *
3257 ms_wb_get_font_markup (MSContainer const *c, unsigned indx)
3259 GnmXLImporter *importer = (GnmXLImporter *)c;
3260 ExcelFont const *fd = excel_font_get (importer, indx);
3262 if (fd == NULL || indx == 0)
3263 return empty_attr_list;
3265 if (fd->attrs == NULL) {
3266 ExcelFont const *fd0 = excel_font_get (importer, 0);
3267 PangoAttrList *attrs;
3269 attrs = pango_attr_list_new ();
3270 if (strcmp (fd->fontname, fd0->fontname) != 0)
3271 add_attr (attrs, pango_attr_family_new (fd->fontname));
3272 if (fd->height != fd0->height)
3273 add_attr (attrs, pango_attr_size_new (fd->height * PANGO_SCALE / 20));
3274 if (fd->boldness != fd0->boldness)
3275 add_attr (attrs, pango_attr_weight_new (fd->boldness));
3276 if (fd->italic != fd0->italic)
3277 add_attr (attrs, pango_attr_style_new (fd->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
3278 if (fd->struck_out != fd0->struck_out)
3279 add_attr (attrs, pango_attr_strikethrough_new (fd->struck_out));
3280 if (fd->underline != fd0->underline) {
3281 PangoUnderline underline = gnm_translate_underline_to_pango
3282 (xls_uline_to_gnm_underline (fd->underline));
3283 add_attr (attrs, pango_attr_underline_new (underline));
3286 switch (fd->script) {
3287 case GO_FONT_SCRIPT_SUB:
3288 add_attr (attrs, go_pango_attr_subscript_new (TRUE));
3289 break;
3290 default:
3291 case GO_FONT_SCRIPT_STANDARD:
3292 /* Just assume fd0 is standard. */
3293 break;
3294 case GO_FONT_SCRIPT_SUPER:
3295 add_attr (attrs, go_pango_attr_superscript_new (TRUE));
3296 break;
3299 if (fd->color_idx != fd0->color_idx) {
3300 GnmColor *color = (fd->color_idx == 127)
3301 ? style_color_black ()
3302 : excel_palette_get (importer, fd->color_idx);
3303 add_attr (attrs, go_color_to_pango (color->go_color, TRUE));
3304 style_color_unref (color);
3307 ((ExcelFont *)fd)->attrs = attrs;
3310 return fd->attrs;
3313 static gint
3314 gnm_xl_get_codepage (char const *enc)
3316 /* These names must match charset_trans_array in go-charmap-sel.c */
3317 static struct {
3318 char const *name;
3319 gint codepage;
3320 } charset_trans_array[] = {
3321 {"IBM864", 0},
3322 {"IBM864i", 0},
3323 {"ISO-8859-6", 0},
3324 {"ISO-8859-6-E", 0},
3325 {"ISO-8859-6-I", 0},
3326 {"x-mac-arabic", 0},
3327 {"windows-1256", 1256},
3328 {"armscii-8", 0},
3329 {"ISO-8859-13", 0},
3330 {"ISO-8859-4", 0},
3331 {"windows-1257", 1257},
3332 {"ISO-8859-14", 0},
3333 {"IBM852", 0},
3334 {"ISO-8859-2", 0},
3335 {"x-mac-ce", 0},
3336 {"windows-1250", 1250},
3337 {"gb18030", 0},
3338 {"GB2312", 0},
3339 {"x-gbk", 0},
3340 {"HZ-GB-2312", 0},
3341 {"windows-936", 936},
3342 {"Big5", 0},
3343 {"Big5-HKSCS", 0},
3344 {"x-euc-tw", 0},
3345 {"x-mac-croatian", 0},
3346 {"IBM855", 0},
3347 {"ISO-8859-5", 0},
3348 {"ISO-IR-111", 0},
3349 {"KOI8-R", 0},
3350 {"x-mac-cyrillic", 0},
3351 {"windows-1251", 1251},
3352 {"IBM866", 0},
3353 {"KOI8-U", 0},
3354 {"x-mac-ukrainian", 0},
3355 {"ANSI_X3.4-1968#ASCII", 0},
3356 {"x-mac-farsi", 0},
3357 {"geostd8", 0},
3358 {"ISO-8859-7", 0},
3359 {"x-mac-greek", 0},
3360 {"windows-1253", 0},
3361 {"x-mac-gujarati", 0},
3362 {"x-mac-gurmukhi", 0},
3363 {"IBM862", 0},
3364 {"ISO-8859-8-E", 0},
3365 {"ISO-8859-8-I", 0},
3366 {"x-mac-hebrew", 0},
3367 {"windows-1255", 1255},
3368 {"x-mac-devanagari", 0},
3369 {"x-mac-icelandic", 0},
3370 {"EUC-JP", 0},
3371 {"ISO-2022-JP", 0},
3372 {"CP932", 0},
3373 {"EUC-KR", 0},
3374 {"ISO-2022-KR", 0},
3375 {"x-johab", 0},
3376 {"x-windows-949", 0},
3377 {"ISO-8859-10", 0},
3378 {"x-mac-romanian", 0},
3379 {"ISO-8859-16", 0},
3380 {"ISO-8859-3", 0},
3381 {"TIS-620", 0},
3382 {"IBM857", 0},
3383 {"ISO-8859-9", 0},
3384 {"x-mac-turkish", 0},
3385 {"windows-1254", 1254},
3386 {"UTF-7", 0},
3387 {"UTF-8", 0},
3388 {"UTF-16BE", 0},
3389 {"UTF-16LE", 0},
3390 {"UTF-32BE", 0},
3391 {"UTF-32LE", 0},
3392 {"x-user-defined", 0},
3393 {"x-viet-tcvn5712", 0},
3394 {"VISCII", 0},
3395 {"x-viet-vps", 0},
3396 {"windows-1258", 1258},
3397 {"ISO-8859-8", 0},
3398 {"IBM850", 0},
3399 {"ISO-8859-1", 0},
3400 {"ISO-8859-15", 0},
3401 {"x-mac-roman", 0},
3402 {"windows-1252", 1252},
3403 {"T61.8bit", 0},
3404 {"x-imap4-modified-utf7", 0},
3405 {"x-u-escaped", 0}
3407 guint i;
3409 if (enc == NULL)
3410 return 0;
3412 for (i = 0; i < G_N_ELEMENTS(charset_trans_array); i++)
3413 if (0 == strcmp (enc, charset_trans_array[i].name))
3414 return charset_trans_array[i].codepage;
3416 return 0;
3419 static GnmXLImporter *
3420 gnm_xl_importer_new (GOIOContext *context, WorkbookView *wb_view, char const *opt_enc)
3422 static MSContainerClass const vtbl = {
3423 NULL, NULL,
3424 &ms_wb_parse_expr,
3425 NULL,
3426 &ms_wb_get_fmt,
3427 &ms_wb_get_font_markup
3430 GnmXLImporter *importer = g_new (GnmXLImporter, 1);
3432 importer->ver = MS_BIFF_V_UNKNOWN;
3433 importer->context = context;
3434 importer->wbv = wb_view;
3435 importer->wb = wb_view_get_workbook (wb_view);
3436 importer->str_iconv = (GIConv)(-1);
3437 importer->codepage_override = gnm_xl_get_codepage (opt_enc);
3438 gnm_xl_importer_set_codepage (importer, (importer->codepage_override > 0) ?
3439 importer->codepage_override : 1252); /* set a default */
3441 importer->expr_sharer = gnm_expr_sharer_new ();
3442 importer->v8.supbook = g_array_new (FALSE, FALSE, sizeof (ExcelSupBook));
3443 importer->v8.externsheet = NULL;
3445 importer->names = g_ptr_array_new ();
3446 importer->num_name_records = 0;
3448 importer->boundsheet_sheet_by_index = g_ptr_array_new ();
3449 importer->boundsheet_data_by_stream = g_hash_table_new_full (
3450 g_direct_hash, g_direct_equal,
3451 NULL, (GDestroyNotify) biff_boundsheet_data_destroy);
3452 importer->font_data = g_hash_table_new_full (
3453 g_direct_hash, g_direct_equal,
3454 NULL, (GDestroyNotify)excel_font_free);
3455 importer->excel_sheets = g_ptr_array_new ();
3456 importer->XF_cell_records = g_ptr_array_new ();
3457 importer->pivot.cache_by_index = g_ptr_array_new ();
3458 importer->pivot.slicer = NULL;
3459 importer->pivot.field_count = 0;
3460 importer->pivot.record_count = 0;
3461 importer->format_table = g_hash_table_new_full (
3462 g_direct_hash, g_direct_equal,
3463 NULL, (GDestroyNotify)biff_format_data_destroy);
3464 importer->palette = NULL;
3465 importer->sst = NULL;
3466 importer->sst_len = 0;
3468 ms_container_init (&importer->container, &vtbl, NULL, importer);
3469 return importer;
3472 static void
3473 excel_workbook_reset_style (GnmXLImporter *importer)
3475 unsigned i;
3477 g_hash_table_destroy (importer->font_data);
3478 importer->font_data = g_hash_table_new_full (
3479 g_direct_hash, g_direct_equal,
3480 NULL, (GDestroyNotify)excel_font_free);
3482 for (i = 0; i < importer->XF_cell_records->len; i++)
3483 biff_xf_data_destroy (g_ptr_array_index (importer->XF_cell_records, i));
3484 g_ptr_array_free (importer->XF_cell_records, TRUE);
3485 importer->XF_cell_records = g_ptr_array_new ();
3487 g_hash_table_destroy (importer->format_table);
3488 importer->format_table = g_hash_table_new_full (
3489 g_direct_hash, g_direct_equal,
3490 NULL, (GDestroyNotify)biff_format_data_destroy);
3493 static void
3494 gnm_xl_importer_free (GnmXLImporter *importer)
3496 unsigned i, j;
3497 GSList *real_order = NULL;
3498 Sheet *sheet;
3500 for (i = importer->boundsheet_sheet_by_index->len; i-- > 0 ; ) {
3501 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, i);
3502 if (sheet != NULL)
3503 real_order = g_slist_prepend (real_order, sheet);
3506 if (real_order != NULL) {
3507 workbook_sheet_reorder (importer->wb, real_order);
3508 g_slist_free (real_order);
3511 gnm_expr_sharer_destroy (importer->expr_sharer);
3513 g_hash_table_destroy (importer->boundsheet_data_by_stream);
3514 importer->boundsheet_data_by_stream = NULL;
3515 g_ptr_array_free (importer->boundsheet_sheet_by_index, TRUE);
3516 importer->boundsheet_sheet_by_index = NULL;
3518 for (i = 0; i < importer->excel_sheets->len; i++)
3519 excel_sheet_destroy (g_ptr_array_index (importer->excel_sheets, i));
3520 g_ptr_array_free (importer->excel_sheets, TRUE);
3521 importer->excel_sheets = NULL;
3523 if (NULL != importer->pivot.slicer) {
3524 g_object_unref (importer->pivot.slicer);
3525 importer->pivot.slicer = NULL;
3527 for (i = 0; i < importer->pivot.cache_by_index->len; i++) {
3528 GObject *cache = g_ptr_array_index (importer->pivot.cache_by_index, i);
3529 if (NULL != cache)
3530 g_object_unref (cache);
3532 g_ptr_array_free (importer->pivot.cache_by_index, TRUE);
3533 importer->pivot.cache_by_index = NULL;
3535 for (i = 0; i < importer->XF_cell_records->len; i++)
3536 biff_xf_data_destroy (g_ptr_array_index (importer->XF_cell_records, i));
3537 g_ptr_array_free (importer->XF_cell_records, TRUE);
3538 importer->XF_cell_records = NULL;
3540 g_hash_table_destroy (importer->font_data);
3541 importer->font_data = NULL;
3543 g_hash_table_destroy (importer->format_table);
3544 importer->format_table = NULL;
3546 if (importer->palette) {
3547 excel_palette_destroy (importer->palette);
3548 importer->palette = NULL;
3551 for (i = 0; i < importer->v8.supbook->len; i++ ) {
3552 ExcelSupBook *sup = &(g_array_index (importer->v8.supbook,
3553 ExcelSupBook, i));
3554 for (j = 0; j < sup->externname->len; j++ ) {
3555 GnmNamedExpr *nexpr = g_ptr_array_index (sup->externname, j);
3556 if (nexpr)
3557 expr_name_unref (nexpr);
3559 g_ptr_array_free (sup->externname, TRUE);
3561 g_array_free (importer->v8.supbook, TRUE);
3562 importer->v8.supbook = NULL;
3563 if (importer->v8.externsheet != NULL) {
3564 g_array_free (importer->v8.externsheet, TRUE);
3565 importer->v8.externsheet = NULL;
3568 if (importer->sst != NULL) {
3569 unsigned i = importer->sst_len;
3570 while (i-- > 0) {
3571 if (importer->sst[i].content)
3572 go_string_unref (importer->sst[i].content);
3573 go_format_unref (importer->sst[i].markup);
3575 g_free (importer->sst);
3578 for (i = importer->names->len; i-- > 0 ; ) {
3579 GnmNamedExpr *nexpr = g_ptr_array_index (importer->names, i);
3580 if (!nexpr)
3581 continue;
3583 /* NAME placeholders need removal, EXTERNNAME
3584 * placeholders will no be active */
3585 if (expr_name_is_active (nexpr) &&
3586 expr_name_is_placeholder (nexpr) &&
3587 /* FIXME: Why do we need this? */
3588 nexpr->ref_count == 2) {
3589 d (1, g_printerr ("Removing name %s\n", expr_name_name (nexpr)););
3590 expr_name_remove (nexpr);
3592 expr_name_unref (nexpr);
3594 g_ptr_array_free (importer->names, TRUE);
3595 importer->names = NULL;
3597 if (importer->str_iconv != (GIConv)(-1)) {
3598 gsf_iconv_close (importer->str_iconv);
3599 importer->str_iconv = (GIConv)(-1);
3602 ms_container_finalize (&importer->container);
3603 g_free (importer);
3607 * Unpacks a MS Excel RK structure,
3609 static GnmValue *
3610 biff_get_rk (guint8 const *ptr)
3612 gint32 number;
3613 enum eType {
3614 eIEEE = 0, eIEEEx100 = 1, eInt = 2, eIntx100 = 3
3615 } type;
3617 number = GSF_LE_GET_GUINT32 (ptr);
3618 type = (number & 0x3);
3619 switch (type) {
3620 case eIEEE:
3621 case eIEEEx100:
3623 guint8 tmp[8];
3624 gnm_float answer;
3625 int lp;
3627 /* Think carefully about big/little endian issues before
3628 changing this code. */
3629 for (lp = 0; lp < 4; lp++) {
3630 tmp[lp + 4]= (lp > 0) ? ptr[lp]: (ptr[lp] & 0xfc);
3631 tmp[lp] = 0;
3634 answer = (gnm_float)gsf_le_get_double (tmp);
3635 return value_new_float (type == eIEEEx100 ? answer / 100 : answer);
3637 case eInt:
3638 return value_new_int (number >> 2);
3639 case eIntx100:
3640 number >>= 2;
3641 if ((number % 100) == 0)
3642 return value_new_int (number / 100);
3643 else
3644 return value_new_float ((gnm_float)number / 100);
3646 while (1) abort ();
3649 static char const *
3650 excel_builtin_name (guint8 const *ptr)
3652 switch (*ptr) {
3653 case 0x00: return "Consolidate_Area";
3654 case 0x01: return "Auto_Open";
3655 case 0x02: return "Auto_Close";
3656 case 0x03: return "Extract";
3657 case 0x04: return "Database";
3658 case 0x05: return "Criteria";
3659 case 0x06: return "Print_Area";
3660 case 0x07: return "Print_Titles";
3661 case 0x08: return "Recorder";
3662 case 0x09: return "Data_Form";
3663 case 0x0A: return "Auto_Activate";
3664 case 0x0B: return "Auto_Deactivate";
3665 case 0x0C: return "Sheet_Title";
3666 case 0x0D: return "_FilterDatabase";
3668 default:
3669 g_warning ("Unknown builtin named expression %d", (int)*ptr);
3671 return NULL;
3674 static GnmNamedExpr *
3675 excel_parse_name (GnmXLImporter *importer, Sheet *sheet, char *name,
3676 guint8 const *expr_data, unsigned expr_len,
3677 unsigned array_data_len,
3678 gboolean link_to_container,
3679 GnmNamedExpr *stub)
3681 GnmParsePos pp;
3682 GnmNamedExpr *nexpr;
3683 GnmExprTop const *texpr = NULL;
3684 char *err = NULL;
3686 g_return_val_if_fail (name != NULL, NULL);
3688 parse_pos_init (&pp, importer->wb, sheet, 0, 0);
3690 if (expr_len == 0) {
3691 /* This seems to indicate a placeholder for an unknown name */
3692 texpr = gnm_expr_top_new_constant (value_new_error_NAME (NULL));
3693 } else {
3694 texpr = excel_parse_formula (&importer->container, NULL, 0, 0,
3695 expr_data, expr_len,
3696 array_data_len,
3697 TRUE, NULL);
3699 if (texpr == NULL) {
3700 go_io_warning (importer->context, _("Failure parsing name '%s'"), name);
3701 texpr = gnm_expr_top_new_constant (value_new_error_REF (NULL));
3702 } else d (2, {
3703 char *tmp = gnm_expr_top_as_string
3704 (texpr, &pp, gnm_conventions_default);
3705 g_printerr ("Expression: %s\n", tmp);
3706 g_free (tmp);
3710 if (0 == strcmp (name, "Print_Area")) {
3711 GnmValue *val = gnm_expr_get_range (texpr->expr);
3712 if (val != NULL && VALUE_IS_CELLRANGE (val)) {
3713 int height, width;
3715 if (sheet == NULL) {
3716 Sheet *start_sheet, *end_sheet;
3717 GnmRange dest;
3719 /* Turn a global Print_Area name into a local
3720 name for the sheet it specifies. This
3721 triggers on the file from 632050. */
3722 gnm_rangeref_normalize_pp (value_get_rangeref (val),
3723 &pp,
3724 &start_sheet,
3725 &end_sheet,
3726 &dest);
3727 if (start_sheet && end_sheet == start_sheet) {
3728 sheet = start_sheet;
3729 pp.sheet = sheet;
3730 gnm_expr_top_unref (texpr);
3731 texpr = gnm_expr_top_new_constant (value_new_cellrange_r (NULL, &dest));
3735 if (sheet) {
3736 GnmRange r;
3738 range_init_rangeref (&r, value_get_rangeref (val));
3739 height = range_height (&r);
3740 width = range_width (&r);
3741 if (height == gnm_sheet_get_max_rows (sheet) &&
3742 width == gnm_sheet_get_max_cols (sheet)) {
3743 gnm_expr_top_unref (texpr);
3744 texpr = NULL;
3748 value_release (val);
3750 /* Completely ignore Print_Area settings of #REF! */
3751 if (texpr == NULL || gnm_expr_top_is_err (texpr, GNM_ERROR_REF)) {
3752 if (texpr) gnm_expr_top_unref (texpr);
3753 return NULL;
3757 nexpr = expr_name_add (&pp, name,
3758 texpr,
3759 &err, link_to_container, stub);
3760 if (nexpr == NULL) {
3761 go_io_warning (importer->context, "%s", err);
3762 g_free (err);
3763 return NULL;
3766 return nexpr;
3769 static char *
3770 excel_read_name_str (GnmXLImporter *importer,
3771 guint8 const *data, unsigned datalen, unsigned *name_len, gboolean is_builtin)
3773 gboolean use_utf16, has_extended;
3774 unsigned trailing_data_len, n_markup;
3775 char *name = NULL;
3777 /* Lovely, they put suffixes on builtins. Then those !#$^
3778 * dipsticks put the unicode header _before_ the builtin id
3779 * and stored the id as a character (possibly two byte).
3780 * NOTE : len is in _characters_ (not bytes) does not include
3781 * the header */
3782 if (is_builtin && *name_len) {
3783 guint8 const *str = data;
3784 char const *builtin;
3785 unsigned clen;
3787 if (importer->ver < MS_BIFF_V8) {
3788 use_utf16 = has_extended = FALSE;
3789 n_markup = trailing_data_len = 0;
3790 } else {
3791 int hlen = excel_read_string_header
3792 (str, datalen,
3793 &use_utf16, &n_markup, &has_extended,
3794 &trailing_data_len);
3795 str += hlen;
3796 datalen -= hlen;
3799 clen = use_utf16 ? 2 : 1;
3801 /* pull out the magic builtin enum */
3802 if (datalen >= clen) {
3803 builtin = excel_builtin_name (str);
3804 str += clen;
3805 datalen -= clen;
3806 } else
3807 builtin = "bogus";
3809 if (--(*name_len)) {
3810 char *tmp;
3812 *name_len = MIN (*name_len, datalen / clen);
3813 tmp = excel_get_chars (importer, str, *name_len, use_utf16, NULL);
3814 name = g_strconcat (builtin, tmp, NULL);
3815 g_free (tmp);
3816 *name_len = clen * (*name_len);
3817 } else
3818 name = g_strdup (builtin);
3820 *name_len += str - data;
3821 } else /* converts char len to byte len, and handles header */
3822 name = excel_get_text (importer, data, *name_len, name_len, NULL, datalen);
3823 return name;
3826 static void
3827 excel_read_EXTERNNAME (BiffQuery *q, MSContainer *container)
3829 MsBiffVersion const ver = container->importer->ver;
3830 GnmNamedExpr *nexpr = NULL;
3831 char *name = NULL;
3832 unsigned array_data_len = 0; /* Is this always true? */
3834 d (2, {
3835 g_printerr ("EXTERNNAME\n");
3836 gsf_mem_dump (q->data, q->length); });
3838 /* use biff version to differentiate, not the record version because
3839 * the version is the same for very old and new, with _v2 used for
3840 * some intermediate variants */
3841 if (ver >= MS_BIFF_V7) {
3842 unsigned expr_len = 0;
3843 guint8 const *expr_data = NULL;
3844 guint16 flags;
3845 guint32 namelen;
3847 XL_CHECK_CONDITION (q->length >= 7);
3849 flags = GSF_LE_GET_GUINT8 (q->data);
3850 namelen = GSF_LE_GET_GUINT8 (q->data + 6);
3852 name = excel_read_name_str (container->importer, q->data + 7, q->length - 7, &namelen, flags&1);
3853 if ((flags & (~1)) == 0) { /* all flags but builtin must be 0 */
3854 if (7 + 2 + namelen <= q->length) {
3855 unsigned el = GSF_LE_GET_GUINT16 (q->data + 7 + namelen);
3856 if (7 + 2 + namelen + el <= q->length) {
3857 expr_len = el;
3858 expr_data = q->data + 9 + namelen;
3859 } else
3860 go_io_warning (container->importer->context,
3861 _("Incorrect expression for name '%s': content will be lost.\n"),
3862 name);
3864 } else if ((flags & 0x10) == 0) /* DDE */
3865 go_io_warning (container->importer->context,
3866 _("DDE links are not supported yet.\nName '%s' will be lost.\n"),
3867 name ? name : "NULL");
3868 else /* OLE */
3869 go_io_warning (container->importer->context,
3870 _("OLE links are not supported yet.\nName '%s' will be lost.\n"),
3871 name ? name : "NULL");
3873 nexpr = excel_parse_name (container->importer, NULL,
3874 name, expr_data, expr_len,
3875 array_data_len, FALSE, NULL);
3876 } else if (ver >= MS_BIFF_V5) {
3877 XL_CHECK_CONDITION (q->length >= 7);
3879 name = excel_biff_text_1 (container->importer, q, 6);
3880 nexpr = excel_parse_name (container->importer, NULL,
3881 name, NULL, 0, array_data_len,
3882 FALSE, NULL);
3883 } else {
3884 XL_CHECK_CONDITION (q->length >= 3);
3886 name = excel_biff_text_1 (container->importer, q, 2);
3887 nexpr = excel_parse_name (container->importer, NULL,
3888 name, NULL, 0, array_data_len,
3889 FALSE, NULL);
3892 /* nexpr is potentially NULL if there was an error */
3893 if (ver >= MS_BIFF_V8) {
3894 GnmXLImporter *importer = container->importer;
3895 ExcelSupBook const *sup;
3897 g_return_if_fail (importer->v8.supbook->len > 0);
3899 /* The name is associated with the last SUPBOOK records seen */
3900 sup = &(g_array_index (importer->v8.supbook, ExcelSupBook,
3901 importer->v8.supbook->len-1));
3902 g_ptr_array_add (sup->externname, nexpr);
3903 } else {
3904 GPtrArray *externnames = container->v7.externnames;
3905 if (externnames == NULL)
3906 externnames = container->v7.externnames = g_ptr_array_new ();
3907 g_ptr_array_add (externnames, nexpr);
3909 g_free (name);
3912 /* Do some error checking to handle the magic name associated with an
3913 * autofilter in a sheet. Do not make it an error.
3914 * We have lots of examples of things that are not autofilters.
3916 static void
3917 excel_prepare_autofilter (GnmXLImporter *importer, GnmNamedExpr *nexpr)
3919 if (nexpr->pos.sheet != NULL) {
3920 GnmValue *v = gnm_expr_top_get_range (nexpr->texpr);
3921 if (v != NULL) {
3922 GnmSheetRange r;
3923 gboolean valid = gnm_sheet_range_from_value (&r, v);
3924 value_release (v);
3926 if (valid) {
3927 unsigned i;
3928 GnmFilter *filter;
3929 ExcelReadSheet *esheet;
3931 filter = gnm_filter_new (r.sheet, &r.range);
3932 expr_name_remove (nexpr);
3934 for (i = 0 ; i < importer->excel_sheets->len; i++) {
3935 esheet = g_ptr_array_index (importer->excel_sheets, i);
3936 if (esheet->sheet == r.sheet) {
3937 g_return_if_fail (esheet->filter == NULL);
3938 esheet->filter = filter;
3939 break;
3947 static void
3948 excel_read_NAME (BiffQuery *q, GnmXLImporter *importer, ExcelReadSheet *esheet)
3950 MsBiffVersion const ver = importer->ver;
3951 GnmNamedExpr *nexpr = NULL;
3952 guint16 expr_len, sheet_index, flags = 0;
3953 guint8 const *data;
3954 gboolean builtin_name = FALSE;
3955 char *name = NULL;
3956 /* length in characters (not bytes) in the same pos for all versions */
3957 unsigned name_len;
3958 /* guint8 kb_shortcut = GSF_LE_GET_GUINT8 (q->data + 2); */
3959 /* int fn_grp_idx = (flags & 0xfc0)>>6; */
3961 XL_CHECK_CONDITION (q->length >= 4);
3963 name_len = GSF_LE_GET_GUINT8 (q->data + 3);
3965 d (2, {
3966 g_printerr ("NAME\n");
3967 gsf_mem_dump (q->data, q->length); });
3969 if (ver >= MS_BIFF_V2) {
3970 flags = GSF_LE_GET_GUINT16 (q->data);
3971 builtin_name = (flags & 0x0020) != 0;
3974 /* use biff version to differentiate, not the record version because
3975 * the version is the same for very old and new, with _v2 used for
3976 * some intermediate variants */
3977 if (ver >= MS_BIFF_V8) {
3978 XL_CHECK_CONDITION (q->length >= 14);
3979 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3980 sheet_index = GSF_LE_GET_GUINT16 (q->data + 8);
3981 data = q->data + 14;
3982 } else if (ver >= MS_BIFF_V7) {
3983 XL_CHECK_CONDITION (q->length >= 14);
3984 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3985 /* opencalc docs claim 8 is the right one, XL docs say 6 == 8
3986 * pivot.xls suggests that at least for local builtin names 6
3987 * is correct and 8 is bogus for == biff7 */
3988 sheet_index = GSF_LE_GET_GUINT16 (q->data + 6);
3989 data = q->data + 14;
3990 } else if (ver >= MS_BIFF_V3) {
3991 XL_CHECK_CONDITION (q->length >= 6);
3992 expr_len = GSF_LE_GET_GUINT16 (q->data + 4);
3993 data = q->data + 6;
3994 sheet_index = 0; /* no sheets */
3995 } else {
3996 XL_CHECK_CONDITION (q->length >= 5);
3997 expr_len = GSF_LE_GET_GUINT8 (q->data + 4);
3998 data = q->data + 5;
3999 sheet_index = 0; /* no sheets */
4002 XL_NEED_BYTES (name_len);
4003 name = excel_read_name_str (importer, data, q->length - (data - q->data), &name_len, builtin_name);
4004 XL_NEED_BYTES (name_len);
4005 data += name_len;
4007 if (name != NULL) {
4008 unsigned array_data_len;
4009 Sheet *sheet = NULL;
4010 d (1, g_printerr ("NAME=%s, sheet_index=%d flags=0x%x\n",
4011 name, sheet_index, flags););
4012 if (sheet_index > 0) {
4013 /* NOTE : the docs lie the index for biff7 is
4014 * indeed a reference to the externsheet
4015 * however we have examples in biff8 that can
4016 * only to be explained by a 1 based index to
4017 * the boundsheets. Which is not unreasonable
4018 * given that these are local names */
4019 if (importer->ver >= MS_BIFF_V8) {
4020 if (sheet_index <= importer->boundsheet_sheet_by_index->len &&
4021 sheet_index > 0)
4022 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, sheet_index-1);
4023 else
4024 g_warning ("So much for that theory 2");
4025 } else
4026 sheet = excel_externsheet_v7 (&importer->container, sheet_index);
4029 if (sheet == XL_EXTERNSHEET_MAGIC_SELFREF)
4030 sheet = esheet ? esheet->sheet : NULL;
4031 else if (sheet == XL_EXTERNSHEET_MAGIC_DELETED)
4032 sheet = NULL;
4034 /* do we have a stub from a forward decl ? */
4035 if (importer->num_name_records < importer->names->len)
4036 nexpr = g_ptr_array_index (importer->names, importer->num_name_records);
4038 XL_NEED_BYTES (expr_len);
4039 array_data_len = expr_len ? q->length - (data - q->data) - expr_len : 0;
4040 nexpr = excel_parse_name (importer, sheet,
4041 name, data, expr_len,
4042 array_data_len, TRUE, nexpr);
4043 g_free (name);
4044 data += expr_len;
4046 /* Add a ref to keep it around after the excel-sheet/wb goes
4047 * away. externnames do not get references and are unrefed
4048 * after import finishes, which destroys them if they are not
4049 * in use. */
4050 if (nexpr != NULL) {
4051 expr_name_ref (nexpr);
4052 nexpr->is_hidden = (flags & 0x0001) ? TRUE : FALSE;
4054 /* Undocumented magic.
4055 * XL stores a hidden name with the details of an autofilter */
4056 if (nexpr->is_hidden && !strcmp (expr_name_name (nexpr), "_FilterDatabase"))
4057 excel_prepare_autofilter (importer, nexpr);
4058 /* g_warning ("flags = %hx, state = %s\n", flags, global ? "global" : "sheet"); */
4060 else if ((flags & 0xE) == 0xE) /* Function & VB-Proc & Proc */
4061 gnm_func_add_placeholder (importer->wb,
4062 expr_name_name (nexpr), "VBA");
4066 /* nexpr is potentially NULL if there was an error */
4067 if (importer->num_name_records < importer->names->len)
4068 g_ptr_array_index (importer->names, importer->num_name_records) = nexpr;
4069 else if (importer->num_name_records == importer->names->len)
4070 g_ptr_array_add (importer->names, nexpr);
4071 importer->num_name_records++;
4073 d (5, {
4074 guint8 menu_txt_len = GSF_LE_GET_GUINT8 (q->data + 10);
4075 guint8 descr_txt_len = GSF_LE_GET_GUINT8 (q->data + 11);
4076 guint8 help_txt_len = GSF_LE_GET_GUINT8 (q->data + 12);
4077 guint8 status_txt_len = GSF_LE_GET_GUINT8 (q->data + 13);
4078 const guint8 *end = q->data + q->length;
4079 char *menu_txt;
4080 char *descr_txt;
4081 char *help_txt;
4082 char *status_txt;
4084 menu_txt = excel_get_text (importer, data, menu_txt_len, NULL, NULL, end - data);
4085 data += menu_txt_len;
4086 descr_txt = excel_get_text (importer, data, descr_txt_len, NULL, NULL, end - data);
4087 data += descr_txt_len;
4088 help_txt = excel_get_text (importer, data, help_txt_len, NULL, NULL, end - data);
4089 data += help_txt_len;
4090 status_txt = excel_get_text (importer, data, status_txt_len, NULL, NULL, end - data);
4092 g_printerr ("Name record: '%s', '%s', '%s', '%s', '%s'\n",
4093 nexpr ? expr_name_name (nexpr) : "(null)",
4094 menu_txt ? menu_txt : "(null)",
4095 descr_txt ? descr_txt : "(null)",
4096 help_txt ? help_txt : "(null)",
4097 status_txt ? status_txt : "(null)");
4099 if ((flags & 0x0001) != 0) g_printerr (" Hidden");
4100 if ((flags & 0x0002) != 0) g_printerr (" Function");
4101 if ((flags & 0x0004) != 0) g_printerr (" VB-Proc");
4102 if ((flags & 0x0008) != 0) g_printerr (" Proc");
4103 if ((flags & 0x0010) != 0) g_printerr (" CalcExp");
4104 if ((flags & 0x0020) != 0) g_printerr (" BuiltIn");
4105 if ((flags & 0x1000) != 0) g_printerr (" BinData");
4106 g_printerr ("\n");
4108 g_free (menu_txt);
4109 g_free (descr_txt);
4110 g_free (help_txt);
4111 g_free (status_txt);
4115 static void
4116 excel_read_XCT (BiffQuery *q, GnmXLImporter *importer)
4118 guint16 last_col, opcode;
4119 guint8 const *data;
4120 unsigned len;
4121 int count;
4122 Sheet *sheet = NULL;
4123 GnmCell *cell;
4124 GnmValue *v;
4125 GnmEvalPos ep;
4127 if (importer->ver >= MS_BIFF_V8) {
4128 guint16 supbook;
4130 XL_CHECK_CONDITION (q->length == 4);
4132 count = GSF_LE_GET_GINT16 (q->data);
4133 supbook = GSF_LE_GET_GUINT16 (q->data+2);
4134 } else {
4135 XL_CHECK_CONDITION (q->length == 2);
4137 count = GSF_LE_GET_GINT16 (q->data);
4140 if (count < 0) /* WHAT THE HECK DOES NEGATIVE MEAN ?? */
4141 count = -count;
4143 if (sheet != NULL)
4144 eval_pos_init_sheet (&ep, sheet);
4146 while (count-- > 0) {
4147 if (!ms_biff_query_peek_next (q, &opcode)) {
4148 g_warning ("Expected a CRN record");
4149 return;
4150 } else if (opcode != BIFF_CRN) {
4151 g_warning ("Expected a CRN record not a %hx", opcode);
4152 return;
4154 ms_biff_query_next (q);
4156 XL_CHECK_CONDITION (q->length >= 4);
4158 ep.eval.col = GSF_LE_GET_GUINT8 (q->data+0);
4159 last_col = GSF_LE_GET_GUINT8 (q->data+1);
4160 ep.eval.row = GSF_LE_GET_GUINT16 (q->data+2);
4162 /* ignore content for sheets that are already loaded */
4163 if (sheet == NULL)
4164 continue;
4166 for (data = q->data + 4; ep.eval.col <= last_col ; ep.eval.col++) {
4167 guint8 oper;
4168 XL_NEED_BYTES (1);
4170 oper = *data++;
4171 switch (oper) {
4172 case 1:
4173 XL_NEED_BYTES (8);
4174 v = value_new_float (GSF_LE_GET_DOUBLE (data));
4175 data += 8;
4176 break;
4177 case 2:
4178 XL_NEED_BYTES (1);
4179 len = *data++;
4180 v = value_new_string_nocopy (
4181 excel_get_text (importer, data, len, NULL, NULL, q->data + q->length - data));
4182 data += len;
4183 break;
4185 case 4:
4186 XL_NEED_BYTES (2);
4187 v = value_new_bool (GSF_LE_GET_GUINT16 (data) != 0);
4188 /* FIXME: 8?? */
4189 data += 8;
4190 break;
4192 case 16:
4193 XL_NEED_BYTES (2);
4194 v = xls_value_new_err (&ep, GSF_LE_GET_GUINT16 (data));
4195 /* FIXME: 8?? */
4196 data += 8;
4197 break;
4199 default :
4200 g_warning ("Unknown oper type 0x%x in a CRN record", oper);
4201 v = NULL;
4204 if (v != NULL) {
4205 cell = sheet_cell_fetch (sheet, ep.eval.col, ep.eval.row);
4206 if (cell)
4207 gnm_cell_set_value (cell, v);
4208 else
4209 value_release (v);
4215 static XL_font_width const *
4216 xl_find_fontspec (ExcelReadSheet *esheet, double *size20)
4218 /* Use the 'Normal' Style which is by definition the 0th */
4219 BiffXFData const *xf = excel_get_xf (esheet, 0);
4220 ExcelFont const *fd = (xf != NULL)
4221 ? excel_font_get (esheet->container.importer, xf->font_idx)
4222 : NULL;
4223 *size20 = (fd != NULL) ? (fd->height / (20. * 10.)) : 1.;
4224 return xl_lookup_font_specs ((fd != NULL) ? fd->fontname : "Arial");
4228 * get_row_height_units:
4229 * @height height in Excel units
4231 * Converts row height from Excel units to points. Returns height in points.
4233 * Excel specifies row height in 1/20 of a point.
4235 * What we now print out is just 0.5% shorter than theoretical
4236 * height. The height of what Excel prints out varies in mysterious
4237 * ways. Sometimes it is close to theoretical, sometimes it is a few %
4238 * shorter. I don't see any point in correcting for the 0.5% until we
4239 * know the whole story.
4241 static double
4242 get_row_height_units (guint16 height)
4244 return 1. / 20. * height;
4247 static void
4248 excel_read_ROW (BiffQuery *q, ExcelReadSheet *esheet)
4250 guint16 row, height;
4251 guint16 flags = 0;
4252 guint16 flags2 = 0;
4253 guint16 xf;
4254 gboolean is_std_height;
4256 XL_CHECK_CONDITION (q->length >= (q->opcode == BIFF_ROW_v2 ? 16 : 8));
4258 row = GSF_LE_GET_GUINT16 (q->data);
4259 #if 0
4260 /* Unnecessary info for now.
4261 * do we want to preallocate based on this info?
4263 guint16 const start_col = GSF_LE_GET_GUINT16 (q->data + 2);
4264 guint16 const end_col = GSF_LE_GET_GUINT16 (q->data + 4) - 1;
4265 #endif
4266 height = GSF_LE_GET_GUINT16 (q->data + 6);
4268 /* If the bit is on it indicates that the row is of 'standard' height.
4269 * However the remaining bits still include the size.
4271 is_std_height = (height & 0x8000) != 0;
4273 if (q->opcode == BIFF_ROW_v2) {
4274 flags = GSF_LE_GET_GUINT16 (q->data + 12);
4275 flags2 = GSF_LE_GET_GUINT16 (q->data + 14);
4277 xf = flags2 & 0xfff;
4279 d (1, {
4280 g_printerr ("Row %d height 0x%x, flags=0x%x 0x%x;\n", row + 1, height, flags, flags2);
4281 if (is_std_height)
4282 g_printerr ("%s\n", "Is Std Height;\n");
4283 if (flags2 & 0x1000)
4284 g_printerr ("%s\n", "Top thick;\n");
4285 if (flags2 & 0x2000)
4286 g_printerr ("%s\n", "Bottom thick;\n");
4289 /* TODO: Put mechanism in place to handle thick margins */
4290 /* TODO: Columns actually set the size even when it is the default.
4291 * Which approach is better?
4293 if (!is_std_height) {
4294 double hu = get_row_height_units (height);
4295 sheet_row_set_size_pts (esheet->sheet, row, hu,
4296 (flags & 0x40) ? TRUE : FALSE);
4299 if (flags & 0x20)
4300 colrow_set_visibility (esheet->sheet, FALSE, FALSE, row, row);
4302 if (flags & 0x80) {
4303 if (xf != 0)
4304 excel_set_xf_segment (esheet,
4305 0, gnm_sheet_get_max_cols (esheet->sheet) - 1,
4306 row, row, xf);
4307 d (1, g_printerr ("row %d has flags 0x%x a default style %hd;\n",
4308 row + 1, flags, xf););
4311 if ((unsigned)(flags & 0x17) > 0)
4312 col_row_info_set_outline (sheet_row_fetch (esheet->sheet, row),
4313 (unsigned)(flags & 0x7), flags & 0x10);
4316 static void
4317 excel_read_TAB_COLOR (BiffQuery *q, ExcelReadSheet *esheet)
4319 /* this is a guess, but the only field I see
4320 * changing seems to be the colour.
4322 #if 0
4323 0 | 62 8 0 0 0 0 0 0 0 0 0 0 14 0 0 0 | b...............
4324 10 | 0 0 0 XX XX XX XX XX XX XX XX XX XX XX XX | ...************
4326 office 12 seems to add 8 bytes
4327 #endif
4328 guint8 color_index;
4329 GnmColor *color;
4330 GnmColor *text_color;
4331 int contrast;
4333 XL_CHECK_CONDITION (q->length >= 20);
4335 /* be conservative for now, we have not seen a palette larger than 56
4336 * so this is largely moot, this is probably a uint32
4338 color_index = GSF_LE_GET_GUINT8 (q->data + 16);
4339 color = excel_palette_get (esheet->container.importer, color_index);
4340 contrast = GO_COLOR_UINT_R (color->go_color) +
4341 GO_COLOR_UINT_G (color->go_color) +
4342 GO_COLOR_UINT_B (color->go_color);
4343 if (contrast >= 0x180)
4344 text_color = style_color_black ();
4345 else
4346 text_color = style_color_white ();
4347 g_object_set (esheet->sheet,
4348 "tab-foreground", text_color,
4349 "tab-background", color,
4350 NULL);
4351 d (1, g_printerr ("%s tab colour = %08x\n",
4352 esheet->sheet->name_unquoted,
4353 color->go_color););
4355 style_color_unref (text_color);
4356 style_color_unref (color);
4359 static void
4360 excel_read_COLINFO (BiffQuery *q, ExcelReadSheet *esheet)
4362 int i;
4363 double scale, width;
4364 guint16 firstcol, lastcol;
4365 int charwidths;
4366 guint16 xf, options;
4367 gboolean hidden, customWidth, bestFit, collapsed;
4368 unsigned outline_level;
4369 XL_font_width const *spec;
4371 XL_CHECK_CONDITION (q->length >= 10);
4373 firstcol = GSF_LE_GET_GUINT16 (q->data);
4374 lastcol = GSF_LE_GET_GUINT16 (q->data + 2);
4375 charwidths = GSF_LE_GET_GUINT16 (q->data + 4);
4376 xf = GSF_LE_GET_GUINT16 (q->data + 6);
4377 options = GSF_LE_GET_GUINT16 (q->data + 8);
4378 hidden = (options & 0x0001) != 0;
4379 customWidth = (options & 0x0002) != 0; /* undocumented */
4380 bestFit = (options & 0x0004) != 0; /* undocumented */
4381 collapsed = (options & 0x1000) != 0;
4382 outline_level = (unsigned)((options >> 8) & 0x7);
4383 spec = xl_find_fontspec (esheet, &scale);
4385 XL_CHECK_CONDITION (firstcol < gnm_sheet_get_max_cols (esheet->sheet));
4386 g_return_if_fail (spec != NULL);
4388 /* Widths appear to be quoted including margins and the leading
4389 * gridline that gnumeric expects. The charwidths here are not
4390 * strictly linear. So I measured in increments of -2 -1 0 1 2 around
4391 * the default width when using each font @ 10pts as
4392 * the Normal Style. The pixel calculation is then reduced to
4394 * (default_size + ((quoted_width - baseline) / step))
4395 * * scale : fonts != 10pts
4396 * * 72/96 : value in pts so that zoom is not a factor
4398 * NOTE: These measurements do NOT correspond to what is shown to the
4399 * user */
4400 width = 8. * spec->defcol_unit +
4401 (double)(charwidths - spec->colinfo_baseline) / spec->colinfo_step;
4402 width *= scale * 72./96.;
4404 if (width <= 0) { /* Columns are of default width */
4405 width = esheet->sheet->cols.default_style.size_pts;
4406 hidden = TRUE;
4407 } else if (width < 4) /* gnumeric can not draw without a margin */
4408 width = 4;
4410 d (1, {
4411 g_printerr ("Column Formatting %s!%s of width "
4412 "%u/256 characters (%f pts)\n",
4413 esheet->sheet->name_quoted,
4414 cols_name (firstcol, lastcol), charwidths, width);
4415 g_printerr ("Options 0x%hx, default style %hu\n", options, xf);
4418 /* NOTE: seems like this is inclusive firstcol, inclusive lastcol */
4419 if (lastcol >= gnm_sheet_get_max_cols (esheet->sheet))
4420 lastcol = gnm_sheet_get_max_cols (esheet->sheet) - 1;
4421 for (i = firstcol; i <= lastcol; i++) {
4422 sheet_col_set_size_pts (esheet->sheet, i, width,
4423 customWidth && !bestFit);
4424 if (outline_level > 0 || collapsed)
4425 col_row_info_set_outline (sheet_col_fetch (esheet->sheet, i),
4426 outline_level, collapsed);
4429 if (xf != 0)
4430 excel_set_xf_segment (esheet, firstcol, lastcol,
4431 0, gnm_sheet_get_max_rows (esheet->sheet) - 1, xf);
4433 if (hidden)
4434 colrow_set_visibility (esheet->sheet, TRUE, FALSE,
4435 firstcol, lastcol);
4438 /* Add a bmp header so that gdk-pixbuf can do the work */
4439 static GdkPixbuf *
4440 excel_read_os2bmp (BiffQuery *q, guint32 image_len)
4442 GError *err = NULL;
4443 GdkPixbufLoader *loader = NULL;
4444 GdkPixbuf *pixbuf = NULL;
4445 gboolean ret = FALSE;
4446 guint8 bmphdr[BMP_HDR_SIZE];
4448 XL_CHECK_CONDITION_VAL (q->length >= 8 && image_len < q->length - 8, NULL);
4450 loader = gdk_pixbuf_loader_new_with_type ("bmp", &err);
4451 if (!loader)
4452 return NULL;
4453 excel_fill_bmp_header(bmphdr, q->data, image_len);
4454 ret = gdk_pixbuf_loader_write (loader, bmphdr, sizeof bmphdr, &err);
4455 if (ret)
4456 ret = gdk_pixbuf_loader_write (loader, q->data+8,
4457 q->length-8, &err);
4458 gdk_pixbuf_loader_close (loader, ret ? &err : NULL);
4459 if (ret) {
4460 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
4461 g_object_ref (pixbuf);
4462 } else {
4463 g_message ("Unable to read OS/2 BMP image: %s\n",
4464 err->message);
4465 g_error_free (err);
4467 g_object_unref (loader);
4468 return pixbuf;
4471 /* When IMDATA or BG_PIC is bitmap, the format is OS/2 BMP, but the
4472 * 14 bytes header is missing.
4474 GdkPixbuf *
4475 excel_read_IMDATA (BiffQuery *q, gboolean keep_image)
4477 guint32 image_len;
4478 GdkPixbuf *pixbuf = NULL;
4479 guint16 format;
4481 XL_CHECK_CONDITION_VAL (q->length >= 8, NULL);
4483 format = GSF_LE_GET_GUINT16 (q->data);
4484 image_len = GSF_LE_GET_GUINT32 (q->data + 4);
4486 switch (format) {
4487 case 0x2: break; /* Windows metafile/Mac pict */
4488 case 0x9: /* OS/2 BMP sans header */
4490 pixbuf = excel_read_os2bmp (q, image_len);
4492 break;
4493 case 0xe: break; /* Native format */
4494 default: break; /* Unknown format */
4497 /* Dump formats which weren't handled above to file */
4498 if (format != 0x9) {
4499 char const *from_name;
4500 char const *format_name;
4501 guint16 const format = GSF_LE_GET_GUINT16 (q->data);
4502 guint16 const from_env = GSF_LE_GET_GUINT16 (q->data + 2);
4504 switch (from_env) {
4505 case 1: from_name = "Windows"; break;
4506 case 2: from_name = "Macintosh"; break;
4507 default: from_name = "Unknown environment?"; break;
4509 switch (format) {
4510 case 0x2:
4511 format_name = (from_env == 1) ? "windows metafile" : "mac pict";
4512 break;
4514 case 0xe: format_name = "'native format'"; break;
4515 default: format_name = "Unknown format?"; break;
4518 d (1, {
4519 FILE *f;
4520 static int count = 0;
4521 char *file_name = g_strdup_printf ("imdata%d", count++);
4522 g_printerr ("Picture from %s in %s format\n",
4523 from_name, format_name);
4525 f = g_fopen (file_name, "w");
4526 fwrite (q->data+8, 1, q->length-8, f);
4527 g_free (file_name);
4528 fclose (f);
4532 return pixbuf;
4535 static void
4536 excel_read_SELECTION (BiffQuery *q, ExcelReadSheet *esheet)
4538 GnmCellPos edit_pos;
4539 unsigned pane_number, i, j, num_refs;
4540 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
4541 GnmRange r;
4543 XL_CHECK_CONDITION (q->length >= 9);
4544 pane_number = GSF_LE_GET_GUINT8 (q->data);
4545 edit_pos.row = GSF_LE_GET_GUINT16 (q->data + 1);
4546 edit_pos.col = GSF_LE_GET_GUINT16 (q->data + 3);
4547 j = GSF_LE_GET_GUINT16 (q->data + 5);
4548 num_refs = GSF_LE_GET_GUINT16 (q->data + 7);
4549 XL_CHECK_CONDITION (q->length >= 9 + 6 * num_refs);
4551 if (pane_number != esheet->active_pane)
4552 return;
4554 /* FIXME: docs say that consequtive records with the same pane
4555 number should be treated as one. No file with this has been
4556 observed. */
4558 d (5, g_printerr ("Start selection in pane #%d\n", pane_number););
4559 d (5, g_printerr ("Cursor: %s in Ref #%d\n", cellpos_as_string (&edit_pos),
4560 j););
4562 g_return_if_fail (sv != NULL);
4564 sv_selection_reset (sv);
4565 for (i = 0; i <= num_refs ; i++) {
4566 GnmCellPos tmp;
4567 unsigned i0 = (i == num_refs) ? j : i;
4569 /* Skip the active; then read it last. */
4570 if (i == j || i0 >= num_refs)
4571 continue;
4573 xls_read_range8 (&r, q->data + 9 + 6 * i0);
4575 d (5, g_printerr ("Ref %d = %s\n", i, range_as_string (&r)););
4577 tmp = (i == num_refs) ? edit_pos : r.start;
4578 sv_selection_add_full (sv,
4579 tmp.col, tmp.row,
4580 r.start.col, r.start.row,
4581 r.end.col, r.end.row,
4582 GNM_SELECTION_MODE_ADD);
4585 if (sv->selections == NULL) {
4586 /* See bug 632050 */
4587 sv_selection_add_pos (sv, 0, 0,
4588 GNM_SELECTION_MODE_ADD);
4589 d (5, g_printerr ("No selection\n"););
4592 d (5, g_printerr ("Done selection\n"););
4595 static void
4596 excel_read_DEF_ROW_HEIGHT (BiffQuery *q, ExcelReadSheet *esheet)
4598 guint16 flags = 0;
4599 guint16 height = 0; /* must be 16 bit */
4600 double height_units;
4602 if (q->opcode != BIFF_DEFAULTROWHEIGHT_v0) {
4603 XL_CHECK_CONDITION (q->length >= 4);
4604 flags = GSF_LE_GET_GUINT16 (q->data);
4605 height = GSF_LE_GET_GUINT16 (q->data + 2);
4606 } else {
4607 XL_CHECK_CONDITION (q->length >= 2);
4608 height = GSF_LE_GET_GUINT16 (q->data);
4609 height &= 0x7fff; /* there seems to be a flag in the top bit */
4612 height_units = get_row_height_units (height);
4613 d (2, {
4614 g_printerr ("Default row height %3.3g;\n", height_units);
4615 if (flags & 0x04)
4616 g_printerr (" + extra space above;\n");
4617 if (flags & 0x08)
4618 g_printerr (" + extra space below;\n");
4621 sheet_row_set_default_size_pts (esheet->sheet, height_units);
4624 static void
4625 excel_read_DEF_COL_WIDTH (BiffQuery *q, ExcelReadSheet *esheet)
4627 guint16 charwidths;
4628 double scale;
4629 XL_font_width const *spec = xl_find_fontspec (esheet, &scale);
4631 XL_CHECK_CONDITION (q->length >= 2);
4632 charwidths = GSF_LE_GET_GUINT16 (q->data);
4633 d (0, g_printerr ("Default column width %hu characters\n", charwidths););
4635 /* According to the tooltip the default width is 8.43 character widths
4636 * and 64 pixels wide (Arial 10) which appears to include margins, and
4637 * the leading gridline That is saved as 8 char widths for
4638 * DEL_COL_WIDTH and 9.14 widths for COLINFO */
4639 sheet_col_set_default_size_pts (esheet->sheet,
4640 charwidths * spec->defcol_unit * scale * 72./96.);
4643 /* we could get this implicitly from the cols/rows
4644 * but this is faster
4646 static void
4647 excel_read_GUTS (BiffQuery *q, ExcelReadSheet *esheet)
4649 int col_gut, row_gut;
4651 XL_CHECK_CONDITION (q->length == 8);
4653 /* ignore the specification of how wide/tall the gutters are */
4654 row_gut = GSF_LE_GET_GUINT16 (q->data + 4);
4655 d (2, g_printerr ("row_gut = %d", row_gut););
4656 if (row_gut >= 1)
4657 row_gut--;
4658 col_gut = GSF_LE_GET_GUINT16 (q->data + 6);
4659 d (2, g_printerr ("col_gut = %d\n", col_gut););
4660 if (col_gut >= 1)
4661 col_gut--;
4662 sheet_colrow_gutter (esheet->sheet, TRUE, col_gut);
4663 sheet_colrow_gutter (esheet->sheet, FALSE, row_gut);
4666 static void
4667 excel_read_SETUP (BiffQuery *q, ExcelReadSheet *esheet)
4669 GnmPrintInformation *pi = esheet->sheet->print_info;
4670 guint16 flags;
4671 gboolean rotate_paper = FALSE;
4672 gboolean portrait_orientation = TRUE;
4674 XL_CHECK_CONDITION (q->length >= 12);
4676 flags = GSF_LE_GET_GUINT16 (q->data + 10);
4677 pi->print_across_then_down = (flags & 0x1) != 0;
4678 pi->print_black_and_white = (flags & 0x8) != 0;
4680 if (0 == (flags & 0x4)) {
4681 guint16 papersize = GSF_LE_GET_GUINT16 (q->data + 0);
4682 const char *paper_name =
4683 xls_paper_name (papersize, &rotate_paper);
4685 d (2, g_printerr ("Paper size %hu --> %s\n",
4686 papersize,
4687 paper_name ? paper_name : "-"););
4689 if (paper_name != NULL)
4690 print_info_set_paper (pi, paper_name);
4692 pi->scaling.percentage.x = pi->scaling.percentage.y =
4693 GSF_LE_GET_GUINT16 (q->data + 2);
4694 pi->start_page = GSF_LE_GET_GUINT16 (q->data + 4);
4695 pi->scaling.dim.cols = GSF_LE_GET_GUINT16 (q->data + 6);
4696 pi->scaling.dim.rows = GSF_LE_GET_GUINT16 (q->data + 8);
4697 if (pi->scaling.percentage.x < 1. || pi->scaling.percentage.x > 1000.) {
4698 if (pi->scaling.percentage.x != 0) {
4699 /* 0 seems to be 'auto' */
4700 g_warning ("setting invalid print scaling (%f) to 100%%",
4701 pi->scaling.percentage.x);
4703 pi->scaling.percentage.x = pi->scaling.percentage.y = 100.;
4706 if (esheet_ver (esheet) == MS_BIFF_V4 || 0 == (flags & 0x40))
4707 portrait_orientation = (flags & 0x2) != 0;
4708 if (rotate_paper)
4709 portrait_orientation = !portrait_orientation;
4711 print_info_set_paper_orientation (pi, portrait_orientation
4712 ? GTK_PAGE_ORIENTATION_PORTRAIT
4713 : GTK_PAGE_ORIENTATION_LANDSCAPE);
4716 if (esheet_ver (esheet) > MS_BIFF_V4) {
4717 XL_CHECK_CONDITION (q->length >= 34);
4719 pi->print_as_draft = (flags & 0x10) != 0;
4720 pi->comment_placement = (flags & 0x20)
4721 ? GNM_PRINT_COMMENTS_IN_PLACE : GNM_PRINT_COMMENTS_NONE;
4722 print_info_set_margin_header (pi,
4723 GO_IN_TO_PT (gsf_le_get_double (q->data + 16)));
4724 print_info_set_margin_footer (pi,
4725 GO_IN_TO_PT (gsf_le_get_double (q->data + 24)));
4726 if (0 == (flags & 0x4))
4727 pi->n_copies = GSF_LE_GET_GUINT16 (q->data + 32);
4728 d (2, g_printerr ("resolution %hu vert. res. %hu\n",
4729 GSF_LE_GET_GUINT16 (q->data + 12),
4730 GSF_LE_GET_GUINT16 (q->data + 14)););
4733 if (esheet_ver (esheet) >= MS_BIFF_V8) {
4734 if ((flags & 0x200) &&
4735 pi->comment_placement == GNM_PRINT_COMMENTS_IN_PLACE)
4736 pi->comment_placement = GNM_PRINT_COMMENTS_AT_END;
4737 switch ((flags >> 10) & 3) {
4738 case 0 : pi->error_display = GNM_PRINT_ERRORS_AS_DISPLAYED; break;
4739 case 1 : pi->error_display = GNM_PRINT_ERRORS_AS_BLANK; break;
4740 case 2 : pi->error_display = GNM_PRINT_ERRORS_AS_DASHES; break;
4741 case 3 : pi->error_display = GNM_PRINT_ERRORS_AS_NA; break;
4746 static void
4747 excel_read_MULRK (BiffQuery *q, ExcelReadSheet *esheet)
4749 guint32 col, row, lastcol;
4750 guint8 const *ptr = q->data;
4751 GnmValue *v;
4752 BiffXFData const *xf;
4753 GnmStyle *mstyle;
4755 XL_CHECK_CONDITION (q->length >= 4 + 6 + 2);
4757 row = GSF_LE_GET_GUINT16 (q->data);
4758 col = GSF_LE_GET_GUINT16 (q->data + 2);
4759 ptr += 4;
4760 lastcol = GSF_LE_GET_GUINT16 (q->data + q->length - 2);
4762 XL_CHECK_CONDITION (lastcol >= col);
4763 XL_CHECK_CONDITION (lastcol < (guint32)gnm_sheet_get_max_cols (esheet->sheet));
4765 if (q->length != 4 + 6 * (lastcol - col + 1) + 2) {
4766 int guess = col + (q->length - (4 + 2)) / 6 - 1;
4767 g_warning ("MULRK with strange size: %d vs %d", lastcol, guess);
4768 lastcol = MIN (lastcol, (guint32)MAX (guess, 0));
4771 for (; col <= lastcol ; col++) {
4772 GnmCell *cell;
4773 /* 2byte XF, 4 byte RK */
4774 v = biff_get_rk (ptr + 2);
4775 xf = excel_get_xf (esheet, GSF_LE_GET_GUINT16 (ptr));
4776 mstyle = excel_get_style_from_xf (esheet, xf);
4777 if (mstyle != NULL)
4778 sheet_style_set_pos (esheet->sheet, col, row, mstyle);
4779 if (xf && xf->is_simple_format)
4780 value_set_fmt (v, xf->style_format);
4781 cell = sheet_cell_fetch (esheet->sheet, col, row);
4782 if (cell)
4783 gnm_cell_set_value (cell, v);
4784 else
4785 value_release (v);
4786 ptr += 6;
4790 static void
4791 excel_read_MULBLANK (BiffQuery *q, ExcelReadSheet *esheet)
4793 /* This is an educated guess, docs are not terribly clear */
4794 int firstcol, lastcol, row;
4795 guint8 const *ptr = (q->data + q->length - 2);
4796 int i, range_end, prev_xf, xf_index;
4798 XL_CHECK_CONDITION (q->length >= 6);
4799 firstcol = XL_GETCOL (q);
4800 row = XL_GETROW (q);
4801 lastcol = GSF_LE_GET_GUINT16 (ptr);
4802 d (0, {
4803 g_printerr ("Cells in row %d are blank starting at col %s until col ",
4804 row + 1, col_name (firstcol));
4805 g_printerr ("%s;\n",
4806 col_name (lastcol));
4809 if (lastcol < firstcol) {
4810 int tmp = firstcol;
4811 firstcol = lastcol;
4812 lastcol = tmp;
4814 XL_CHECK_CONDITION (q->length >= 4u + 2u * (lastcol - firstcol + 1));
4816 range_end = i = lastcol;
4817 prev_xf = -1;
4818 do {
4819 ptr -= 2;
4820 xf_index = GSF_LE_GET_GUINT16 (ptr);
4821 d (2, {
4822 g_printerr (" xf (%s) = 0x%x", col_name (i), xf_index);
4823 if (i == firstcol)
4824 g_printerr ("\n");
4827 if (prev_xf != xf_index) {
4828 if (prev_xf >= 0)
4829 excel_set_xf_segment (esheet, i + 1, range_end,
4830 row, row, prev_xf);
4831 prev_xf = xf_index;
4832 range_end = i;
4834 } while (--i >= firstcol);
4835 excel_set_xf_segment (esheet, firstcol, range_end,
4836 row, row, prev_xf);
4837 d (2, g_printerr ("\n"););
4840 void
4841 xls_read_range32 (GnmRange *r, guint8 const *data)
4843 r->start.row = GSF_LE_GET_GUINT32 (data + 0);
4844 r->end.row = GSF_LE_GET_GUINT32 (data + 4);
4845 r->start.col = GSF_LE_GET_GUINT16 (data + 8);
4846 r->end.col = GSF_LE_GET_GUINT16 (data + 10);
4848 r->start.row = CLAMP (r->start.row, 0, GNM_MAX_ROWS - 1);
4849 r->end.row = CLAMP (r->end.row, 0, GNM_MAX_ROWS - 1);
4850 r->start.col = CLAMP (r->start.col, 0, GNM_MAX_COLS - 1);
4851 r->end.col = CLAMP (r->end.col, 0, GNM_MAX_COLS - 1);
4853 d (4, range_dump (r, ";\n"););
4856 void
4857 xls_read_range16 (GnmRange *r, guint8 const *data)
4859 r->start.row = GSF_LE_GET_GUINT16 (data + 0);
4860 r->end.row = GSF_LE_GET_GUINT16 (data + 2);
4861 r->start.col = GSF_LE_GET_GUINT16 (data + 4);
4862 r->end.col = GSF_LE_GET_GUINT16 (data + 6);
4864 r->start.row = CLAMP (r->start.row, 0, GNM_MAX_ROWS - 1);
4865 r->end.row = CLAMP (r->end.row, 0, GNM_MAX_ROWS - 1);
4866 r->start.col = CLAMP (r->start.col, 0, GNM_MAX_COLS - 1);
4867 r->end.col = CLAMP (r->end.col, 0, GNM_MAX_COLS - 1);
4869 d (4, range_dump (r, ";\n"););
4872 void
4873 xls_read_range8 (GnmRange *r, guint8 const *data)
4875 r->start.row = GSF_LE_GET_GUINT16 (data + 0);
4876 r->end.row = GSF_LE_GET_GUINT16 (data + 2);
4877 r->start.col = GSF_LE_GET_GUINT8 (data + 4);
4878 r->end.col = GSF_LE_GET_GUINT8 (data + 5);
4879 d (4, range_dump (r, ";\n"););
4883 * No documentation exists for this record, but this makes
4884 * sense given the other record formats.
4886 static void
4887 excel_read_MERGECELLS (BiffQuery *q, ExcelReadSheet *esheet)
4889 int num_merged;
4890 guint8 const *data = q->data + 2;
4891 GnmRange r;
4892 GSList *overlap;
4894 XL_CHECK_CONDITION (q->length >= 2);
4895 num_merged = GSF_LE_GET_GUINT16 (q->data);
4896 XL_CHECK_CONDITION (q->length == (unsigned int)(2 + 8 * num_merged));
4898 for (; num_merged-- > 0 ; data += 8) {
4899 xls_read_range16 (&r, data);
4900 overlap = gnm_sheet_merge_get_overlap (esheet->sheet, &r);
4901 if (overlap) {
4902 GnmRange *r2 = overlap->data;
4904 /* Unmerge r2, then merge (r U r2) */
4906 /* Do this early because the _remove can kill r2. */
4907 r = range_union (&r, r2);
4909 gnm_sheet_merge_remove (esheet->sheet, r2);
4910 g_slist_free (overlap);
4912 gnm_sheet_merge_add (esheet->sheet, &r, FALSE,
4913 GO_CMD_CONTEXT (esheet->container.importer->context));
4917 static void
4918 excel_read_DIMENSIONS (BiffQuery *q, ExcelReadSheet *esheet)
4920 GnmRange r;
4921 const char *key = "DIMENSION";
4923 if (!esheet)
4924 return;
4926 if (esheet_ver (esheet) >= MS_BIFF_V8) {
4927 XL_CHECK_CONDITION (q->length >= 12);
4928 xls_read_range32 (&r, q->data);
4929 } else {
4930 XL_CHECK_CONDITION (q->length >= 8);
4931 xls_read_range16 (&r, q->data);
4934 if (range_width (&r) <= 1 || range_height (&r) <= 1) {
4935 g_object_set_data (G_OBJECT (esheet->sheet), key, NULL);
4936 d (1, g_printerr ("Dimension = -\n"););
4937 } else {
4938 r.end.col--;
4939 r.end.row--;
4940 d (1, g_printerr ("Dimension = %s\n", range_as_string (&r)););
4942 /* Hack: we need to get this information out to
4943 table_cellregion_read */
4944 g_object_set_data_full (G_OBJECT (esheet->sheet),
4945 key, gnm_range_dup (&r),
4946 g_free);
4950 static MSContainer *
4951 sheet_container (ExcelReadSheet *esheet)
4953 ms_container_set_blips (&esheet->container, esheet->container.importer->container.blips);
4954 return &esheet->container;
4957 static gboolean
4958 excel_read_sheet_PROTECT (BiffQuery *q, ExcelReadSheet *esheet)
4960 gboolean is_protected = TRUE;
4962 /* MS Docs fail to mention that in some stream this
4963 * record can have size zero. I assume the in that
4964 * case its existence is the flag. */
4965 if (q->length >= 2)
4966 is_protected = (1 == GSF_LE_GET_GUINT16 (q->data));
4968 esheet->sheet->is_protected = is_protected;
4970 return is_protected;
4973 static gboolean
4974 excel_read_workbook_PROTECT (BiffQuery *q, WorkbookView *wb_view)
4976 gboolean is_protected = TRUE;
4978 /* MS Docs fail to mention that in some stream this
4979 * record can have size zero. I assume the in that
4980 * case its existence is the flag. */
4981 if (q->length >= 2)
4982 is_protected = (1 == GSF_LE_GET_GUINT16 (q->data));
4984 wb_view->is_protected = is_protected;
4986 return is_protected;
4989 static void
4990 excel_read_WSBOOL (BiffQuery *q, ExcelReadSheet *esheet)
4992 guint16 options;
4994 XL_CHECK_CONDITION (q->length == 2);
4996 options = GSF_LE_GET_GUINT16 (q->data);
4997 /* 0x0001 automatic page breaks are visible */
4998 /* 0x0010 the sheet is a dialog sheet */
4999 /* 0x0020 automatic styles are not applied to an outline */
5000 esheet->sheet->outline_symbols_below = 0 != (options & 0x040);
5001 esheet->sheet->outline_symbols_right = 0 != (options & 0x080);
5002 if (NULL != esheet->sheet->print_info)
5003 esheet->sheet->print_info->scaling.type =
5004 (options & 0x100) ? PRINT_SCALE_FIT_PAGES : PRINT_SCALE_PERCENTAGE;
5006 /* 0x0200 biff 3-4 0 == save external linked values, 1 == do not save */
5007 /* XL docs wrong 0xc00 no 0x600, OOo docs wrong no distinct row vs col */
5008 esheet->sheet->display_outlines = 0 != (options & 0xc00);
5010 /* Biff4 0x3000 window arrangement
5011 * 0b == tiled
5012 * 1b == arrange horiz
5013 * 10b == arrange vert
5014 * 11b == cascade */
5016 /* biff 4-8 0x4000, 0 == std expr eval, 1 == alt expr eval ? */
5017 /* biff 4-8 0x8000, 0 == std fmla entry, 1 == alt fmla entry ? */
5020 static void
5021 excel_read_CALCCOUNT (BiffQuery *q, GnmXLImporter *importer)
5023 guint16 count;
5025 XL_CHECK_CONDITION (q->length == 2);
5027 count = GSF_LE_GET_GUINT16 (q->data);
5029 workbook_iteration_max_number (importer->wb, count);
5032 static void
5033 excel_read_CALCMODE (BiffQuery *q, GnmXLImporter *importer)
5035 XL_CHECK_CONDITION (q->length == 2);
5036 workbook_set_recalcmode (importer->wb,
5037 GSF_LE_GET_GUINT16 (q->data) != 0);
5040 static void
5041 excel_read_DELTA (BiffQuery *q, GnmXLImporter *importer)
5043 double tolerance;
5045 XL_CHECK_CONDITION (q->length == 8);
5047 tolerance = gsf_le_get_double (q->data);
5048 XL_CHECK_CONDITION (tolerance >= 0);
5050 workbook_iteration_tolerance (importer->wb, tolerance);
5053 static void
5054 excel_read_ITERATION (BiffQuery *q, GnmXLImporter *importer)
5056 guint16 enabled;
5058 XL_CHECK_CONDITION (q->length == 2);
5060 enabled = GSF_LE_GET_GUINT16 (q->data);
5061 workbook_iteration_enabled (importer->wb, enabled != 0);
5064 static void
5065 excel_read_PANE (BiffQuery *q, ExcelReadSheet *esheet, WorkbookView *wb_view)
5067 XL_CHECK_CONDITION (q->length == 10);
5068 if (esheet->freeze_panes) {
5069 guint16 x = GSF_LE_GET_GUINT16 (q->data + 0);
5070 guint16 y = GSF_LE_GET_GUINT16 (q->data + 2);
5071 guint16 rwTop = GSF_LE_GET_GUINT16 (q->data + 4);
5072 guint16 colLeft = GSF_LE_GET_GUINT16 (q->data + 6);
5073 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
5074 GnmCellPos frozen, unfrozen;
5076 esheet->active_pane = GSF_LE_GET_GUINT16 (q->data + 8);
5077 if (esheet->active_pane > 3) {
5078 g_warning ("Invalid pane '%u' selected", esheet->active_pane);
5079 esheet->active_pane = 3;
5082 g_return_if_fail (sv != NULL);
5084 frozen = unfrozen = sv->initial_top_left;
5085 if (x > 0)
5086 unfrozen.col += x;
5087 else
5088 colLeft = sv->initial_top_left.col;
5089 if (y > 0)
5090 unfrozen.row += y;
5091 else
5092 rwTop = sv->initial_top_left.row;
5093 sv_freeze_panes (sv, &frozen, &unfrozen);
5094 sv_set_initial_top_left (sv, colLeft, rwTop);
5095 } else {
5096 g_warning ("EXCEL : no support for split panes yet (%s)", esheet->sheet->name_unquoted);
5100 static void
5101 excel_read_WINDOW2 (BiffQuery *q, ExcelReadSheet *esheet, WorkbookView *wb_view)
5103 SheetView *sv = sheet_get_view (esheet->sheet, esheet->container.importer->wbv);
5104 guint16 top_row = 0;
5105 guint16 left_col = 0;
5106 guint32 biff_pat_col;
5107 gboolean set_grid_color;
5109 if (q->opcode == BIFF_WINDOW2_v2) {
5110 guint16 options;
5112 XL_CHECK_CONDITION (q->length >= 10);
5114 options = GSF_LE_GET_GUINT16 (q->data + 0);
5115 esheet->sheet->display_formulas = ((options & 0x0001) != 0);
5116 esheet->sheet->hide_grid = ((options & 0x0002) == 0);
5117 esheet->sheet->hide_col_header =
5118 esheet->sheet->hide_row_header = ((options & 0x0004) == 0);
5119 esheet->freeze_panes = ((options & 0x0008) != 0);
5120 esheet->sheet->hide_zero = ((options & 0x0010) == 0);
5121 set_grid_color = (options & 0x0020) == 0;
5122 g_object_set (esheet->sheet, "text-is-rtl", (options & 0x0040) != 0, NULL);
5124 top_row = GSF_LE_GET_GUINT16 (q->data + 2);
5125 left_col = GSF_LE_GET_GUINT16 (q->data + 4);
5126 biff_pat_col = GSF_LE_GET_GUINT32 (q->data + 6);
5128 d (0, if (options & 0x0200) g_printerr ("Sheet flag selected\n"););
5129 if (options & 0x0400)
5130 wb_view_sheet_focus (wb_view, esheet->sheet);
5132 if (esheet_ver (esheet) >= MS_BIFF_V8 && q->length >= 14) {
5133 d (2, {
5134 guint16 const pageBreakZoom = GSF_LE_GET_GUINT16 (q->data + 10);
5135 guint16 const normalZoom = GSF_LE_GET_GUINT16 (q->data + 12);
5136 g_printerr ("%hx %hx\n", normalZoom, pageBreakZoom);
5139 } else {
5140 XL_CHECK_CONDITION (q->length >= 14);
5142 esheet->sheet->display_formulas = (q->data[0] != 0);
5143 esheet->sheet->hide_grid = (q->data[1] == 0);
5144 esheet->sheet->hide_col_header =
5145 esheet->sheet->hide_row_header = (q->data[2] == 0);
5146 esheet->freeze_panes = (q->data[3] != 0);
5147 esheet->sheet->hide_zero = (q->data[4] == 0);
5148 set_grid_color = (q->data[9] == 0);
5150 top_row = GSF_LE_GET_GUINT16 (q->data + 5);
5151 left_col = GSF_LE_GET_GUINT16 (q->data + 7);
5152 biff_pat_col = GSF_LE_GET_GUINT32 (q->data + 10);
5155 if (set_grid_color) {
5156 GnmColor *pattern_color;
5157 if (esheet_ver (esheet) >= MS_BIFF_V8) {
5158 /* Get style color from palette*/
5159 pattern_color = excel_palette_get (
5160 esheet->container.importer,
5161 biff_pat_col & 0x7f);
5162 } else {
5163 guint8 r, g, b;
5165 r = (guint8) biff_pat_col;
5166 g = (guint8) (biff_pat_col >> 8);
5167 b = (guint8) (biff_pat_col >> 16);
5168 pattern_color = gnm_color_new_rgb8 (r, g, b);
5170 d (2, g_printerr ("auto pattern color "
5171 "0x%08x\n",
5172 pattern_color->go_color););
5173 sheet_style_set_auto_pattern_color (
5174 esheet->sheet, pattern_color);
5177 g_return_if_fail (sv != NULL);
5179 /* until we import multiple views unfreeze just in case a previous view
5180 * had frozen */
5181 sv_freeze_panes (sv, NULL, NULL);
5183 /* NOTE : This is top left of screen even if frozen, modify when
5184 * we read PANE */
5185 sv_set_initial_top_left (sv, left_col, top_row);
5188 static void
5189 excel_read_CF_border (GnmStyle *style, ExcelReadSheet *esheet,
5190 GnmStyleBorderLocation type,
5191 unsigned xl_pat_index, unsigned xl_color_index)
5193 GnmStyleElement elem = GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (type);
5194 gnm_style_set_border (style, elem,
5195 gnm_style_border_fetch (biff_xf_map_border (xl_pat_index),
5196 excel_palette_get (esheet->container.importer,
5197 xl_color_index),
5198 gnm_style_border_get_orientation (type)));
5201 static void
5202 excel_read_CF (BiffQuery *q, ExcelReadSheet *esheet, GnmStyleConditions *sc,
5203 GnmXLImporter *importer)
5205 guint8 type, op;
5206 guint16 expr0_len,expr1_len;
5207 guint32 flags;
5208 guint16 flags2;
5209 unsigned offset;
5210 GnmStyleCond *cond = NULL;
5211 GnmStyleCondOp cop;
5212 GnmStyle *overlay = NULL;
5214 XL_CHECK_CONDITION (q->length >= 12);
5216 type = GSF_LE_GET_GUINT8 (q->data + 0);
5217 op = GSF_LE_GET_GUINT8 (q->data + 1);
5218 expr0_len = GSF_LE_GET_GUINT16 (q->data + 2);
5219 expr1_len = GSF_LE_GET_GUINT16 (q->data + 4);
5220 flags = GSF_LE_GET_GUINT32 (q->data + 6);
5221 flags2 = GSF_LE_GET_GUINT16 (q->data + 10);
5223 XL_CHECK_CONDITION (q->length >= 10u + expr0_len + expr1_len);
5225 d (1, {
5226 gsf_mem_dump (q->data+6, 6);
5227 g_printerr ("cond type = %d, op type = %d, flags = 0x%08x\n", (int)type, (int)op, flags);
5229 switch (type) {
5230 case 1 :
5231 switch (op) {
5232 case 0x01: cop = GNM_STYLE_COND_BETWEEN; break;
5233 case 0x02: cop = GNM_STYLE_COND_NOT_BETWEEN; break;
5234 case 0x03: cop = GNM_STYLE_COND_EQUAL; break;
5235 case 0x04: cop = GNM_STYLE_COND_NOT_EQUAL; break;
5236 case 0x05: cop = GNM_STYLE_COND_GT; break;
5237 case 0x06: cop = GNM_STYLE_COND_LT; break;
5238 case 0x07: cop = GNM_STYLE_COND_GTE; break;
5239 case 0x08: cop = GNM_STYLE_COND_LTE; break;
5240 default:
5241 g_warning ("EXCEL : Unknown condition (%d) for conditional format in sheet %s.",
5242 op, esheet->sheet->name_unquoted);
5243 return;
5245 break;
5246 case 2:
5247 cop = GNM_STYLE_COND_CUSTOM;
5248 break;
5250 default:
5251 g_warning ("EXCEL : Unknown condition type (%d) for format in sheet %s.",
5252 (int)type, esheet->sheet->name_unquoted);
5253 return;
5256 cond = gnm_style_cond_new (cop, esheet->sheet);
5258 if (expr0_len > 0) {
5259 GnmExprTop const *texpr =
5260 ms_sheet_parse_expr_internal
5261 (esheet,
5262 q->data + q->length - expr0_len - expr1_len,
5263 expr0_len);
5264 gnm_style_cond_set_expr (cond, texpr, 0);
5265 gnm_expr_top_unref (texpr);
5267 if (expr1_len > 0) {
5268 GnmExprTop const *texpr =
5269 ms_sheet_parse_expr_internal
5270 (esheet,
5271 q->data + q->length - expr1_len,
5272 expr1_len);
5273 gnm_style_cond_set_expr (cond, texpr, 1);
5274 gnm_expr_top_unref (texpr);
5277 /* Reverse the alternate-expression treatment on save. */
5278 gnm_style_cond_canonicalize (cond);
5280 /* UNDOCUMENTED : the format of the conditional format
5281 * is unspecified.
5283 * header == 6
5284 * 0xff : I'll guess fonts
5285 * uint8 : 0xff = no border
5286 * 0xf7 = R
5287 * 0xfb = L
5288 * 0xef = T
5289 * 0xdf = B
5290 * 0xc3 == T,L,B,R
5291 * uint8 : 0x3f == no background elements,
5292 * 0x3b == background
5293 * 0x3a == background & pattern
5294 * 0x38 == background & pattern & pattern colour
5295 * uint8 : 0x04 = font | 0x10 = border | 0x20 = colour
5296 * 0x02 : ?
5297 * 0x00 : ?
5299 * font == 118
5300 * border == 8
5301 * colour == 4
5302 * Similar to XF from biff7
5305 overlay = gnm_style_new ();
5307 offset = 6 /* CF record header */ + 6; /* format header */
5309 if (flags & 0x02000000) { /* number format */
5310 gboolean ignore = (flags & 0x00080000) != 0;
5312 XL_CHECK_CONDITION_FULL (q->length >= offset + 2, goto fail;);
5314 if (flags2 & 1) {
5315 /* Format as string */
5316 guint bytes = GSF_LE_GET_GUINT16 (q->data + offset);
5317 if (!ignore) {
5318 char *xlfmt = excel_biff_text_2 (importer, q, offset + 2);
5319 GOFormat *fmt = go_format_new_from_XL (xlfmt);
5320 gnm_style_set_format (overlay, fmt);
5321 go_format_unref (fmt);
5322 g_free (xlfmt);
5324 offset += bytes;
5325 } else {
5326 /* Format as index */
5327 offset += 2;
5331 if (flags & 0x04000000) { /* font */
5332 guint32 size, colour;
5333 guint8 tmp8, font_flags;
5334 guint8 const *data = q->data + offset;
5336 XL_CHECK_CONDITION_FULL (q->length >= offset + 64 + 54, goto fail;);
5338 if (data[0] && GSF_LE_GET_GUINT16 (data + 116) > 0) {
5339 char *font = excel_biff_text_1
5340 (importer, q, offset);
5341 gnm_style_set_font_name (overlay, font);
5342 g_free (font);
5345 data += 64;
5347 if (0xFFFFFFFF != (size = GSF_LE_GET_GUINT32 (data)))
5348 gnm_style_set_font_size (overlay, size / 20.);
5349 if (0xFFFFFFFF != (colour = GSF_LE_GET_GUINT32 (data + 16)))
5350 gnm_style_set_font_color (overlay,
5351 excel_palette_get (esheet->container.importer,
5352 colour));
5354 if (0 == GSF_LE_GET_GUINT8 (data + 36)) {
5355 gnm_style_set_font_bold (overlay,
5356 GSF_LE_GET_GUINT16 (data + 8) >= 0x2bc);
5359 tmp8 = GSF_LE_GET_GUINT8 (data + 4);
5360 font_flags = GSF_LE_GET_GUINT8 (data + 24);
5361 if (0 == (font_flags & 2))
5362 gnm_style_set_font_italic (overlay, 0 != (tmp8 & 2));
5364 if (0 == (font_flags & 0x80))
5365 gnm_style_set_font_strike (overlay, 0 != (tmp8 & 0x80));
5367 if (0 == GSF_LE_GET_GUINT8 (data + 28)) {
5368 switch (GSF_LE_GET_GUINT8 (data + 10)) {
5369 default : g_printerr ("Unknown script %d\n", GSF_LE_GET_GUINT8 (data));
5370 /* fall through */
5371 case 0: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_STANDARD); break;
5372 case 1: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_SUPER); break;
5373 case 2: gnm_style_set_font_script (overlay, GO_FONT_SCRIPT_SUB); break;
5376 if (0 == GSF_LE_GET_GUINT8 (data + 32)) {
5377 MsBiffFontUnderline mul;
5378 switch (GSF_LE_GET_GUINT8 (data + 12)) {
5379 default :
5380 case 0:
5381 mul = XLS_ULINE_NONE;
5382 break;
5383 case 1:
5384 mul = XLS_ULINE_SINGLE;
5385 break;
5386 case 2:
5387 mul = XLS_ULINE_DOUBLE;
5388 break;
5389 case 0x21:
5390 mul = XLS_ULINE_SINGLE_ACC;
5391 break;
5392 case 0x22:
5393 mul = XLS_ULINE_DOUBLE_ACC;
5394 break;
5396 gnm_style_set_font_uline
5397 (overlay,
5398 xls_uline_to_gnm_underline (mul));
5401 d (3, {
5402 g_printerr ("%s\n", "Font");
5403 gsf_mem_dump (data, 54);
5406 offset += 118;
5409 if (flags & 0x08000000) { /* alignment block */
5410 guint16 d1, d2;
5412 XL_CHECK_CONDITION_FULL (q->length >= offset + 8, goto fail;);
5413 d1 = GSF_LE_GET_GUINT16 (q->data + offset);
5414 d2 = GSF_LE_GET_GUINT16 (q->data + offset + 2);
5416 if (0 == (flags & 0x1))
5417 gnm_style_set_align_h (overlay,
5418 halign_from_excel ((d1 >> 0) & 7));
5420 if (0 == (flags & 0x2))
5421 gnm_style_set_align_v (overlay,
5422 valign_from_excel ((d1 >> 4) & 7));
5424 if (0 == (flags & 0x4))
5425 gnm_style_set_wrap_text (overlay, ((d1 >> 3) & 1));
5427 if (0 == (flags & 0x8)) {
5428 int r = (esheet_ver (esheet) >= MS_BIFF_V8
5429 ? rotation_from_excel_v8 (d1 >> 8)
5430 : rotation_from_excel_v7 (d1 >> 8));
5431 gnm_style_set_rotation (overlay, r);
5434 if (0 == (flags & 0x20))
5435 gnm_style_set_indent (overlay, ((d2 >> 0) & 0xf));
5437 if (0 == (flags & 0x40))
5438 gnm_style_set_shrink_to_fit (overlay, ((d2 >> 4) & 1));
5440 offset += 8;
5443 if (flags & 0x10000000) { /* borders */
5444 guint32 d0, d1;
5446 XL_CHECK_CONDITION_FULL (q->length >= offset + 8, goto fail;);
5447 d0 = GSF_LE_GET_GUINT32 (q->data + offset);
5448 d1 = GSF_LE_GET_GUINT32 (q->data + offset + 4);
5450 if (0 == (flags & 0x0400))
5451 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_LEFT,
5452 (d0 >> 0) & 0xf,
5453 (d0 >> 16) & 0x7f);
5454 if (0 == (flags & 0x0800))
5455 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_RIGHT,
5456 (d0 >> 4) & 0xf,
5457 (d0 >> 23) & 0x7f);
5458 if (0 == (flags & 0x1000))
5459 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_TOP,
5460 (d0 >> 8) & 0xf,
5461 (d1 >> 0) & 0x7f);
5462 if (0 == (flags & 0x2000))
5463 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_BOTTOM,
5464 (d0 >> 12) & 0xf,
5465 (d1 >> 7) & 0x7f);
5466 if (0 == (flags & 0x4000) && (d0 & 0x80000000)) {
5467 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_DIAG,
5468 (d1 >> 21) & 0xf,
5469 (d1 >> 14) & 0x7f);
5471 if (0 == (flags & 0x8000) && (d0 & 0x40000000))
5472 excel_read_CF_border (overlay, esheet, GNM_STYLE_BORDER_REV_DIAG,
5473 (d1 >> 21) & 0xf,
5474 (d1 >> 14) & 0x7f);
5476 offset += 8;
5479 if (flags & 0x20000000) { /* pattern */
5480 guint32 background_flags;
5481 int pattern = 0;
5483 XL_CHECK_CONDITION_FULL (q->length >= offset + 4, goto fail;);
5484 background_flags = GSF_LE_GET_GUINT32 (q->data + offset);
5486 if (0 == (flags & 0x10000))
5487 gnm_style_set_pattern (overlay,
5488 pattern = excel_map_pattern_index_from_excel (
5489 (background_flags >> 10) & 0x3F));
5490 if (0 == (flags & 0x20000))
5491 gnm_style_set_pattern_color (overlay,
5492 excel_palette_get (esheet->container.importer,
5493 (background_flags >> 16) & 0x7F));
5494 if (0 == (flags & 0x40000))
5495 gnm_style_set_back_color (overlay,
5496 excel_palette_get (esheet->container.importer,
5497 (background_flags >> 23) & 0x7F));
5499 offset += 4;
5502 if (flags & 0x40000000) { /* protection */
5503 offset += 2;
5506 XL_CHECK_CONDITION_FULL (q->length == offset + expr0_len + expr1_len, goto fail;);
5508 d (1, gnm_style_dump (overlay););
5510 gnm_style_cond_set_overlay (cond, overlay);
5511 gnm_style_unref (overlay);
5512 gnm_style_conditions_insert (sc, cond, -1);
5513 gnm_style_cond_free (cond);
5514 return;
5516 fail:
5517 if (cond)
5518 gnm_style_cond_free (cond);
5519 if (overlay)
5520 gnm_style_unref (overlay);
5523 static void
5524 excel_read_CONDFMT (BiffQuery *q, ExcelReadSheet *esheet,
5525 GnmXLImporter *importer)
5527 guint16 num_fmts, num_areas;
5528 unsigned i;
5529 guint8 const *data;
5530 GnmStyleConditions *sc;
5531 GnmStyle *style;
5532 GnmRange region;
5533 GSList *ptr, *regions = NULL;
5535 XL_CHECK_CONDITION (q->length >= 14);
5537 num_fmts = GSF_LE_GET_GUINT16 (q->data + 0);
5538 num_areas = GSF_LE_GET_GUINT16 (q->data + 12);
5540 d (1, g_printerr ("Num areas == %hu\n", num_areas););
5541 #if 0
5542 /* The bounding box or the region containing all conditional formats.
5543 * It seems like this region is 0,0 -> 0xffff,0xffff when there are no
5544 * regions.
5546 if (num_areas > 0)
5547 xls_read_range16 (&region, q->data+4);
5548 #endif
5550 data = q->data + 14;
5551 for (i = 0 ; i < num_areas && (data+8) <= (q->data + q->length) ; i++, data += 8) {
5552 xls_read_range16 (&region, data);
5553 regions = g_slist_prepend (regions, gnm_range_dup (&region));
5556 XL_CHECK_CONDITION (data == q->data + q->length);
5558 sc = gnm_style_conditions_new (esheet->sheet);
5559 for (i = 0 ; i < num_fmts ; i++) {
5560 guint16 next;
5561 if (!ms_biff_query_peek_next (q, &next) || next != BIFF_CF) {
5562 g_object_unref (sc);
5563 g_slist_free_full (regions, g_free);
5564 g_warning ("EXCEL: missing CF record");
5565 return;
5567 ms_biff_query_next (q);
5568 excel_read_CF (q, esheet, sc, importer);
5571 style = gnm_style_new ();
5572 gnm_style_set_conditions (style, sc);
5573 for (ptr = regions ; ptr != NULL ; ptr = ptr->next) {
5574 gnm_style_ref (style);
5575 sheet_style_apply_range (esheet->sheet, ptr->data, style);
5576 g_free (ptr->data);
5578 gnm_style_unref (style);
5579 g_slist_free (regions);
5582 static void
5583 excel_read_DV (BiffQuery *q, ExcelReadSheet *esheet)
5585 GnmExprTop const *texpr1 = NULL;
5586 GnmExprTop const *texpr2 = NULL;
5587 int expr1_len, expr2_len;
5588 char *input_msg, *error_msg, *input_title, *error_title;
5589 guint32 options, len;
5590 guint8 const *data, *expr1_dat, *expr2_dat;
5591 guint8 const *end = q->data + q->length;
5592 int i, col, row;
5593 GnmRange r;
5594 ValidationStyle style;
5595 ValidationType type;
5596 ValidationOp op;
5597 GSList *ptr, *ranges = NULL;
5598 GnmStyle *mstyle;
5600 XL_CHECK_CONDITION (q->length >= 4);
5601 options = GSF_LE_GET_GUINT32 (q->data);
5602 data = q->data + 4;
5604 XL_CHECK_CONDITION (data+3 <= end);
5605 input_title = excel_get_text (esheet->container.importer, data + 2,
5606 GSF_LE_GET_GUINT16 (data), &len, NULL,
5607 end - (data + 2));
5608 data += len + 2;
5610 XL_CHECK_CONDITION (data+3 <= end);
5611 error_title = excel_get_text (esheet->container.importer, data + 2,
5612 GSF_LE_GET_GUINT16 (data), &len, NULL,
5613 end - (data + 2));
5614 data += len + 2;
5616 XL_CHECK_CONDITION (data+3 <= end);
5617 input_msg = excel_get_text (esheet->container.importer, data + 2,
5618 GSF_LE_GET_GUINT16 (data), &len, NULL,
5619 end - (data + 2));
5620 data += len + 2;
5622 XL_CHECK_CONDITION (data+3 <= end);
5623 error_msg = excel_get_text (esheet->container.importer, data + 2,
5624 GSF_LE_GET_GUINT16 (data), &len, NULL,
5625 end - (data + 2));
5626 data += len + 2;
5628 d (1, {
5629 g_printerr ("Input Title : '%s'\n", input_title);
5630 g_printerr ("Input Msg : '%s'\n", input_msg);
5631 g_printerr ("Error Title : '%s'\n", error_title);
5632 g_printerr ("Error Msg : '%s'\n", error_msg);
5635 XL_CHECK_CONDITION (data+4 <= end);
5636 expr1_len = GSF_LE_GET_GUINT16 (data);
5637 d (5, g_printerr ("Unknown1 = %hx\n", GSF_LE_GET_GUINT16 (data+2)););
5638 expr1_dat = data + 4; /* TODO : What are the missing 2 bytes ? */
5639 data += expr1_len + 4;
5641 XL_CHECK_CONDITION (data+4 <= end);
5642 expr2_len = GSF_LE_GET_GUINT16 (data);
5643 d (5, g_printerr ("Unknown2 = %hx\n", GSF_LE_GET_GUINT16 (data+2)););
5644 expr2_dat = data + 4; /* TODO : What are the missing 2 bytes ? */
5645 data += expr2_len + 4;
5647 XL_CHECK_CONDITION (data+2 < end);
5648 i = GSF_LE_GET_GUINT16 (data);
5649 data += 2;
5650 XL_CHECK_CONDITION ((end - data) / 8 >= i);
5652 for (; i-- > 0 ; data += 8) {
5653 xls_read_range16 (&r, data);
5654 ranges = g_slist_prepend (ranges, gnm_range_dup (&r));
5657 /* these enums align, but lets be explicit so that the filter
5658 * is easier to read.
5660 switch (options & 0x0f) {
5661 case 0 : type = GNM_VALIDATION_TYPE_ANY; break;
5662 case 1 : type = GNM_VALIDATION_TYPE_AS_INT; break;
5663 case 2 : type = GNM_VALIDATION_TYPE_AS_NUMBER; break;
5664 case 3 : type = GNM_VALIDATION_TYPE_IN_LIST; break;
5665 case 4 : type = GNM_VALIDATION_TYPE_AS_DATE; break;
5666 case 5 : type = GNM_VALIDATION_TYPE_AS_TIME; break;
5667 case 6 : type = GNM_VALIDATION_TYPE_TEXT_LENGTH; break;
5668 case 7 : type = GNM_VALIDATION_TYPE_CUSTOM; break;
5669 default :
5670 g_warning ("EXCEL : Unknown constraint type %d", options & 0x0f);
5671 return;
5674 switch ((options >> 4) & 0x07) {
5675 case 0 : style = GNM_VALIDATION_STYLE_STOP; break;
5676 case 1 : style = GNM_VALIDATION_STYLE_WARNING; break;
5677 case 2 : style = GNM_VALIDATION_STYLE_INFO; break;
5678 default :
5679 g_warning ("EXCEL : Unknown validation style %d",
5680 (options >> 4) & 0x07);
5681 return;
5683 if (!(options & 0x80000))
5684 style = GNM_VALIDATION_STYLE_NONE;
5686 if (type == GNM_VALIDATION_TYPE_CUSTOM || type == GNM_VALIDATION_TYPE_IN_LIST)
5687 op = GNM_VALIDATION_OP_NONE;
5688 else
5689 switch ((options >> 20) & 0x0f) {
5690 case 0: op = GNM_VALIDATION_OP_BETWEEN; break;
5691 case 1: op = GNM_VALIDATION_OP_NOT_BETWEEN; break;
5692 case 2: op = GNM_VALIDATION_OP_EQUAL; break;
5693 case 3: op = GNM_VALIDATION_OP_NOT_EQUAL; break;
5694 case 4: op = GNM_VALIDATION_OP_GT; break;
5695 case 5: op = GNM_VALIDATION_OP_LT; break;
5696 case 6: op = GNM_VALIDATION_OP_GTE; break;
5697 case 7: op = GNM_VALIDATION_OP_LTE; break;
5698 default :
5699 g_warning ("EXCEL : Unknown constraint operator %d",
5700 (options >> 20) & 0x0f);
5701 return;
5704 if (ranges != NULL) {
5705 GnmRange const *r = ranges->data;
5706 col = r->start.col;
5707 row = r->start.row;
5708 } else
5709 col = row = 0;
5711 if (expr1_len > 0)
5712 texpr1 = excel_parse_formula (&esheet->container, esheet,
5713 col, row,
5714 expr1_dat, expr1_len, 0 /* FIXME */,
5715 TRUE, NULL);
5717 if (expr2_len > 0)
5718 texpr2 = excel_parse_formula (&esheet->container, esheet,
5719 col, row,
5720 expr2_dat, expr2_len, 0 /* FIXME */,
5721 TRUE, NULL);
5723 d (1, g_printerr ("style = %d, type = %d, op = %d\n",
5724 style, type, op););
5726 mstyle = gnm_style_new ();
5727 gnm_style_set_validation
5728 (mstyle,
5729 gnm_validation_new (style, type, op,
5730 esheet->sheet,
5731 error_title, error_msg,
5732 texpr1,
5733 texpr2,
5734 options & 0x0100, 0 == (options & 0x0200)));
5735 if (options & 0x40000)
5736 gnm_style_set_input_msg (mstyle,
5737 gnm_input_msg_new (input_msg, input_title));
5739 for (ptr = ranges; ptr != NULL ; ptr = ptr->next) {
5740 GnmRange *r = ptr->data;
5741 gnm_style_ref (mstyle);
5742 sheet_style_apply_range (esheet->sheet, r, mstyle);
5743 d (1, range_dump (r, "\n"););
5744 g_free (r);
5746 g_slist_free (ranges);
5747 gnm_style_unref (mstyle);
5748 g_free (input_msg);
5749 g_free (error_msg);
5750 g_free (input_title);
5751 g_free (error_title);
5754 static void
5755 excel_read_DVAL (BiffQuery *q, ExcelReadSheet *esheet)
5757 guint16 options;
5758 guint32 input_coord_x, input_coord_y, drop_down_id, dv_count;
5759 unsigned i;
5761 XL_CHECK_CONDITION (q->length == 18);
5763 options = GSF_LE_GET_GUINT16 (q->data + 0);
5764 input_coord_x = GSF_LE_GET_GUINT32 (q->data + 2);
5765 input_coord_y = GSF_LE_GET_GUINT32 (q->data + 6);
5766 drop_down_id = GSF_LE_GET_GUINT32 (q->data + 10);
5767 dv_count = GSF_LE_GET_GUINT32 (q->data + 14);
5769 d (5, if (options & 0x1) g_printerr ("DV input window is closed\n"););
5770 d (5, if (options & 0x2) g_printerr ("DV input window is pinned\n"););
5771 d (5, if (options & 0x4) g_printerr ("DV info has been cached ??\n"););
5773 for (i = 0 ; i < dv_count ; i++) {
5774 guint16 next;
5775 if (!ms_biff_query_peek_next (q, &next) || next != BIFF_DV) {
5776 g_warning ("EXCEL: missing DV record");
5777 return;
5779 ms_biff_query_next (q);
5780 excel_read_DV (q, esheet);
5784 static void
5785 excel_read_SHEETPROTECTION (BiffQuery *q, Sheet *sheet)
5787 guint16 flags;
5789 g_return_if_fail (sheet != NULL);
5791 /* 2003/2007 == 23
5792 * XP == 19 */
5793 XL_CHECK_CONDITION (q->length >= 19);
5795 if (q->length >= 23) {
5796 flags = GSF_LE_GET_GUINT16 (q->data + 19);
5797 sheet->protected_allow.edit_objects = (flags & (1 << 0)) != 0;
5798 sheet->protected_allow.edit_scenarios = (flags & (1 << 1)) != 0;
5799 sheet->protected_allow.cell_formatting = (flags & (1 << 2)) != 0;
5800 sheet->protected_allow.column_formatting = (flags & (1 << 3)) != 0;
5801 sheet->protected_allow.row_formatting = (flags & (1 << 4)) != 0;
5802 sheet->protected_allow.insert_columns = (flags & (1 << 5)) != 0;
5803 sheet->protected_allow.insert_rows = (flags & (1 << 6)) != 0;
5804 sheet->protected_allow.insert_hyperlinks = (flags & (1 << 7)) != 0;
5805 sheet->protected_allow.delete_columns = (flags & (1 << 8)) != 0;
5806 sheet->protected_allow.delete_rows = (flags & (1 << 9)) != 0;
5807 sheet->protected_allow.select_locked_cells = (flags & (1 << 10)) != 0;
5808 sheet->protected_allow.sort_ranges = (flags & (1 << 11)) != 0;
5809 sheet->protected_allow.edit_auto_filters = (flags & (1 << 12)) != 0;
5810 sheet->protected_allow.edit_pivottable = (flags & (1 << 13)) != 0;
5811 sheet->protected_allow.select_unlocked_cells = (flags & (1 << 14)) != 0;
5815 /***********************************************************************/
5817 static guchar *
5818 read_utf16_str (int word_len, guint8 const *data)
5820 int i;
5821 gunichar2 *uni_text = g_alloca (word_len * sizeof (gunichar2));
5823 /* be wary about endianness */
5824 for (i = 0 ; i < word_len ; i++, data += 2)
5825 uni_text [i] = GSF_LE_GET_GUINT16 (data);
5827 return (guchar *)g_utf16_to_utf8 (uni_text, word_len, NULL, NULL, NULL);
5831 * XL (at least XL 2000) stores URLs exactly as input by the user. No
5832 * quoting, no mime encoding of email headers. If cgi parameters are
5833 * separated by '&', '&' is stored, not '&amp;'. An email subject in
5834 * cyrillic characters is stored as cyrillic characters, not as an
5835 * RFC 2047 MIME encoded header.
5837 static void
5838 excel_read_HLINK (BiffQuery *q, ExcelReadSheet *esheet)
5840 static guint8 const stdlink_guid[] = {
5841 0xd0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
5842 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
5843 /* unknown */
5844 0x02, 0x00, 0x00, 0x00
5846 static guint8 const url_guid[] = {
5847 0xe0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
5848 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
5850 static guint8 const file_guid[] = {
5851 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5852 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
5854 GnmRange r;
5855 guint32 options, len;
5856 guint16 next_opcode;
5857 guint8 const *data = q->data;
5858 guchar *label = NULL;
5859 guchar *target_base = NULL;
5860 guchar *mark = NULL;
5861 guchar *tip = NULL;
5862 GnmHLink *link = NULL;
5864 XL_CHECK_CONDITION (q->length > 32);
5866 xls_read_range16 (&r, data);
5867 options = GSF_LE_GET_GUINT32 (data + 28);
5869 XL_CHECK_CONDITION (!memcmp (data + 8, stdlink_guid, sizeof (stdlink_guid)));
5871 data += 32;
5873 d (1, {
5874 range_dump (&r, "");
5875 g_printerr (" = hlink options(0x%04x)\n", options);
5878 if ((options & 0x14) == 0x14) { /* label */
5879 XL_NEED_ITEMS (1, 4);
5880 len = GSF_LE_GET_GUINT32 (data);
5881 data += 4;
5882 XL_NEED_ITEMS (len, 2);
5883 label = read_utf16_str (len, data);
5884 data += len*2;
5885 d (1, g_printerr ("label = %s\n", label););
5888 if (options & 0x80) { /* target_base */
5889 XL_NEED_ITEMS (1, 4);
5890 len = GSF_LE_GET_GUINT32 (data);
5891 data += 4;
5892 XL_NEED_ITEMS (len, 2);
5893 target_base = read_utf16_str (len, data);
5894 data += len*2;
5895 d (1, g_printerr ("target_base = %s\n", target_base););
5898 if (options & 0x8) { /* 'text mark' */
5899 XL_NEED_ITEMS (1, 4);
5900 len = GSF_LE_GET_GUINT32 (data);
5901 data += 4;
5903 XL_NEED_ITEMS (len, 2);
5904 mark = read_utf16_str (len, data);
5905 data += len*2;
5906 d (1, g_printerr ("mark = %s\n", mark););
5909 if ((options & 0x163) == 0x003 && !memcmp (data, url_guid, sizeof (url_guid))) {
5910 guchar *url;
5912 data += sizeof (url_guid);
5913 XL_NEED_ITEMS (1, 4);
5914 len = GSF_LE_GET_GUINT32 (data);
5915 data += 4;
5917 XL_NEED_BYTES (len);
5918 url = read_utf16_str (len/2, data);
5919 if (NULL != url && 0 == g_ascii_strncasecmp (url, "mailto:", 7))
5920 link = gnm_hlink_new (gnm_hlink_email_get_type (), esheet->sheet);
5921 else
5922 link = gnm_hlink_new (gnm_hlink_url_get_type (), esheet->sheet);
5923 gnm_hlink_set_target (link, url);
5924 g_free (url);
5926 /* File link */
5927 } else if ((options & 0x1e1) == 0x001 && !memcmp (data, file_guid, sizeof (file_guid))) {
5928 guchar *path;
5929 GString *accum;
5930 int up;
5932 data += sizeof (file_guid);
5934 XL_NEED_BYTES (6);
5935 up = GSF_LE_GET_GUINT16 (data + 0);
5936 len = GSF_LE_GET_GUINT32 (data + 2);
5937 d (1, g_printerr ("# leading ../ %d len 0x%04x\n",
5938 up, len););
5939 data += 6;
5941 XL_NEED_BYTES (len);
5942 data += len;
5944 XL_NEED_BYTES (16 + 12 + 6);
5945 data += 16 + 12;
5946 len = GSF_LE_GET_GUINT32 (data);
5947 data += 6;
5949 XL_NEED_BYTES (len);
5950 path = read_utf16_str (len/2, data);
5951 accum = g_string_new (NULL);
5952 while (up-- > 0)
5953 g_string_append (accum, "..\\");
5954 g_string_append (accum, path);
5955 g_free (path);
5956 link = gnm_hlink_new (gnm_hlink_external_get_type (), esheet->sheet);
5957 gnm_hlink_set_target (link, accum->str);
5958 g_string_free (accum, TRUE);
5960 /* UNC File link */
5961 } else if ((options & 0x1e3) == 0x103) {
5962 guchar *path;
5964 XL_NEED_ITEMS (1, 4);
5965 len = GSF_LE_GET_GUINT32 (data);
5966 data += 4;
5968 XL_NEED_BYTES (len);
5969 path = read_utf16_str (len/2, data);
5970 link = gnm_hlink_new (gnm_hlink_external_get_type (), esheet->sheet);
5971 gnm_hlink_set_target (link, path);
5972 g_free (path);
5974 } else if ((options & 0x1eb) == 0x008) {
5975 link = gnm_hlink_new (gnm_hlink_cur_wb_get_type (), esheet->sheet);
5976 gnm_hlink_set_target (link, mark);
5977 } else {
5978 g_warning ("Unknown hlink type 0x%x", options);
5981 if (ms_biff_query_peek_next (q, &next_opcode) &&
5982 next_opcode == BIFF_LINK_TIP) {
5983 ms_biff_query_next (q);
5984 /* according to OO the bytes 2..10 are the range for the tip */
5985 XL_CHECK_CONDITION (q->length > 10);
5986 tip = read_utf16_str ((q->length - 10)/ 2, q->data + 10);
5989 if (link != NULL) {
5990 GnmStyle *style = gnm_style_new ();
5991 gnm_hlink_set_tip (link, tip);
5992 gnm_style_set_hlink (style, link);
5993 sheet_style_apply_range (esheet->sheet, &r, style);
5996 g_free (tip);
5997 g_free (mark);
5998 g_free (target_base);
5999 g_free (label);
6002 static void
6003 excel_read_CODENAME (BiffQuery *q, GnmXLImporter *importer, ExcelReadSheet *esheet)
6005 char *codename;
6006 GObject *obj;
6008 XL_CHECK_CONDITION (q->length >= 2);
6010 codename = excel_biff_text_2 (importer, q, 0);
6011 obj = esheet ? G_OBJECT (esheet->sheet) : G_OBJECT (importer->wb);
6012 g_object_set_data_full (obj, CODENAME_KEY, codename, g_free);
6015 static void
6016 excel_read_BG_PIC (BiffQuery *q,
6017 ExcelReadSheet *esheet)
6019 /* undocumented, looks similar to IMDATA */
6020 GdkPixbuf *background = excel_read_IMDATA (q, TRUE);
6021 if (background != NULL)
6022 g_object_unref (background);
6025 static GnmValue *
6026 read_DOPER (guint8 const *doper, gboolean is_equal,
6027 unsigned *str_len, GnmFilterOp *op)
6029 static GnmFilterOp const ops [] = {
6030 GNM_FILTER_OP_LT,
6031 GNM_FILTER_OP_EQUAL,
6032 GNM_FILTER_OP_LTE,
6033 GNM_FILTER_OP_GT,
6034 GNM_FILTER_OP_NOT_EQUAL,
6035 GNM_FILTER_OP_GTE
6037 GnmValue *res = NULL;
6039 *str_len = 0;
6040 *op = GNM_FILTER_UNUSED;
6041 switch (doper[0]) {
6042 case 0: return NULL; /* ignore */
6044 case 2: res = biff_get_rk (doper + 2);
6045 break;
6046 case 4: res = value_new_float (GSF_LE_GET_DOUBLE (doper+2));
6047 break;
6048 case 6: *str_len = doper[6];
6049 break;
6051 case 8: if (doper[2])
6052 res = xls_value_new_err (NULL, doper[3]);
6053 else
6054 res = value_new_bool (doper[3] ? TRUE : FALSE);
6055 break;
6057 case 0xC: *op = GNM_FILTER_OP_BLANKS;
6058 return NULL;
6059 case 0xE: *op = GNM_FILTER_OP_NON_BLANKS;
6060 return NULL;
6063 g_return_val_if_fail (doper[1] > 0 && doper[1] <=6, NULL);
6064 *op = ops [doper[1] - 1];
6066 return res;
6069 static void
6070 excel_read_AUTOFILTER (BiffQuery *q, ExcelReadSheet *esheet)
6072 guint16 flags;
6073 GnmFilterCondition *cond = NULL;
6074 GnmFilter *filter;
6076 /* XL only supports 1 filter per sheet */
6077 g_return_if_fail (esheet->sheet->filters != NULL);
6078 g_return_if_fail (esheet->sheet->filters->data != NULL);
6079 g_return_if_fail (esheet->sheet->filters->next == NULL);
6081 XL_CHECK_CONDITION (q->length >= 4);
6082 flags = GSF_LE_GET_GUINT16 (q->data + 2);
6084 filter = esheet->sheet->filters->data;
6086 if (esheet_ver (esheet) >= MS_BIFF_V8 && flags & 0x10)
6087 /* it's a top/bottom n */
6088 cond = gnm_filter_condition_new_bucket (
6089 (flags & 0x20) ? TRUE : FALSE,
6090 (flags & 0x40) ? FALSE : TRUE,
6091 FALSE,
6092 (flags >> 7) & 0x1ff);
6094 if (cond == NULL) {
6095 unsigned len0, len1;
6096 GnmFilterOp op0, op1;
6097 guint8 const *data;
6098 guint8 const *end = q->data + q->length;
6099 GnmValue *v0, *v1;
6101 XL_CHECK_CONDITION (q->length >= 24);
6102 v0 = read_DOPER (q->data + 4, flags & 4, &len0, &op0);
6103 v1 = read_DOPER (q->data + 14, flags & 8, &len1, &op1);
6105 data = q->data + 24;
6106 if (len0 > 0) {
6107 v0 = value_new_string_nocopy (
6108 excel_get_text (esheet->container.importer, data, len0, NULL, NULL, end - data));
6109 data += len0;
6111 if (len1 > 0)
6112 v1 = value_new_string_nocopy (
6113 excel_get_text (esheet->container.importer, data, len1, NULL, NULL, end - data));
6115 /* Survive fuzzed files. */
6116 if (op0 == GNM_FILTER_UNUSED)
6117 op0 = GNM_FILTER_OP_BLANKS;
6119 if (op1 == GNM_FILTER_UNUSED) {
6120 cond = gnm_filter_condition_new_single (op0, v0);
6121 value_release (v1); /* paranoia */
6122 } else {
6123 /* NOTE : Docs are backwards */
6124 cond = gnm_filter_condition_new_double
6125 (op0, v0, (flags & 3) ? FALSE : TRUE, op1, v1);
6129 gnm_filter_set_condition (filter,
6130 GSF_LE_GET_GUINT16 (q->data), cond, FALSE);
6133 void
6134 excel_read_SCL (BiffQuery *q, Sheet *sheet)
6136 unsigned num, denom;
6138 XL_CHECK_CONDITION (q->length == 4);
6140 num = GSF_LE_GET_GUINT16 (q->data);
6141 denom = GSF_LE_GET_GUINT16 (q->data + 2);
6143 XL_CHECK_CONDITION (denom != 0);
6145 g_object_set (sheet, "zoom-factor", num / (double)denom, NULL);
6149 * excel_externsheet_v8 :
6151 * WARNING WARNING WARNING
6153 * This function can and will return intentionally INVALID pointers in some
6154 * cases. You need to check for XL_EXTERNSHEET_MAGIC_SELFREF
6155 * It should only happen for external names to deal with 'reference self'
6156 * supbook entries. However, you never know.
6158 * you also need to check for XL_EXTERNSHEET_MAGIC_DELETED which indicates deleted
6160 * WARNING WARNING WARNING
6162 ExcelExternSheetV8 const *
6163 excel_externsheet_v8 (GnmXLImporter const *importer, guint16 i)
6165 d (2, g_printerr ("externv8 %hd\n", i););
6167 g_return_val_if_fail (importer->v8.externsheet != NULL, NULL);
6169 if (i >= importer->v8.externsheet->len) {
6170 g_warning ("%hd >= %u\n", i, importer->v8.externsheet->len);
6171 return NULL;
6174 return &(g_array_index (importer->v8.externsheet, ExcelExternSheetV8, i));
6177 static Sheet *
6178 supbook_get_sheet (GnmXLImporter *importer, gint16 sup_index, unsigned i)
6180 if (sup_index < 0) {
6181 g_warning ("external references not supported yet.");
6182 return NULL;
6185 /* 0xffff == deleted */
6186 if (i >= 0xffff)
6187 return XL_EXTERNSHEET_MAGIC_DELETED; /* magic value */
6189 /* WARNING : 0xfffe record for local names kludge a solution */
6190 if (i == 0xfffe)
6191 return XL_EXTERNSHEET_MAGIC_SELFREF; /* magic value */
6193 g_return_val_if_fail ((unsigned)sup_index < importer->v8.supbook->len, NULL);
6195 switch (g_array_index (importer->v8.supbook, ExcelSupBook, sup_index).type) {
6196 case EXCEL_SUP_BOOK_SELFREF: {
6197 Sheet *sheet;
6198 g_return_val_if_fail (i < importer->boundsheet_sheet_by_index->len, NULL);
6199 sheet = g_ptr_array_index (importer->boundsheet_sheet_by_index, i);
6200 g_return_val_if_fail (IS_SHEET (sheet), NULL);
6201 return sheet;
6203 case EXCEL_SUP_BOOK_STD:
6204 g_warning ("external references not supported yet.");
6205 break;
6206 case EXCEL_SUP_BOOK_PLUGIN:
6207 g_warning ("strange external reference.");
6208 break;
6211 return XL_EXTERNSHEET_MAGIC_DELETED; /* pretend "deleted" */
6214 static void
6215 excel_read_EXTERNSHEET_v8 (BiffQuery const *q, GnmXLImporter *importer)
6217 ExcelExternSheetV8 *v8;
6218 gint16 sup_index;
6219 unsigned i, num, first, last;
6221 XL_CHECK_CONDITION (importer->ver >= MS_BIFF_V8);
6222 g_return_if_fail (importer->v8.externsheet == NULL);
6224 XL_CHECK_CONDITION (q->length >= 2);
6225 num = GSF_LE_GET_GUINT16 (q->data);
6226 XL_CHECK_CONDITION (q->length >= 2 + num * 6);
6228 d (2, g_printerr ("ExternSheet (%d entries)\n", num););
6229 d (10, gsf_mem_dump (q->data, q->length););
6231 importer->v8.externsheet = g_array_set_size (
6232 g_array_new (FALSE, FALSE, sizeof (ExcelExternSheetV8)), num);
6234 for (i = 0; i < num; i++) {
6235 sup_index = GSF_LE_GET_GINT16 (q->data + 2 + i * 6 + 0);
6236 first = GSF_LE_GET_GUINT16 (q->data + 2 + i * 6 + 2);
6237 last = GSF_LE_GET_GUINT16 (q->data + 2 + i * 6 + 4);
6239 d (2, g_printerr ("ExternSheet: sup = %hd First sheet 0x%x, Last sheet 0x%x\n",
6240 sup_index, first, last););
6242 v8 = &g_array_index(importer->v8.externsheet, ExcelExternSheetV8, i);
6243 v8->supbook = sup_index;
6244 v8->first = supbook_get_sheet (importer, sup_index, first);
6245 v8->last = supbook_get_sheet (importer, sup_index, last);
6246 d (2, g_printerr ("\tFirst sheet %p, Last sheet %p\n",
6247 v8->first, v8->last););
6252 * excel_externsheet_v7 :
6255 Sheet *
6256 excel_externsheet_v7 (MSContainer const *container, gint16 idx)
6258 GPtrArray const *externsheets;
6260 d (2, g_printerr ("externv7 %hd\n", idx););
6262 externsheets = container->v7.externsheets;
6263 g_return_val_if_fail (externsheets != NULL, NULL);
6264 g_return_val_if_fail (idx > 0, NULL);
6265 g_return_val_if_fail (idx <= (int)externsheets->len, NULL);
6267 return g_ptr_array_index (externsheets, idx-1);
6270 void
6271 excel_read_EXTERNSHEET_v7 (BiffQuery const *q, MSContainer *container)
6273 Sheet *sheet = NULL;
6274 guint8 type;
6276 XL_CHECK_CONDITION (q->length >= 2);
6278 type = GSF_LE_GET_GUINT8 (q->data + 1);
6280 d (1, {
6281 g_printerr ("extern v7 %p\n", container);
6282 gsf_mem_dump (q->data, q->length); });
6284 switch (type) {
6285 case 2: sheet = ms_container_sheet (container);
6286 if (sheet == NULL)
6287 g_warning ("What does this mean ?");
6288 break;
6290 /* Type 3 is undocumented magic. It is used to forward declare sheet
6291 * names in the current workbook */
6292 case 3: {
6293 unsigned len = GSF_LE_GET_GUINT8 (q->data);
6294 char *name;
6296 /* opencalc screws up its export, overstating
6297 * the length by 1 */
6298 if (len + 2 > q->length)
6299 len = q->length - 2;
6301 name = excel_biff_text (container->importer, q, 2, len);
6303 if (name != NULL) {
6304 sheet = workbook_sheet_by_name (container->importer->wb, name);
6305 if (sheet == NULL) {
6306 /* There was a bug in 1.0.x export that spewed the quoted name
6307 * includling internal backquoting */
6308 if (name[0] == '\'') {
6309 GString *fixed = g_string_new (NULL);
6310 if (NULL != go_strunescape (fixed, name) &&
6311 NULL != (sheet = workbook_sheet_by_name (container->importer->wb, fixed->str))) {
6312 g_free (name);
6313 name = g_string_free (fixed, FALSE);
6314 } else
6315 g_string_free (fixed, TRUE);
6318 if (sheet == NULL) {
6319 sheet = sheet_new (container->importer->wb,
6320 name,
6321 XLS_MaxCol,
6322 XLS_MaxRow_V7);
6323 workbook_sheet_attach (container->importer->wb, sheet);
6326 g_free (name);
6328 break;
6330 case 4: /* undocumented. Seems to be used as a placeholder for names */
6331 sheet = XL_EXTERNSHEET_MAGIC_SELFREF;
6332 break;
6334 case 0x3a : /* undocumented magic. seems to indicate the sheet for an
6335 * addin with functions. 01 3a
6336 * the same as SUPBOOK
6338 if (*q->data == 1 && q->length == 2)
6339 break;
6341 default:
6342 /* Fix when we get placeholders to external workbooks */
6343 d (1, gsf_mem_dump (q->data, q->length););
6344 go_io_warning_unsupported_feature (container->importer->context,
6345 _("external references"));
6348 if (container->v7.externsheets == NULL)
6349 container->v7.externsheets = g_ptr_array_new ();
6350 g_ptr_array_add (container->v7.externsheets, sheet);
6353 static void
6354 excel_read_EXTERNSHEET (BiffQuery const *q, GnmXLImporter *importer,
6355 const MsBiffBofData *ver)
6357 XL_CHECK_CONDITION (ver != NULL);
6359 if (ver->version >= MS_BIFF_V8)
6360 excel_read_EXTERNSHEET_v8 (q, importer);
6361 else
6362 excel_read_EXTERNSHEET_v7 (q, &importer->container);
6366 /* FILEPASS, ask the user for a password if necessary
6367 * return value is an error string, or NULL for success
6369 static const char *
6370 excel_read_FILEPASS (BiffQuery *q, GnmXLImporter *importer)
6372 /* files with workbook protection are encrypted using a
6373 * static password (why ?? ). */
6374 if (ms_biff_query_set_decrypt(q, importer->ver, "VelvetSweatshop"))
6375 return NULL;
6377 while (TRUE) {
6378 guint8 *passwd = go_cmd_context_get_password (
6379 GO_CMD_CONTEXT (importer->context),
6380 go_doc_get_uri (GO_DOC (importer->wb)));
6381 gboolean ok;
6383 if (passwd == NULL)
6384 return _("No password supplied");
6386 ok = ms_biff_query_set_decrypt (q, importer->ver, passwd);
6387 go_destroy_password (passwd);
6388 g_free (passwd);
6389 if (ok)
6390 return NULL;
6394 static void
6395 excel_read_LABEL (BiffQuery *q, ExcelReadSheet *esheet, gboolean has_markup)
6397 GnmValue *v;
6398 guint in_len, str_len;
6399 gchar *txt;
6400 BiffXFData const *xf;
6401 ExcelFont const *fd;
6402 GnmCell *cell = excel_cell_fetch (q, esheet);
6404 if (!cell)
6405 return;
6407 XL_CHECK_CONDITION (q->length >= 8);
6408 in_len = (q->opcode == BIFF_LABEL_v0)
6409 ? GSF_LE_GET_GUINT8 (q->data + 7)
6410 : GSF_LE_GET_GUINT16 (q->data + 6);
6411 XL_CHECK_CONDITION (q->length - 8 >= in_len);
6413 xf = excel_set_xf (esheet, q);
6414 if (!xf)
6415 return;
6416 fd = excel_font_get (esheet->container.importer, xf->font_idx);
6418 txt = excel_get_text (esheet->container.importer, q->data + 8,
6419 in_len, &str_len, fd ? &fd->codepage : NULL,
6420 q->length - 8);
6422 d (0, g_printerr ("%s in %s;\n",
6423 has_markup ? "formatted string" : "string",
6424 cell_name (cell)););
6426 if (txt != NULL) {
6427 GOFormat *fmt = NULL;
6428 if (has_markup)
6429 fmt = excel_read_LABEL_markup (q, esheet, txt, str_len);
6431 /* might free txt, do not do this until after parsing markup */
6432 v = value_new_string_nocopy (txt);
6433 if (fmt != NULL) {
6434 value_set_fmt (v, fmt);
6435 go_format_unref (fmt);
6437 gnm_cell_set_value (cell, v);
6441 static void
6442 excel_read_BOOLERR (BiffQuery *q, ExcelReadSheet *esheet)
6444 unsigned base = (q->opcode == BIFF_BOOLERR_v0) ? 7 : 6;
6445 GnmValue *v;
6447 XL_CHECK_CONDITION (q->length >= base + 2);
6449 if (GSF_LE_GET_GUINT8 (q->data + base + 1)) {
6450 GnmEvalPos ep;
6451 eval_pos_init (&ep, esheet->sheet, XL_GETCOL (q), XL_GETROW (q));
6452 v = xls_value_new_err (&ep, GSF_LE_GET_GUINT8 (q->data + base));
6453 } else
6454 v = value_new_bool (GSF_LE_GET_GUINT8 (q->data + base));
6455 excel_sheet_insert_val (esheet, q, v);
6458 static void
6459 excel_read_HEADER_FOOTER (GnmXLImporter const *importer,
6460 BiffQuery *q, ExcelReadSheet *esheet,
6461 gboolean is_header)
6463 GnmPrintInformation *pi = esheet->sheet->print_info;
6465 if (q->length) {
6466 char *str;
6468 if (importer->ver >= MS_BIFF_V8)
6469 str = excel_biff_text_2 (importer, q, 0);
6470 else
6471 str = excel_biff_text_1 (importer, q, 0);
6473 d (2, g_printerr ("%s == '%s'\n", is_header ? "header" : "footer", str););
6475 xls_header_footer_import (is_header ? &pi->header : &pi->footer,
6476 str);
6478 g_free (str);
6482 static void
6483 excel_read_REFMODE (BiffQuery *q, ExcelReadSheet *esheet)
6485 guint16 mode;
6487 XL_CHECK_CONDITION (q->length >= 2);
6488 mode = GSF_LE_GET_GUINT16 (q->data);
6489 g_object_set (esheet->sheet, "use-r1c1", mode == 0, NULL);
6492 static void
6493 excel_read_PAGE_BREAK (BiffQuery *q, ExcelReadSheet *esheet, gboolean is_vert)
6495 unsigned i;
6496 unsigned step = (esheet_ver (esheet) >= MS_BIFF_V8) ? 6 : 2;
6497 guint16 count;
6498 GnmPageBreaks *breaks;
6500 XL_CHECK_CONDITION (q->length >= 2);
6501 count = GSF_LE_GET_GUINT16 (q->data);
6502 XL_CHECK_CONDITION (q->length >= 2 + count * step);
6503 breaks = gnm_page_breaks_new (is_vert);
6505 /* 1) Ignore the first/last info for >= biff8
6506 * 2) Assume breaks are manual in the absence of any information */
6507 for (i = 0; i < count ; i++) {
6508 gnm_page_breaks_append_break (breaks,
6509 GSF_LE_GET_GUINT16 (q->data + 2 + i*step), GNM_PAGE_BREAK_MANUAL);
6510 #if 0
6511 g_printerr ("%d %d:%d\n",
6512 GSF_LE_GET_GUINT16 (q->data + 2 + i*step),
6513 GSF_LE_GET_GUINT16 (q->data + 2 + i*step + 2),
6514 GSF_LE_GET_GUINT16 (q->data + 2 + i*step + 4));
6515 #endif
6517 print_info_set_breaks (esheet->sheet->print_info, breaks);
6520 static void
6521 excel_read_INTEGER (BiffQuery *q, ExcelReadSheet *esheet)
6523 XL_CHECK_CONDITION (q->length >= 7 + 2);
6524 excel_sheet_insert_val
6525 (esheet, q,
6526 value_new_int (GSF_LE_GET_GUINT16 (q->data + 7)));
6529 static void
6530 excel_read_NUMBER (BiffQuery *q, ExcelReadSheet *esheet, size_t ofs)
6532 XL_CHECK_CONDITION (q->length >= ofs + 8);
6533 excel_sheet_insert_val
6534 (esheet, q,
6535 value_new_float (gsf_le_get_double (q->data + ofs)));
6538 static void
6539 excel_read_MARGIN (BiffQuery *q, ExcelReadSheet *esheet)
6541 double m;
6542 GnmPrintInformation *pi = esheet->sheet->print_info;
6544 XL_CHECK_CONDITION (q->length >= 8);
6545 m = GO_IN_TO_PT (gsf_le_get_double (q->data));
6547 switch (q->opcode) {
6548 case BIFF_LEFT_MARGIN: print_info_set_margin_left (pi, m); break;
6549 case BIFF_RIGHT_MARGIN: print_info_set_margin_right (pi, m); break;
6550 case BIFF_TOP_MARGIN: print_info_set_edge_to_below_header (pi, m); break;
6551 case BIFF_BOTTOM_MARGIN: print_info_set_edge_to_above_footer (pi, m); break;
6552 default: g_assert_not_reached ();
6557 static void
6558 excel_read_PRINTHEADERS (BiffQuery *q, ExcelReadSheet *esheet)
6560 XL_CHECK_CONDITION (q->length >= 2);
6561 esheet->sheet->print_info->print_titles =
6562 (GSF_LE_GET_GUINT16 (q->data) == 1);
6565 static void
6566 excel_read_PRINTGRIDLINES (BiffQuery *q, ExcelReadSheet *esheet)
6568 XL_CHECK_CONDITION (q->length >= 2);
6569 esheet->sheet->print_info->print_grid_lines =
6570 (GSF_LE_GET_GUINT16 (q->data) == 1);
6573 static void
6574 excel_read_HCENTER (BiffQuery *q, ExcelReadSheet *esheet)
6576 XL_CHECK_CONDITION (q->length >= 2);
6577 esheet->sheet->print_info->center_horizontally =
6578 GSF_LE_GET_GUINT16 (q->data) == 0x1;
6581 static void
6582 excel_read_VCENTER (BiffQuery *q, ExcelReadSheet *esheet)
6584 XL_CHECK_CONDITION (q->length >= 2);
6585 esheet->sheet->print_info->center_vertically =
6586 GSF_LE_GET_GUINT16 (q->data) == 0x1;
6589 static void
6590 excel_read_PLS (BiffQuery *q, ExcelReadSheet *esheet)
6592 XL_CHECK_CONDITION (q->length >= 2);
6593 if (GSF_LE_GET_GUINT16 (q->data) == 0x00) {
6595 * q->data + 2 -> q->data + q->length
6596 * map to a DEVMODE structure see MS' SDK.
6598 } else if (GSF_LE_GET_GUINT16 (q->data) == 0x01) {
6600 * q's data maps to a TPrint structure
6601 * see Inside Macintosh Vol II p 149.
6606 static void
6607 excel_read_RK (BiffQuery *q, ExcelReadSheet *esheet)
6609 XL_CHECK_CONDITION (q->length >= 6 + 4);
6610 excel_sheet_insert_val (esheet, q,
6611 biff_get_rk (q->data + 6));
6614 static void
6615 excel_read_LABELSST (BiffQuery *q, ExcelReadSheet *esheet)
6617 unsigned i;
6619 XL_CHECK_CONDITION (q->length >= 6 + 4);
6620 i = GSF_LE_GET_GUINT32 (q->data + 6);
6622 if (esheet->container.importer->sst && i < esheet->container.importer->sst_len) {
6623 GnmValue *v;
6624 GOString *str = esheet->container.importer->sst[i].content;
6625 /* ? Why would there be a NULL ? */
6626 if (NULL != str) {
6627 go_string_ref (str);
6628 v = value_new_string_str (str);
6629 d (2, g_printerr ("str=%s\n", str->str););
6630 } else
6631 v = value_new_string ("");
6632 if (esheet->container.importer->sst[i].markup != NULL)
6633 value_set_fmt (v, esheet->container.importer->sst[i].markup);
6634 excel_sheet_insert_val (esheet, q, v);
6635 } else
6636 g_warning ("string index 0x%u >= 0x%x\n",
6637 i, esheet->container.importer->sst_len);
6640 static gboolean
6641 excel_read_sheet (BiffQuery *q, GnmXLImporter *importer,
6642 WorkbookView *wb_view, ExcelReadSheet *esheet)
6644 MsBiffVersion const ver = importer->ver;
6646 g_return_val_if_fail (importer != NULL, FALSE);
6647 g_return_val_if_fail (esheet != NULL, FALSE);
6648 g_return_val_if_fail (esheet->sheet != NULL, FALSE);
6649 g_return_val_if_fail (esheet->sheet->print_info != NULL, FALSE);
6651 d (1, g_printerr ("----------------- '%s' -------------\n",
6652 esheet->sheet->name_unquoted););
6654 if (ver <= MS_BIFF_V4) {
6655 /* Style is per-sheet in early Excel - default TODO */
6656 excel_workbook_reset_style (importer);
6657 } else {
6658 /* Apply the default style */
6659 GnmStyle *mstyle = excel_get_style_from_xf (esheet,
6660 excel_get_xf (esheet, 15));
6661 if (mstyle != NULL) {
6662 GnmRange r;
6663 range_init_full_sheet (&r, esheet->sheet);
6664 sheet_style_set_range (esheet->sheet, &r, mstyle);
6668 for (; ms_biff_query_next (q) ;
6669 go_io_value_progress_update (importer->context, q->streamPos)) {
6671 d (5, {
6672 const char *opname = biff_opcode_name (q->opcode);
6673 g_printerr ("Opcode: 0x%x %s\n",
6674 q->opcode,
6675 opname ? opname : "unknown");
6678 switch (q->opcode) {
6679 case BIFF_DIMENSIONS_v0: break; /* ignore ancient XL2 variant */
6680 case BIFF_DIMENSIONS_v2: excel_read_DIMENSIONS (q, esheet); break;
6682 case BIFF_BLANK_v0:
6683 case BIFF_BLANK_v2:
6684 (void)excel_set_xf (esheet, q);
6685 break;
6687 case BIFF_INTEGER:
6688 excel_read_INTEGER (q, esheet);
6689 break;
6690 case BIFF_NUMBER_v0:
6691 excel_read_NUMBER (q, esheet, 7);
6692 break;
6693 case BIFF_NUMBER_v2:
6694 excel_read_NUMBER (q, esheet, 6);
6695 break;
6697 case BIFF_LABEL_v0:
6698 case BIFF_LABEL_v2:
6699 excel_read_LABEL (q, esheet, FALSE);
6700 break;
6702 case BIFF_BOOLERR_v0:
6703 case BIFF_BOOLERR_v2:
6704 excel_read_BOOLERR (q, esheet);
6705 break;
6707 case BIFF_FORMULA_v0:
6708 case BIFF_FORMULA_v2:
6709 case BIFF_FORMULA_v4: excel_read_FORMULA (q, esheet); break;
6710 /* case STRING : is handled elsewhere since it always follows FORMULA */
6711 case BIFF_ROW_v0:
6712 case BIFF_ROW_v2: excel_read_ROW (q, esheet); break;
6713 case BIFF_EOF: goto success;
6715 /* NOTE : bytes 12 & 16 appear to require the non decrypted data */
6716 case BIFF_INDEX_v0:
6717 case BIFF_INDEX_v2: break;
6719 case BIFF_CALCCOUNT: excel_read_CALCCOUNT (q, importer); break;
6720 case BIFF_CALCMODE: excel_read_CALCMODE (q,importer); break;
6722 case BIFF_PRECISION :
6723 #if 0
6725 /* FIXME: implement in gnumeric */
6726 /* state of 'Precision as Displayed' option */
6727 guint16 const data = GSF_LE_GET_GUINT16 (q->data);
6728 gboolean const prec_as_displayed = (data == 0);
6730 #endif
6731 break;
6733 case BIFF_REFMODE: excel_read_REFMODE (q, esheet); break;
6734 case BIFF_DELTA: excel_read_DELTA (q, importer); break;
6735 case BIFF_ITERATION: excel_read_ITERATION (q, importer); break;
6736 case BIFF_OBJPROTECT:
6737 case BIFF_PROTECT: excel_read_sheet_PROTECT (q, esheet); break;
6739 case BIFF_PASSWORD:
6740 if (q->length == 2) {
6741 d (2, g_printerr ("sheet password '%hx'\n",
6742 GSF_LE_GET_GUINT16 (q->data)););
6744 break;
6748 case BIFF_HEADER: excel_read_HEADER_FOOTER (importer, q, esheet, TRUE); break;
6749 case BIFF_FOOTER: excel_read_HEADER_FOOTER (importer, q, esheet, FALSE); break;
6751 case BIFF_EXTERNCOUNT: /* ignore */ break;
6752 case BIFF_EXTERNSHEET: /* These cannot be biff8 */
6753 excel_read_EXTERNSHEET_v7 (q, &esheet->container);
6754 break;
6756 case BIFF_VERTICALPAGEBREAKS: excel_read_PAGE_BREAK (q, esheet, TRUE); break;
6757 case BIFF_HORIZONTALPAGEBREAKS: excel_read_PAGE_BREAK (q, esheet, FALSE); break;
6759 case BIFF_NOTE: excel_read_NOTE (q, esheet); break;
6760 case BIFF_SELECTION: excel_read_SELECTION (q, esheet); break;
6761 case BIFF_EXTERNNAME_v0:
6762 case BIFF_EXTERNNAME_v2:
6763 excel_read_EXTERNNAME (q, &esheet->container);
6764 break;
6765 case BIFF_DEFAULTROWHEIGHT_v0:
6766 case BIFF_DEFAULTROWHEIGHT_v2:
6767 excel_read_DEF_ROW_HEIGHT (q, esheet);
6768 break;
6770 case BIFF_LEFT_MARGIN:
6771 case BIFF_RIGHT_MARGIN:
6772 case BIFF_TOP_MARGIN:
6773 case BIFF_BOTTOM_MARGIN:
6774 excel_read_MARGIN (q, esheet);
6775 break;
6777 case BIFF_PRINTHEADERS:
6778 excel_read_PRINTHEADERS (q, esheet);
6779 break;
6781 case BIFF_PRINTGRIDLINES:
6782 excel_read_PRINTGRIDLINES (q, esheet);
6783 break;
6785 case BIFF_WINDOW1: break; /* what does this do for a sheet ? */
6786 case BIFF_WINDOW2_v0:
6787 case BIFF_WINDOW2_v2:
6788 excel_read_WINDOW2 (q, esheet, wb_view);
6789 break;
6790 case BIFF_BACKUP: break;
6791 case BIFF_PANE: excel_read_PANE (q, esheet, wb_view); break;
6793 case BIFF_PLS: excel_read_PLS (q, esheet); break;
6795 case BIFF_DEFCOLWIDTH: excel_read_DEF_COL_WIDTH (q, esheet); break;
6797 case BIFF_OBJ: ms_read_OBJ (q, sheet_container (esheet), NULL); break;
6799 case BIFF_SAVERECALC: break;
6800 case BIFF_TAB_COLOR: excel_read_TAB_COLOR (q, esheet); break;
6801 case BIFF_COLINFO: excel_read_COLINFO (q, esheet); break;
6803 case BIFF_RK: excel_read_RK (q, esheet); break;
6805 case BIFF_IMDATA: {
6806 GdkPixbuf *pixbuf = excel_read_IMDATA (q, FALSE);
6807 if (pixbuf)
6808 g_object_unref (pixbuf);
6809 break;
6811 case BIFF_GUTS: excel_read_GUTS (q, esheet); break;
6812 case BIFF_WSBOOL: excel_read_WSBOOL (q, esheet); break;
6813 case BIFF_GRIDSET: break;
6815 case BIFF_HCENTER: excel_read_HCENTER (q, esheet); break;
6816 case BIFF_VCENTER: excel_read_VCENTER (q, esheet); break;
6818 case BIFF_COUNTRY: break;
6819 case BIFF_STANDARDWIDTH: break; /* the 'standard width dialog' ? */
6820 case BIFF_FILTERMODE: break;
6821 case BIFF_AUTOFILTERINFO: break;
6822 case BIFF_AUTOFILTER: excel_read_AUTOFILTER (q, esheet); break;
6823 case BIFF_SCL: excel_read_SCL (q, esheet->sheet); break;
6824 case BIFF_SETUP: excel_read_SETUP (q, esheet); break;
6825 case BIFF_GCW: break;
6826 case BIFF_SCENMAN: break;
6827 case BIFF_SCENARIO: break;
6828 case BIFF_MULRK: excel_read_MULRK (q, esheet); break;
6829 case BIFF_MULBLANK: excel_read_MULBLANK (q, esheet); break;
6831 case BIFF_RSTRING: excel_read_LABEL (q, esheet, TRUE); break;
6833 case BIFF_DBCELL: break; /* Can be ignored on read side */
6835 case BIFF_BG_PIC: excel_read_BG_PIC (q, esheet); break;
6836 case BIFF_MERGECELLS: excel_read_MERGECELLS (q, esheet); break;
6838 case BIFF_MS_O_DRAWING:
6839 case BIFF_MS_O_DRAWING_GROUP:
6840 case BIFF_MS_O_DRAWING_SELECTION:
6841 ms_escher_parse (q, sheet_container (esheet), FALSE);
6842 break;
6843 case BIFF_PHONETIC: break;
6845 case BIFF_LABELSST:
6846 excel_read_LABELSST (q, esheet);
6847 break;
6849 case BIFF_XF_OLD_v0:
6850 case BIFF_XF_OLD_v2:
6851 case BIFF_XF_OLD_v4:
6852 excel_read_XF_OLD (q, importer);
6853 break;
6854 case BIFF_XF_INDEX:
6855 excel_read_XF_INDEX (q, esheet);
6856 break;
6858 case BIFF_NAME_v0:
6859 case BIFF_NAME_v2: excel_read_NAME (q, importer, esheet); break;
6860 case BIFF_FONT_v0:
6861 case BIFF_FONT_v2: excel_read_FONT (q, importer); break;
6862 case BIFF_FORMAT_v0:
6863 case BIFF_FORMAT_v4: excel_read_FORMAT (q, importer); break;
6864 case BIFF_STYLE: break;
6865 case BIFF_1904: excel_read_1904 (q, importer); break;
6866 case BIFF_FILEPASS: {
6867 const char *problem = excel_read_FILEPASS (q, importer);
6868 if (problem != NULL) {
6869 go_cmd_context_error_import (GO_CMD_CONTEXT (importer->context), problem);
6870 return FALSE;
6872 break;
6875 case BIFF_CONDFMT:
6876 excel_read_CONDFMT (q, esheet, importer);
6877 break;
6878 case BIFF_CF:
6879 g_warning ("Found a CF record without a CONDFMT ??");
6880 break;
6881 case BIFF_DVAL: excel_read_DVAL (q, esheet); break;
6882 case BIFF_HLINK: excel_read_HLINK (q, esheet); break;
6883 case BIFF_CODENAME: excel_read_CODENAME (q, importer, esheet); break;
6884 break;
6885 case BIFF_DV:
6886 g_warning ("Found a DV record without a DVal ??");
6887 excel_read_DV (q, esheet);
6888 break;
6890 case BIFF_SXVIEWEX9 :
6891 /* Seems to contain pivot table autoformat indicies, plus ?? */
6892 /* samples/excel/dbfuns.xls has as sample of this record
6893 * and I added code in OOo */
6894 break;
6896 case BIFF_SHEETPROTECTION:
6897 excel_read_SHEETPROTECTION (q, esheet->sheet);
6898 break;
6900 /* HACK: it seems that in older versions of XL the
6901 * charts did not have a wrapper object. the first
6902 * record in the sequence of chart records was a
6903 * CHART_UNITS followed by CHART_CHART. We play off of
6904 * that. When we encounter a CHART_units record we
6905 * jump to the chart handler which then starts parsing
6906 * at the NEXT record.
6908 case BIFF_CHART_units : {
6909 SheetObject *obj = sheet_object_graph_new (NULL);
6910 ms_excel_chart_read (q, sheet_container (esheet),
6911 obj, NULL);
6912 sheet_object_set_sheet (obj, esheet->sheet);
6913 g_object_unref (obj);
6914 break;
6916 case BIFF_SXVIEW: xls_read_SXVIEW (q, esheet); break;
6917 case BIFF_SXVD : xls_read_SXVD (q, esheet); break;
6918 case BIFF_SXVDEX : break; /* pulled in with SXVD */
6919 case BIFF_SXVI : break; /* pulled in with SXVD */
6920 case BIFF_SXIVD: xls_read_SXIVD (q, esheet); break;
6922 default:
6923 excel_unexpected_biff (q, "Sheet", ms_excel_read_debug);
6927 g_printerr ("Error, hit end without EOF\n");
6929 return FALSE;
6931 success :
6932 /* We need a sheet to extract styles, so store the workbook default as
6933 * soon as we parse a sheet. It is a kludge, but not terribly costly */
6934 g_object_set_data_full (G_OBJECT (importer->wb),
6935 "xls-default-style",
6936 excel_get_style_from_xf (esheet, excel_get_xf (esheet, 0)),
6937 (GDestroyNotify) gnm_style_unref);
6938 return TRUE;
6942 * NOTE : MS Docs are incorrect
6944 * unsigned short num_tabs;
6945 * unsigned short num_characters_in_book;
6946 * unsigned char flag_for_unicode; 0 or 1
6947 * var encoded string stored as 1 or 2 byte characters
6949 static void
6950 excel_read_SUPBOOK (BiffQuery *q, GnmXLImporter *importer)
6952 unsigned numTabs, len;
6953 unsigned i, t;
6954 guint32 byte_length, ofs;
6955 ExcelSupBook *new_supbook;
6956 char *bookname;
6958 XL_CHECK_CONDITION (q->length >= 4);
6959 numTabs = GSF_LE_GET_GUINT16 (q->data);
6960 len = GSF_LE_GET_GUINT16 (q->data + 2);
6962 i = importer->v8.supbook->len;
6963 g_array_set_size (importer->v8.supbook, i + 1);
6964 new_supbook = &g_array_index (importer->v8.supbook, ExcelSupBook, i);
6966 d (2, g_printerr ("supbook %d has %d sheets\n", i, numTabs););
6968 new_supbook->externname = g_ptr_array_new ();
6969 new_supbook->wb = NULL;
6971 if (q->length == 4 && len == 0x0401) {
6972 d (2, g_printerr ("\t is self referential\n"););
6973 new_supbook->type = EXCEL_SUP_BOOK_SELFREF;
6974 return;
6976 if (q->length == 4 && len == 0x3A01) {
6977 d (2, g_printerr ("\t is a plugin\n"););
6978 new_supbook->type = EXCEL_SUP_BOOK_PLUGIN;
6979 return;
6982 new_supbook->type = EXCEL_SUP_BOOK_STD;
6983 XL_CHECK_CONDITION (q->length >= 5);
6985 bookname = excel_get_text (importer, q->data + 4, len,
6986 &byte_length, NULL, q->length - 4);
6987 d (2, g_printerr ("\trefers to %s\n", bookname););
6989 * Bookname can be
6990 * (1) a single space -- "unused"
6991 * (2) a single nul -- self referencing
6992 * (3) an OLE-link VirtualPath
6993 * (4) any other VirtualPath -- external workbook
6995 if (len == 1 && *bookname == 0) {
6996 new_supbook->type = EXCEL_SUP_BOOK_SELFREF;
6997 } else if (len == 1 && *bookname == ' ') {
6998 /* what? */
7000 g_free (bookname);
7002 ofs = 4 + byte_length;
7003 XL_CHECK_CONDITION (ofs <= q->length);
7005 for (t = 0; t < numTabs; t++) {
7006 char *name;
7007 guint32 length;
7009 XL_CHECK_CONDITION (ofs + 2 <= q->length);
7011 length = GSF_LE_GET_GUINT16 (q->data + ofs);
7012 ofs += 2;
7013 name = excel_get_text (importer, q->data + ofs, length,
7014 &byte_length, NULL, q->length - ofs);
7015 d (2, g_printerr ("\tSheet %d -> %s\n", t, name););
7016 g_free (name);
7018 ofs += byte_length;
7021 #warning "create a workbook and sheets when we have a facility for merging things"
7024 static void
7025 excel_read_WINDOW1 (BiffQuery *q, WorkbookView *wb_view)
7027 if (q->length >= 16) {
7028 #if 0
7029 /* In 1/20ths of a point */
7030 guint16 const xPos = GSF_LE_GET_GUINT16 (q->data + 0);
7031 guint16 const yPos = GSF_LE_GET_GUINT16 (q->data + 2);
7032 #endif
7033 guint16 const width = GSF_LE_GET_GUINT16 (q->data + 4);
7034 guint16 const height = GSF_LE_GET_GUINT16 (q->data + 6);
7035 guint16 const options = GSF_LE_GET_GUINT16 (q->data + 8);
7036 #if 0
7037 /* duplicated in the WINDOW2 record */
7038 guint16 const selTab = GSF_LE_GET_GUINT16 (q->data + 10);
7040 guint16 const firstTab= GSF_LE_GET_GUINT16 (q->data + 12);
7041 guint16 const tabsSel = GSF_LE_GET_GUINT16 (q->data + 14);
7043 /* (width of tab)/(width of horizontal scroll bar) / 1000 */
7044 guint16 const ratio = GSF_LE_GET_GUINT16 (q->data + 16);
7045 #endif
7048 * We are sizing the window including the toolbars,
7049 * menus, and notbook tabs. Excel does not.
7051 * NOTE: This is the size of the MDI sub-window, not the size of
7052 * the containing excel window.
7054 wb_view_preferred_size (wb_view,
7055 .5 + width * gnm_app_display_dpi_get (TRUE) / (72. * 20.),
7056 .5 + height * gnm_app_display_dpi_get (FALSE) / (72. * 20.));
7058 if (options & 0x0001)
7059 g_printerr ("Unsupported: Hidden workbook\n");
7060 if (options & 0x0002)
7061 g_printerr ("Unsupported: Iconic workbook\n");
7063 g_object_set (G_OBJECT (wb_view),
7064 "show-horizontal-scrollbar", !!(options & 0x0008),
7065 "show-vertical-scrollbar", !!(options & 0x0010),
7066 "show-notebook-tabs", !!(options & 0x0020),
7067 NULL);
7071 static void
7072 excel_read_BOF (BiffQuery *q,
7073 GnmXLImporter *importer,
7074 WorkbookView *wb_view,
7075 GOIOContext *context,
7076 MsBiffBofData **version, unsigned *current_sheet)
7078 /* The first BOF seems to be OK, the rest lie ? */
7079 MsBiffVersion vv = MS_BIFF_V_UNKNOWN;
7080 MsBiffBofData *ver = *version;
7081 char const *version_desc = NULL;
7083 if (ver) {
7084 vv = ver->version;
7085 ms_biff_bof_data_destroy (ver);
7087 *version = ver = ms_biff_bof_data_new (q);
7088 if (vv != MS_BIFF_V_UNKNOWN)
7089 ver->version = vv;
7091 if (ver->type == MS_BIFF_TYPE_Workbook) {
7092 gnm_xl_importer_set_version (importer, ver->version);
7093 if (ver->version >= MS_BIFF_V8) {
7094 guint32 ver;
7095 XL_CHECK_CONDITION (q->length >= 8);
7096 ver = GSF_LE_GET_GUINT32 (q->data + 4);
7097 if (ver == 0x4107cd18)
7098 version_desc = "Excel 2000 ?";
7099 else
7100 version_desc = "Excel 97 +";
7101 } else if (ver->version >= MS_BIFF_V7)
7102 version_desc = "Excel 95";
7103 else if (ver->version >= MS_BIFF_V5)
7104 version_desc = "Excel 5.x";
7105 else if (ver->version >= MS_BIFF_V4)
7106 version_desc = "Excel 4.x";
7107 else if (ver->version >= MS_BIFF_V3)
7108 version_desc = "Excel 3.x - shouldn't happen";
7109 else if (ver->version >= MS_BIFF_V2)
7110 version_desc = "Excel 2.x - shouldn't happen";
7111 } else if (ver->type == MS_BIFF_TYPE_Worksheet ||
7112 ver->type == MS_BIFF_TYPE_Chart) {
7113 BiffBoundsheetData *bs = g_hash_table_lookup (
7114 importer->boundsheet_data_by_stream, GINT_TO_POINTER (q->streamPos));
7115 ExcelReadSheet *esheet;
7116 if (bs == NULL) {
7117 if (ver->version > MS_BIFF_V4) /* be anal */
7118 g_printerr ("Sheet offset in stream of 0x%lx not found in list\n",
7119 (long)q->streamPos);
7120 if (*current_sheet >= importer->excel_sheets->len) {
7121 esheet = excel_sheet_new (importer, "Worksheet", GNM_SHEET_DATA);
7122 /* Top level worksheets existed up to & including 4.x */
7123 gnm_xl_importer_set_version (importer, ver->version);
7124 if (ver->version >= MS_BIFF_V5)
7125 version_desc = ">= Excel 5 with no BOUNDSHEET ?? - shouldn't happen";
7126 else if (ver->version >= MS_BIFF_V4)
7127 version_desc = "Excel 4.x single worksheet";
7128 else if (ver->version >= MS_BIFF_V3)
7129 version_desc = "Excel 3.x single worksheet";
7130 else if (ver->version >= MS_BIFF_V2)
7131 version_desc = "Excel 2.x single worksheet";
7132 } else
7133 esheet = g_ptr_array_index (importer->excel_sheets, *current_sheet);
7134 } else
7135 esheet = bs->esheet;
7137 g_return_if_fail (esheet != NULL);
7138 (*current_sheet)++;
7140 if (ver->type == MS_BIFF_TYPE_Worksheet) {
7141 excel_read_sheet (q, importer, wb_view, esheet);
7142 ms_container_realize_objs (sheet_container (esheet));
7143 /* reverse the sheet objects satck order */
7144 esheet->sheet->sheet_objects = g_slist_reverse (esheet->sheet->sheet_objects);
7145 } else {
7146 SheetObject *obj = sheet_object_graph_new (NULL);
7147 ms_excel_chart_read (q, sheet_container (esheet),
7148 obj, esheet->sheet);
7149 sheet_object_set_sheet (obj, esheet->sheet);
7150 g_object_unref (obj);
7153 } else if (ver->type == MS_BIFF_TYPE_VBModule ||
7154 ver->type == MS_BIFF_TYPE_Macrosheet) {
7155 /* Skip contents of Module, or MacroSheet */
7156 if (ver->type != MS_BIFF_TYPE_Macrosheet)
7157 version_desc = "VB Module";
7158 else {
7159 (*current_sheet)++;
7160 version_desc = "XLM Macrosheet";
7163 while (ms_biff_query_next (q) && q->opcode != BIFF_EOF)
7164 d (5, ms_biff_query_dump (q););
7165 if (q->opcode != BIFF_EOF)
7166 g_warning ("EXCEL: file format error. Missing BIFF_EOF");
7167 } else if (ver->type == MS_BIFF_TYPE_Workspace) {
7168 /* Multiple sheets, XLW format from Excel 4.0 */
7169 version_desc = "Excel 4.x workbook";
7170 gnm_xl_importer_set_version (importer, ver->version);
7171 } else
7172 g_printerr ("Unknown BOF (%x)\n", ver->type);
7174 if (NULL != version_desc) {
7175 d (1, g_printerr ("%s\n", version_desc););
7179 static void
7180 excel_read_CODEPAGE (BiffQuery *q, GnmXLImporter *importer)
7182 /* This seems to appear within a workbook */
7183 /* MW: And on Excel seems to drive the display
7184 of currency amounts. */
7185 XL_CHECK_CONDITION (q->length >= 2);
7186 gnm_xl_importer_set_codepage (importer,
7187 GSF_LE_GET_GUINT16 (q->data));
7190 static void
7191 excel_read_RECALCID (BiffQuery *q, GnmXLImporter *importer)
7193 guint32 engine;
7195 XL_CHECK_CONDITION (q->length >= 8);
7196 engine = GSF_LE_GET_GUINT32 (q->data + 4);
7198 (void)engine;
7201 static void
7202 excel_read_UNCALCED (BiffQuery *q, GnmXLImporter *importer)
7206 void
7207 excel_read_workbook (GOIOContext *context, WorkbookView *wb_view,
7208 GsfInput *input,
7209 gboolean *is_double_stream_file,
7210 char const *opt_enc)
7212 GnmXLImporter *importer;
7213 BiffQuery *q;
7214 MsBiffBofData *ver = NULL;
7215 unsigned current_sheet = 0;
7216 const char *problem_loading = NULL;
7217 gboolean stop_loading = FALSE;
7218 gboolean prev_was_eof = FALSE;
7220 go_io_progress_message (context, _("Reading file..."));
7221 go_io_value_progress_set (context, gsf_input_size (input), N_BYTES_BETWEEN_PROGRESS_UPDATES);
7222 q = ms_biff_query_new (input);
7224 importer = gnm_xl_importer_new (context, wb_view, opt_enc);
7226 *is_double_stream_file = FALSE;
7227 if (ms_biff_query_next (q) &&
7228 (q->opcode == BIFF_BOF_v0 ||
7229 q->opcode == BIFF_BOF_v2 ||
7230 q->opcode == BIFF_BOF_v4 ||
7231 q->opcode == BIFF_BOF_v8))
7232 excel_read_BOF (q, importer, wb_view, context,
7233 &ver, &current_sheet);
7234 while (!stop_loading && /* we have not hit the end */
7235 problem_loading == NULL && /* there were no problems so far */
7236 ms_biff_query_next (q)) { /* we can load the record */
7238 d (5, {
7239 const char *opname = biff_opcode_name (q->opcode);
7240 g_printerr ("Opcode: 0x%x %s\n",
7241 q->opcode,
7242 opname ? opname : "unknown");
7245 switch (q->opcode) {
7246 case BIFF_BOF_v0:
7247 case BIFF_BOF_v2:
7248 case BIFF_BOF_v4:
7249 case BIFF_BOF_v8:
7250 excel_read_BOF (q, importer, wb_view, context, &ver, &current_sheet);
7251 break;
7253 case BIFF_EOF:
7254 prev_was_eof = TRUE;
7255 d (0, g_printerr ("End of worksheet spec.\n"););
7256 break;
7258 case BIFF_FONT_v0:
7259 case BIFF_FONT_v2: excel_read_FONT (q, importer); break;
7260 case BIFF_WINDOW1: excel_read_WINDOW1 (q, wb_view); break;
7261 case BIFF_BOUNDSHEET: excel_read_BOUNDSHEET (q, importer); break;
7262 case BIFF_PALETTE: excel_read_PALETTE (q, importer); break;
7264 case BIFF_XF_OLD_v0:
7265 case BIFF_XF_OLD_v2:
7266 case BIFF_XF_OLD_v4: excel_read_XF_OLD (q, importer); break;
7267 case BIFF_XF: excel_read_XF (q, importer); break;
7269 case BIFF_EXTERNCOUNT: /* ignore */ break;
7270 case BIFF_EXTERNSHEET:
7271 excel_read_EXTERNSHEET (q, importer, ver);
7272 break;
7274 case BIFF_PRECISION : {
7275 #if 0
7276 /* FIXME: implement in gnumeric */
7277 /* state of 'Precision as Displayed' option */
7278 guint16 const data = GSF_LE_GET_GUINT16 (q->data);
7279 gboolean const prec_as_displayed = (data == 0);
7280 #endif
7281 break;
7284 case BIFF_FORMAT_v0:
7285 case BIFF_FORMAT_v4: excel_read_FORMAT (q, importer); break;
7287 case BIFF_BACKUP: break;
7288 case BIFF_CODEPAGE: /* DUPLICATE 42 */
7289 excel_read_CODEPAGE (q, importer);
7290 break;
7292 case BIFF_OBJPROTECT:
7293 case BIFF_PROTECT:
7294 excel_read_workbook_PROTECT (q, wb_view);
7295 break;
7297 case BIFF_PASSWORD:
7298 break;
7300 case BIFF_FILEPASS: /* All records after this are encrypted */
7301 problem_loading = excel_read_FILEPASS(q, importer);
7302 break;
7304 case BIFF_STYLE:
7305 break;
7307 case BIFF_WINDOWPROTECT:
7308 break;
7310 case BIFF_EXTERNNAME_v0:
7311 case BIFF_EXTERNNAME_v2: excel_read_EXTERNNAME (q, &importer->container); break;
7312 case BIFF_NAME_v0:
7313 case BIFF_NAME_v2: excel_read_NAME (q, importer, NULL); break;
7314 case BIFF_XCT: excel_read_XCT (q, importer); break;
7316 case BIFF_WRITEACCESS:
7317 break;
7319 case BIFF_HIDEOBJ: break;
7320 case BIFF_FNGROUPCOUNT: break;
7321 case BIFF_MMS: break;
7323 /* Flags that the project has some VBA */
7324 case BIFF_OBPROJ: break;
7325 case BIFF_BOOKBOOL: break;
7326 case BIFF_COUNTRY: break;
7327 case BIFF_INTERFACEHDR: break;
7328 case BIFF_INTERFACEEND: break;
7329 case BIFF_TOOLBARHDR: break;
7330 case BIFF_TOOLBAREND: break;
7332 case BIFF_1904: excel_read_1904 (q, importer); break;
7334 case BIFF_SELECTION: /* 0, NOT 10 */
7335 break;
7337 case BIFF_DIMENSIONS_v0:
7338 /* Ignore files that pad the end with zeros */
7339 if (prev_was_eof) {
7340 stop_loading = TRUE;
7341 break;
7343 /* fall through */
7344 case BIFF_DIMENSIONS_v2:
7345 excel_read_DIMENSIONS (q, NULL);
7346 break;
7348 case BIFF_OBJ: ms_read_OBJ (q, &importer->container, NULL); break;
7349 case BIFF_SCL: break;
7350 case BIFF_TABIDCONF: break;
7351 case BIFF_MS_O_DRAWING:
7352 case BIFF_MS_O_DRAWING_GROUP:
7353 case BIFF_MS_O_DRAWING_SELECTION:
7354 ms_escher_parse (q, &importer->container, FALSE);
7355 break;
7357 case BIFF_ADDMENU:
7358 d (1, g_printerr ("%smenu with %d sub items",
7359 (GSF_LE_GET_GUINT8 (q->data + 6) == 1) ? "" : "Placeholder ",
7360 GSF_LE_GET_GUINT8 (q->data + 5)););
7361 break;
7363 case BIFF_SST: excel_read_SST (q, importer); break;
7364 case BIFF_EXTSST: excel_read_EXSST (q, importer); break;
7366 case BIFF_DSF: /* stored in the biff8 workbook */
7367 case BIFF_XL5MODIFY: /* stored in the biff5/7 book */
7369 gboolean dsf = (q->length >= 2 &&
7370 GSF_LE_GET_GUINT16 (q->data));
7371 d (0, g_printerr ("Double stream file : %d\n",
7372 dsf););
7373 if (dsf)
7374 *is_double_stream_file = TRUE;
7375 break;
7378 case BIFF_XL9FILE:
7379 d (0, g_printerr ("%s\n", "XL 2000 file"););
7380 break;
7382 case BIFF_RECALCID: excel_read_RECALCID (q, importer); break;
7383 case BIFF_UNCALCED: excel_read_UNCALCED (q, importer); break;
7384 case BIFF_REFRESHALL: break;
7385 case BIFF_CODENAME: excel_read_CODENAME (q, importer, NULL); break;
7386 case BIFF_PROT4REVPASS: break;
7388 case BIFF_USESELFS: break;
7389 case BIFF_TABID: break;
7390 case BIFF_PROT4REV:
7391 break;
7394 case BIFF_SUPBOOK: excel_read_SUPBOOK (q, importer); break;
7396 case BIFF_SXStreamID: xls_read_SXStreamID (importer, q, gsf_input_container (input)); break;
7398 default:
7399 excel_unexpected_biff (q, "Workbook", ms_excel_read_debug);
7400 break;
7402 /* check here in case any of the handlers read additional records */
7403 prev_was_eof = (q->opcode == BIFF_EOF);
7406 ms_biff_query_destroy (q);
7407 if (ver)
7408 ms_biff_bof_data_destroy (ver);
7409 go_io_progress_unset (context);
7411 d (1, g_printerr ("finished read\n"););
7413 gnm_xl_importer_free (importer);
7415 /* If we were forced to stop then the load failed */
7416 if (problem_loading != NULL)
7417 go_cmd_context_error_import (GO_CMD_CONTEXT (context), problem_loading);
7421 static GSList *formats;
7424 * These are special in that they appear to be saved as macros.
7425 * Excel will only recognize them when saved as macros: neither
7426 * externname as "IFERROR" nor "_xlfn.IFERROR" will work.
7428 static const ExcelFuncDesc excel97_func_desc[] = {
7429 { 0xff, "_xlfn.AVERAGEIF", -1, -1, XL_XLM },
7430 { 0xff, "_xlfn.AVERAGEIFS", -1, -1, XL_XLM },
7431 { 0xff, "_xlfn.CUBEKPIMEMBER", -1, -1, XL_XLM },
7432 { 0xff, "_xlfn.CUBEMEMBER", -1, -1, XL_XLM },
7433 { 0xff, "_xlfn.CUBEMEMBERPROPERTY", -1, -1, XL_XLM },
7434 { 0xff, "_xlfn.CUBERANKEDMEMBER", -1, -1, XL_XLM },
7435 { 0xff, "_xlfn.CUBESET", -1, -1, XL_XLM },
7436 { 0xff, "_xlfn.CUBESETCOUNT", -1, -1, XL_XLM },
7437 { 0xff, "_xlfn.CUBEVALUE", -1, -1, XL_XLM },
7438 { 0xff, "_xlfn.COUNTIFS", -1, -1, XL_XLM },
7439 { 0xff, "_xlfn.IFERROR", 2, 2, XL_XLM, 2, 'V', "VV" },
7440 { 0xff, "_xlfn.SUMIFS", -1, -1, XL_XLM }
7443 void
7444 excel_read_init (void)
7446 int i;
7447 int mbd = go_locale_month_before_day ();
7448 GOFormat *fmt;
7450 fmt = go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATE);
7451 formats = g_slist_prepend (formats, fmt);
7452 excel_builtin_formats[0x0e] = go_format_as_XL (fmt);
7454 fmt = go_format_new_magic (GO_FORMAT_MAGIC_MEDIUM_DATE);
7455 formats = g_slist_prepend (formats, fmt);
7456 excel_builtin_formats[0x0f] = go_format_as_XL (fmt);
7458 /* Doesn't seem to have a name. */
7459 excel_builtin_formats[0x10] = mbd ? "d-mmm" : "mmm-d";
7461 fmt = go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATETIME);
7462 formats = g_slist_prepend (formats, fmt);
7463 excel_builtin_formats[0x16] = go_format_as_XL (fmt);
7465 excel_func_by_name = g_hash_table_new (g_str_hash, g_str_equal);
7466 for (i = 0; i < excel_func_desc_size; i++) {
7467 const ExcelFuncDesc *efd = excel_func_desc + i;
7468 const char *name = efd->name;
7469 GnmFunc *func = gnm_func_lookup (name, NULL);
7471 /* Fix case. */
7472 if (func)
7473 name = gnm_func_get_name (func, FALSE);
7475 g_assert (g_hash_table_lookup (excel_func_by_name, name) ==
7476 NULL);
7477 g_hash_table_insert (excel_func_by_name,
7478 (gpointer)name,
7479 (gpointer)efd);
7482 for (i = 0; i < (int)G_N_ELEMENTS(excel97_func_desc); i++) {
7483 const ExcelFuncDesc *efd = excel97_func_desc + i;
7484 const char *excel_name = efd->name;
7485 const char *gnm_name = strchr (excel_name, '.') + 1;
7486 GnmFunc *func = gnm_func_lookup (gnm_name, NULL);
7488 /* Fix case. */
7489 if (func)
7490 gnm_name = gnm_func_get_name (func, FALSE);
7492 g_assert (g_hash_table_lookup (excel_func_by_name, gnm_name) ==
7493 NULL);
7494 g_hash_table_insert (excel_func_by_name,
7495 (gpointer)gnm_name,
7496 (gpointer)efd);
7499 empty_attr_list = pango_attr_list_new ();
7502 void
7503 excel_read_cleanup (void)
7505 g_hash_table_destroy (excel_func_by_name);
7506 excel_func_by_name = NULL;
7508 g_slist_free_full (formats, (GDestroyNotify)go_format_unref);
7509 formats = NULL;
7511 pango_attr_list_unref (empty_attr_list);
7512 empty_attr_list = NULL;