Shutdown: help the style leak printer out a bit.
[gnumeric.git] / plugins / excel / ms-excel-write.c
blob668222311d9d316bf6900ebecc6ae955fa3e1b63
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /**
3 * ms-excel-write.c: MS Excel support for Gnumeric
5 * Authors:
6 * Michael Meeks (mmeeks@gnu.org)
7 * Jon K Hellan (hellan@acm.org)
8 * Jody Goldberg (jody@gnome.org)
9 * Morten Welinder (terra@gnome.org)
11 * (C) 1998-2014 Michael Meeks, Jon K Hellan, Jody Goldberg, Morten Welinder
12 **/
15 * FIXME: Check for errors and propagate upward. We've only started.
17 #include <gnumeric-config.h>
18 #include <glib/gi18n-lib.h>
19 #include <gnumeric.h>
20 #include <string.h>
21 #include "ms-formula-write.h"
22 #include "boot.h"
23 #include "ms-biff.h"
24 #include "excel.h"
25 #include "ranges.h"
26 #include "pattern.h"
27 #include "sheet-filter.h"
28 #include "ms-excel-write.h"
29 #include "ms-excel-xf.h"
30 #include "ms-escher.h"
31 #include "ms-obj.h"
32 #include "ms-chart.h"
33 #include "formula-types.h"
35 #include <sheet-filter-combo.h>
36 #include <gnm-format.h>
37 #include <gutils.h>
38 #include <position.h>
39 #include <style-color.h>
40 #include <cell.h>
41 #include <func.h>
42 #include <cellspan.h>
43 #include <sheet.h>
44 #include <sheet-view.h>
45 #include <sheet-object.h>
46 #include <sheet-object-cell-comment.h>
47 #include <sheet-object-graph.h>
48 #include <sheet-object-image.h>
49 #include <sheet-object-widget.h>
50 #include <gnm-so-filled.h>
51 #include <gnm-so-line.h>
52 #include <application.h>
53 #include <style.h>
54 #include <style-conditions.h>
55 #include <validation.h>
56 #include <input-msg.h>
57 #include <hlink.h>
58 #include <sheet-style.h>
59 #include <libgnumeric.h>
60 #include <value.h>
61 #include <parse-util.h>
62 #include <print-info.h>
63 #include <workbook-view.h>
64 #include <workbook-priv.h>
65 #include <goffice/goffice.h>
66 #include <command-context.h>
67 #include <expr.h>
68 #include <expr-impl.h>
69 #include <expr-name.h>
70 #include <mathfunc.h>
72 #include <go-data-slicer.h>
73 #include <gsf/gsf-utils.h>
74 #include <gsf/gsf-output.h>
75 #include <gsf/gsf-outfile.h>
76 #include <gsf/gsf-msole-utils.h>
78 #include <math.h>
79 #include <zlib.h>
80 #include <crypt-md4.h>
82 /* #define NO_DEBUG_EXCEL */
83 #ifndef NO_DEBUG_EXCEL
84 #define d(level, code) do { if (ms_excel_write_debug > level) { code } } while (0)
85 #else
86 #define d(level, code)
87 #endif
89 #define BLIP_ID_LEN 16
90 #define BSE_HDR_LEN 44
91 #define RASTER_BLIP_HDR_LEN 25
92 #define VECTOR_BLIP_HDR_LEN 58
94 #define N_CELLS_BETWEEN_UPDATES 100
96 typedef struct {
97 char *type;
98 GByteArray bytes;
99 gint32 uncomp_len;
100 gint32 header_len;
101 gboolean needs_free;
102 guint8 id[BLIP_ID_LEN];
103 SheetObject *so;
104 } BlipInf;
106 typedef struct _BlipType BlipType;
108 struct _BlipType {
109 char const *type_name;
110 guint8 type;
111 guint8 blip_tag[2];
112 void (*handler) (ExcelWriteState *ewb,
113 BlipInf *blip,
114 const BlipType *bt);
118 * We sometimes need multiple XF records for a single Gnumeric style. That
119 * happens, for example when we synthesize the cell-is-quoted flag.
121 typedef struct {
122 GnmStyle const *style;
123 int variant; /* bit 0: cell-is-quoted; bit 2: cell-contains-newline */
124 } ExcelStyleVariant;
126 static guint
127 excel_style_variant_hash (ExcelStyleVariant const *esv)
129 return gnm_style_hash_XL (esv->style) ^ esv->variant;
132 static gboolean
133 excel_style_variant_equal (ExcelStyleVariant const *a,
134 ExcelStyleVariant const *b)
136 return a->variant == b->variant && gnm_style_equal (a->style, b->style);
139 static guint
140 go_color_to_bgr (GOColor const c)
142 guint32 abgr;
143 abgr = GO_COLOR_UINT_R(c);
144 abgr |= GO_COLOR_UINT_G(c) << 8;
145 abgr |= GO_COLOR_UINT_B(c) << 16;
146 return abgr;
149 static guint
150 gnm_color_to_bgr (GnmColor const *c)
152 return go_color_to_bgr (c->go_color);
156 * map_pattern_to_xl
157 * @i Gnumeric pattern index
159 * Map Gnumeric pattern index to Excel ditto.
161 * FIXME:
162 * Move this and ms-excel-read.c::excel_map_pattern_index_from_excel
163 * to common utility file. Generate one map from the other for
164 * consistency
166 static int
167 map_pattern_to_xl (int i)
169 static int const map_to_excel[GNM_PATTERNS_MAX + 1] = {
171 1, 3, 2, 4, 17, 18,
172 5, 6, 8, 7, 9, 10,
173 11, 12, 13, 14, 15, 16,
174 /* The above are XL patterns. We have a few more. */
175 12, 5, 4, 4, 3,
176 1, 2
179 /* Default to Solid if out of range */
180 g_return_val_if_fail (i >= 0 && i < (int)G_N_ELEMENTS (map_to_excel),
183 return map_to_excel[i];
186 static guint
187 map_border_to_xl (GnmStyleBorderType btype, MsBiffVersion ver)
189 guint ibtype = btype;
191 if (btype <= GNM_STYLE_BORDER_NONE)
192 ibtype = GNM_STYLE_BORDER_NONE;
194 if (ver <= MS_BIFF_V7 && btype > GNM_STYLE_BORDER_HAIR)
195 ibtype = GNM_STYLE_BORDER_MEDIUM;
197 return ibtype;
200 static guint16
201 map_color_to_palette (XLExportBase const *xle,
202 GnmColor const *c, guint16 auto_index)
204 if (NULL == c || c->is_auto)
205 return auto_index;
206 return palette_get_index (xle, gnm_color_to_bgr (c));
209 void
210 excel_sheet_extent (Sheet const *sheet, GnmRange *extent, GnmStyle **col_styles,
211 int maxcols, int maxrows, GOIOContext *io_context)
213 int i;
214 GnmRange r;
216 /* Ignore spans and merges past the bound */
217 *extent = sheet_get_extent (sheet, FALSE, TRUE);
219 range_init (&r, 0, 0,
220 MAX (maxcols, gnm_sheet_get_max_cols(sheet)) - 1,
221 MAX (maxrows, gnm_sheet_get_max_rows(sheet)) - 1);
222 sheet_style_get_nondefault_extent (sheet, extent, &r, col_styles);
224 if (extent->end.col >= maxcols) {
225 go_io_warning (io_context,
226 ngettext("Some content will be lost when saving. "
227 "This format only supports %u column, and "
228 "this workbook has %d",
229 "Some content will be lost when saving. "
230 "This format only supports %u columns, "
231 "and this workbook has %d",
232 maxcols),
233 maxcols, extent->end.col);
234 extent->end.col = maxcols - 1;
236 if (extent->end.row >= maxrows) {
237 go_io_warning (io_context,
238 ngettext("Some content will be lost when saving. "
239 "This format only supports %u row, and "
240 "this workbook has %d",
241 "Some content will be lost when saving. "
242 "This format only supports %u rows, "
243 "and this workbook has %d",
244 maxrows),
245 maxrows, extent->end.row);
246 extent->end.row = maxrows - 1;
249 /* include collapsed or hidden rows */
250 for (i = maxrows ; i-- > extent->end.row ; )
251 if (!col_row_info_is_empty (sheet_row_get (sheet, i))) {
252 extent->end.row = i;
253 break;
255 /* include collapsed or hidden rows */
256 for (i = maxcols ; i-- > extent->end.col ; )
257 if (!col_row_info_is_empty (sheet_col_get (sheet, i))) {
258 extent->end.col = i;
259 break;
264 * excel_strlen :
265 * @str: The utf8 encoded string in question
266 * @bytes:
268 * Returns the size of the string in _characters_ and stores the number of
269 * bytes in @bytes.
271 static unsigned int
272 excel_strlen (guint8 const *str, size_t *bytes)
274 guint8 const *p = str;
275 size_t i = 0;
277 g_return_val_if_fail (str != NULL, 0);
279 for (; *p ; i++)
280 p = (guint8 const *)g_utf8_next_char (p);
282 if (bytes != NULL)
283 *bytes = p - str;
284 return i;
287 static gpointer
288 excel_convert_string (BiffPut *bp, const char *txt, size_t *out_bytes)
290 GError *err = NULL;
291 size_t bytes_read;
292 GString *accum;
293 gpointer res;
294 gboolean is_ilseq;
296 res = g_convert_with_iconv
297 (txt, -1,
298 bp->convert,
299 &bytes_read, out_bytes,
300 &err);
301 if (res)
302 return res;
304 is_ilseq = g_error_matches (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE);
305 g_error_free (err);
307 if (!is_ilseq) {
308 g_printerr ("Unexpected conversion error for string\n");
309 *out_bytes = 0;
310 return g_strdup ("");
314 * Invalid character. Be crude: convert the sequence before,
315 * add a fallback character, and finally convert the sequence
316 * after.
318 accum = g_string_new (NULL);
320 res = g_convert_with_iconv
321 (txt, bytes_read,
322 bp->convert,
323 NULL, out_bytes,
324 NULL);
325 if (res) {
326 g_string_append_len (accum, res, *out_bytes);
327 g_free (res);
330 res = g_convert_with_iconv
331 ("?", -1,
332 bp->convert,
333 NULL, out_bytes,
334 NULL);
335 if (res) {
336 g_string_append_len (accum, res, *out_bytes);
337 g_free (res);
340 res = excel_convert_string (bp,
341 g_utf8_next_char (txt + bytes_read),
342 out_bytes);
343 if (res) {
344 g_string_append_len (accum, res, *out_bytes);
345 g_free (res);
348 *out_bytes = accum->len;
350 /* Ensure termination with 4+ NULs. */
351 g_string_append_len (accum, "\0\0\0\0", 4);
353 return g_string_free (accum, FALSE);
357 static guint32
358 string_maxlen[4] = {
359 /* STR_ONE_BYTE_LENGTH */ G_MAXUINT8,
360 /* STR_TWO_BYTE_LENGTH */ G_MAXUINT16,
361 /* STR_FOUR_BYTE_LENGTH */ G_MAXUINT32,
362 /* STR_NO_LENGTH */ G_MAXUINT32
366 * excel_write_string :
367 * @bp:
368 * @flags:
369 * @txt:
371 * NOTE : I considered putting markup here too to be strictly correct and
372 * export rich text directly. But it was easier to just use RSTRING.
374 * Returns: The number of bytes used to write the len, header, and text
376 unsigned
377 excel_write_string (BiffPut *bp, WriteStringFlags flags,
378 guint8 const *txt)
380 size_t char_len, byte_len, out_bytes, len_len, max_len, items, conv_bytes;
381 gboolean need_uni_marker =
382 (bp->version >= MS_BIFF_V8 && !(flags & STR_SUPPRESS_HEADER));
383 guint8 *convdata = NULL;
384 guchar isunistr, tmp[4];
386 g_return_val_if_fail (txt != NULL, 0);
388 /* before biff8 all lengths were in bytes */
389 if (bp->version < MS_BIFF_V8)
390 flags |= STR_LEN_IN_BYTES;
392 len_len = ((flags & STR_LENGTH_MASK) == STR_NO_LENGTH)
394 : 1 << (flags & STR_LENGTH_MASK);
395 max_len = string_maxlen[flags & STR_LENGTH_MASK];
397 char_len = excel_strlen (txt, &byte_len);
398 if (char_len != byte_len || (flags & STR_SUPPRESS_HEADER)) {
399 convdata = excel_convert_string (bp, txt, &conv_bytes);
400 isunistr = 1;
402 /* g_convert_with_iconv terminates with 4 NULs. */
403 if (flags & STR_TRAILING_NULL)
404 conv_bytes += 2;
406 items = (flags & STR_LEN_IN_BYTES)
407 ? conv_bytes
408 : conv_bytes / 2;
409 if (items > max_len) {
410 g_printerr ("Truncating string of %u %s\n",
411 (guint)items,
412 (flags & STR_LEN_IN_BYTES) ? "bytes" : "characters");
413 items = max_len;
414 conv_bytes = (flags & STR_LEN_IN_BYTES)
415 ? items
416 : items * 2;
418 } else {
419 /* char_len == byte_len here, so just use char_len */
421 isunistr = 0;
423 if (char_len > max_len) {
424 g_printerr ("Truncating string of %u %s\n",
425 (guint)char_len,
426 (flags & STR_LEN_IN_BYTES) ? "bytes" : "characters");
427 char_len = max_len;
430 conv_bytes = items = char_len;
433 switch (flags & STR_LENGTH_MASK) {
434 default:
435 case STR_NO_LENGTH:
436 break;
437 case STR_ONE_BYTE_LENGTH:
438 GSF_LE_SET_GUINT8 (tmp, items);
439 break;
440 case STR_TWO_BYTE_LENGTH:
441 GSF_LE_SET_GUINT16 (tmp, items);
442 break;
443 case STR_FOUR_BYTE_LENGTH:
444 GSF_LE_SET_GUINT32 (tmp, items);
445 break;
447 ms_biff_put_var_write (bp, tmp, len_len);
448 out_bytes = len_len;
450 if (need_uni_marker) {
451 ms_biff_put_var_write (bp, &isunistr, 1);
452 out_bytes++;
454 ms_biff_put_var_write (bp, convdata ? convdata : txt, conv_bytes);
455 out_bytes += conv_bytes;
457 g_free (convdata);
459 return out_bytes;
462 unsigned
463 excel_write_BOF (BiffPut *bp, MsBiffFileType type)
465 guint8 *data;
466 unsigned ans;
467 guint len = 8;
468 guint16 record;
470 switch (bp->version) {
471 case MS_BIFF_V2: record = BIFF_BOF_v0; break;
472 case MS_BIFF_V3: record = BIFF_BOF_v2; break;
473 case MS_BIFF_V4: record = BIFF_BOF_v4; break;
475 case MS_BIFF_V8: len = 16;
476 case MS_BIFF_V7: record = BIFF_BOF_v8; break;
477 default:
478 g_warning ("Unknown biff version '%d' requested.", bp->version);
479 return 0;
481 data = ms_biff_put_len_next (bp, record, len);
482 ans = bp->streamPos;
484 switch (type) {
485 case MS_BIFF_TYPE_Workbook:
486 GSF_LE_SET_GUINT16 (data+2, 0x0005);
487 break;
488 case MS_BIFF_TYPE_VBModule:
489 GSF_LE_SET_GUINT16 (data+2, 0x0006);
490 break;
491 case MS_BIFF_TYPE_Worksheet:
492 GSF_LE_SET_GUINT16 (data+2, 0x0010);
493 break;
494 case MS_BIFF_TYPE_Chart:
495 GSF_LE_SET_GUINT16 (data+2, 0x0020);
496 break;
497 case MS_BIFF_TYPE_Macrosheet:
498 GSF_LE_SET_GUINT16 (data+2, 0x0040);
499 break;
500 case MS_BIFF_TYPE_Workspace:
501 GSF_LE_SET_GUINT16 (data+2, 0x0100);
502 break;
503 default:
504 g_warning ("Unknown type.");
505 break;
508 switch (bp->version) {
509 case MS_BIFF_V8:
510 GSF_LE_SET_GUINT16 (data+ 0, 0x0600); /* worksheet */
511 GSF_LE_SET_GUINT16 (data+ 4, 0x2775); /* build id == XP SP3 */
512 GSF_LE_SET_GUINT16 (data+ 6, 0x07cd); /* build year (= 1997) */
513 GSF_LE_SET_GUINT32 (data+ 8, 0x000080c9); /* flags */
514 GSF_LE_SET_GUINT32 (data+12, 0x00000206);
515 break;
517 case MS_BIFF_V7:
518 GSF_LE_SET_GUINT16 (data, 0x0500); /* worksheet */
519 /* fall through */
521 case MS_BIFF_V5:
522 GSF_LE_SET_GUINT16 (data+4, 0x096c);
523 GSF_LE_SET_GUINT16 (data+6, 0x07c9);
524 break;
526 default:
527 g_printerr ("FIXME: I need some magic numbers\n");
528 GSF_LE_SET_GUINT16 (data+4, 0x0);
529 GSF_LE_SET_GUINT16 (data+6, 0x0);
530 break;
532 ms_biff_put_commit (bp);
534 return ans;
537 static inline double
538 points_to_inches (double pts)
540 return pts / 72.0;
543 void
544 excel_write_SETUP (BiffPut *bp, ExcelWriteSheet *esheet)
546 GnmPrintInformation *pi = NULL;
547 double header = 0., footer = 0.;
548 guint8 *data = ms_biff_put_len_next (bp, BIFF_SETUP, 34);
549 guint16 flags = 0;
550 guint16 scale = 100;
551 guint16 papersize = 0; /* _invalid_ paper size */
553 if (NULL != esheet)
554 pi = esheet->gnum_sheet->print_info;
555 if (NULL != pi) {
556 GtkPageOrientation orient;
557 GtkPaperSize *ps;
558 gboolean rotated = FALSE; /* ??? */
560 if (pi->print_across_then_down)
561 flags |= 0x01;
562 orient = print_info_get_paper_orientation (pi);
563 if (orient == GTK_PAGE_ORIENTATION_PORTRAIT
564 || orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
565 flags |= 0x02;
566 if (pi->print_black_and_white)
567 flags |= 0x08;
568 if (pi->print_as_draft)
569 flags |= 0x10;
570 switch (pi->comment_placement) {
571 case GNM_PRINT_COMMENTS_NONE: break;
572 default:
573 case GNM_PRINT_COMMENTS_IN_PLACE: flags |= 0x020; break;
574 case GNM_PRINT_COMMENTS_AT_END: flags |= 0x220; break;
577 switch (pi->error_display) {
578 default :
579 case GNM_PRINT_ERRORS_AS_DISPLAYED: break;
580 case GNM_PRINT_ERRORS_AS_BLANK: flags |= 0x400; break;
581 case GNM_PRINT_ERRORS_AS_DASHES: flags |= 0x800; break;
582 case GNM_PRINT_ERRORS_AS_NA: flags |= 0xC00; break;
585 if (pi->scaling.percentage.x < USHRT_MAX)
586 scale = pi->scaling.percentage.x + .5;
587 print_info_get_margins (pi, &header, &footer,
588 NULL, NULL, NULL, NULL);
590 ps = print_info_get_paper_size (pi);
591 if (ps)
592 papersize = xls_paper_size (ps, rotated);
593 } else
594 flags |= 0x44; /* mark orientation, copies, and start page as being invalid */
595 header = points_to_inches (header);
596 footer = points_to_inches (footer);
598 GSF_LE_SET_GUINT16 (data + 0, papersize);
599 GSF_LE_SET_GUINT16 (data + 2, scale); /* scaling factor */
600 GSF_LE_SET_GUINT16 (data + 4, pi ? pi->start_page : 0);
601 GSF_LE_SET_GUINT16 (data + 6, pi ? pi->scaling.dim.cols : 1);
602 GSF_LE_SET_GUINT16 (data + 8, pi ? pi->scaling.dim.rows : 1);
603 GSF_LE_SET_GUINT16 (data + 10, flags);
604 GSF_LE_SET_GUINT32 (data + 12, 600); /* guess x resolution */
605 GSF_LE_SET_GUINT32 (data + 14, 600); /* guess y resolution */
606 gsf_le_set_double (data + 16, header);
607 gsf_le_set_double (data + 24, footer);
608 GSF_LE_SET_GUINT16 (data + 32, pi ? pi->n_copies : 1);
609 ms_biff_put_commit (bp);
612 static void
613 excel_write_externsheets_v7 (ExcelWriteState *ewb)
615 /* 2 byte expression #REF! */
616 static guint8 const expr_ref [] = { 0x02, 0, 0x1c, 0x17 };
617 static guint8 const zeros [] = { 0, 0, 0, 0, 0 ,0 };
618 static guint8 const magic_addin[] = { 0x01, 0x3a };
619 static guint8 const magic_self[] = { 0x01, 0x04 };
620 unsigned i, num_sheets = ewb->esheets->len;
622 ms_biff_put_2byte (ewb->bp, BIFF_EXTERNCOUNT, num_sheets + 2);
624 for (i = 0; i < num_sheets; i++) {
625 ExcelWriteSheet const *esheet = g_ptr_array_index (ewb->esheets, i);
626 unsigned len;
627 guint8 data[2];
629 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNSHEET);
630 len = excel_strlen (
631 esheet->gnum_sheet->name_unquoted, NULL);
633 GSF_LE_SET_GUINT8 (data, len);
634 GSF_LE_SET_GUINT8 (data + 1, 3); /* undocumented */
635 ms_biff_put_var_write (ewb->bp, data, 2);
636 excel_write_string (ewb->bp, STR_NO_LENGTH,
637 esheet->gnum_sheet->name_unquoted);
638 ms_biff_put_commit (ewb->bp);
641 /* Add magic externsheets for addin functions and self refs */
642 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNSHEET);
643 ms_biff_put_var_write (ewb->bp, magic_addin, sizeof magic_addin);
644 ms_biff_put_commit (ewb->bp);
646 for (i = 0; i < ewb->externnames->len ; i++) {
647 const char *func_name;
649 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNNAME_v0); /* yes v0 */
650 ms_biff_put_var_write (ewb->bp, zeros, 6);
652 /* write the name and the 1 byte length */
653 func_name = g_ptr_array_index (ewb->externnames, i);
654 excel_write_string (ewb->bp, STR_ONE_BYTE_LENGTH, func_name);
656 ms_biff_put_var_write (ewb->bp, expr_ref, sizeof (expr_ref));
657 ms_biff_put_commit (ewb->bp);
659 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNSHEET);
660 ms_biff_put_var_write (ewb->bp, magic_self, sizeof magic_self);
661 ms_biff_put_commit (ewb->bp);
664 static void
665 cb_write_sheet_pairs (ExcelSheetPair *sp, gconstpointer dummy, ExcelWriteState *ewb)
667 guint8 data[6];
669 GSF_LE_SET_GUINT16 (data + 0, ewb->supbook_idx);
670 GSF_LE_SET_GUINT16 (data + 2, sp->a->index_in_wb);
671 GSF_LE_SET_GUINT16 (data + 4, sp->b->index_in_wb);
672 ms_biff_put_var_write (ewb->bp, data, 6);
674 sp->idx_a = ewb->tmp_counter++;
677 static void
678 excel_write_externsheets_v8 (ExcelWriteState *ewb)
680 static guint8 const expr_ref [] = { 0x02, 0, 0x1c, 0x17 };
681 static guint8 const zeros [] = { 0, 0, 0, 0, 0 ,0 };
682 static guint8 const magic_addin[] = { 0x01, 0x00, 0x01, 0x3a };
683 static guint8 const magic_self[] = { 0x01, 0x04 };
684 unsigned i;
685 guint8 data[8];
687 /* XL appears to get irrate if we export an addin SUPBOOK with
688 * no names. So be extra tidy and only export it if necessary */
689 if (ewb->externnames->len > 0) {
690 ms_biff_put_var_next (ewb->bp, BIFF_SUPBOOK);
691 ms_biff_put_var_write (ewb->bp, magic_addin, sizeof (magic_addin));
692 ms_biff_put_commit (ewb->bp);
694 for (i = 0; i < ewb->externnames->len ; i++) {
695 const char *func_name;
697 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNNAME_v0); /* yes v0 */
698 ms_biff_put_var_write (ewb->bp, zeros, 6);
700 /* write the name and the 1 byte length */
701 func_name = g_ptr_array_index (ewb->externnames, i);
702 excel_write_string (ewb->bp, STR_ONE_BYTE_LENGTH, func_name);
703 ms_biff_put_var_write (ewb->bp, expr_ref, sizeof (expr_ref));
704 ms_biff_put_commit (ewb->bp);
706 ewb->supbook_idx = 1;
707 } else
708 ewb->supbook_idx = 0;
710 ms_biff_put_var_next (ewb->bp, BIFF_SUPBOOK);
711 GSF_LE_SET_GUINT16 (data, ewb->esheets->len);
712 ms_biff_put_var_write (ewb->bp, data, 2);
713 ms_biff_put_var_write (ewb->bp, magic_self, sizeof (magic_self));
714 ms_biff_put_commit (ewb->bp);
716 /* Now do the EXTERNSHEET */
717 ms_biff_put_var_next (ewb->bp, BIFF_EXTERNSHEET);
718 i = g_hash_table_size (ewb->sheet_pairs);
720 if (ewb->externnames->len > 0) {
721 GSF_LE_SET_GUINT16 (data + 0, i+1); /* the magic self we're about to add */
722 GSF_LE_SET_GUINT16 (data + 2, 0); /* magic self */
723 GSF_LE_SET_GUINT16 (data + 4, 0xfffe);
724 GSF_LE_SET_GUINT16 (data + 6, 0xfffe);
725 ms_biff_put_var_write (ewb->bp, data, 8);
726 ewb->tmp_counter = 1;
727 } else {
728 GSF_LE_SET_GUINT16 (data + 0, i);
729 ms_biff_put_var_write (ewb->bp, data, 2);
730 ewb->tmp_counter = 0;
733 g_hash_table_foreach (ewb->sheet_pairs,
734 (GHFunc) cb_write_sheet_pairs, ewb);
735 ms_biff_put_commit (ewb->bp);
738 static void
739 excel_write_WINDOW1 (BiffPut *bp, WorkbookView const *wb_view)
741 guint8 *data = ms_biff_put_len_next (bp, BIFF_WINDOW1, 18);
742 double hdpi = gnm_app_display_dpi_get (TRUE) / (72. * 20.);
743 double vdpi = gnm_app_display_dpi_get (FALSE) / (72. * 20.);
744 guint16 width = .5 + wb_view->preferred_width / hdpi;
745 guint16 height = .5 + wb_view->preferred_height / vdpi;
746 guint16 options = 0;
747 guint16 active_page = 0;
748 Sheet *sheet;
750 if (wb_view->show_horizontal_scrollbar)
751 options |= 0x0008;
752 if (wb_view->show_vertical_scrollbar)
753 options |= 0x0010;
754 if (wb_view->show_notebook_tabs)
755 options |= 0x0020;
757 sheet = wb_view_cur_sheet (wb_view);
758 if (sheet != NULL)
759 active_page = sheet->index_in_wb;
761 GSF_LE_SET_GUINT16 (data+ 0, 0x0000);
762 GSF_LE_SET_GUINT16 (data+ 2, 0x0000);
763 GSF_LE_SET_GUINT16 (data+ 4, width);
764 GSF_LE_SET_GUINT16 (data+ 6, height);
765 GSF_LE_SET_GUINT16 (data+ 8, options); /* various flags */
766 GSF_LE_SET_GUINT16 (data+ 10, active_page); /* selected tab */
767 /* We don't know the scroll state of the notebook tabs at this level */
768 GSF_LE_SET_GUINT16 (data+ 12, 0x0000);
769 GSF_LE_SET_GUINT16 (data+ 14, 0x0001);
770 GSF_LE_SET_GUINT16 (data+ 16, 0x0258);
771 ms_biff_put_commit (bp);
774 static void
775 excel_write_WINDOW2 (BiffPut *bp, ExcelWriteSheet *esheet, SheetView *sv)
777 /* 1 0x020 grids are the colour of the normal style */
778 /* 1 0x080 display outlines if they exist */
779 /* 0 0x800 (biff8 only) no page break mode*/
780 guint16 options = 0x0A0;
781 guint8 *data;
782 GnmCellPos topLeft, frozen_topLeft;
783 Sheet const *sheet = esheet->gnum_sheet;
784 GnmColor *sheet_auto = sheet_style_get_auto_pattern_color (sheet);
785 GnmColor *default_auto = style_color_auto_pattern ();
786 guint32 biff_pat_col = 0x40; /* default grid color index == auto */
787 int const frozen_height = sv->unfrozen_top_left.row -
788 sv->frozen_top_left.row;
789 int const frozen_width = sv->unfrozen_top_left.col -
790 sv->frozen_top_left.col;
791 guint16 freeze_type; /* NOTE docs lie, this is not 'active pane' */
793 if (frozen_width > 0) {
794 topLeft.col = sv->frozen_top_left.col;
795 frozen_topLeft.col = sv->initial_top_left.col;
796 } else {
797 topLeft.col = sv->initial_top_left.col;
798 frozen_topLeft.col = sv->frozen_top_left.col;
800 if (frozen_height > 0) {
801 topLeft.row = sv->frozen_top_left.row;
802 frozen_topLeft.row = sv->initial_top_left.row;
803 } else {
804 topLeft.row = sv->initial_top_left.row;
805 frozen_topLeft.row = sv->frozen_top_left.row;
808 if (sheet->display_formulas)
809 options |= 0x0001;
810 if (!sheet->hide_grid)
811 options |= 0x0002;
812 if (!sheet->hide_col_header || !sheet->hide_row_header)
813 options |= 0x0004;
814 if (gnm_sheet_view_is_frozen (sv))
815 options |= 0x0108;
816 if (!sheet->hide_zero)
817 options |= 0x0010;
818 if (sheet->text_is_rtl)
819 options |= 0x0040;
820 /* Grid / auto pattern color */
821 if (!style_color_equal (sheet_auto, default_auto)) {
822 biff_pat_col = gnm_color_to_bgr (sheet_auto);
823 if (bp->version > MS_BIFF_V7)
824 biff_pat_col = palette_get_index (&esheet->ewb->base,
825 biff_pat_col);
826 options &= ~0x0020;
828 if (sheet == wb_view_cur_sheet (esheet->ewb->base.wb_view))
829 options |= 0x600; /* Excel ignores this and uses WINDOW1 */
831 if (bp->version <= MS_BIFF_V7) {
832 data = ms_biff_put_len_next (bp, BIFF_WINDOW2_v2, 10);
834 GSF_LE_SET_GUINT16 (data + 0, options);
835 GSF_LE_SET_GUINT16 (data + 2, topLeft.row);
836 GSF_LE_SET_GUINT16 (data + 4, topLeft.col);
837 GSF_LE_SET_GUINT32 (data + 6, biff_pat_col);
838 } else {
839 data = ms_biff_put_len_next (bp, BIFF_WINDOW2_v2, 18);
841 GSF_LE_SET_GUINT16 (data + 0, options);
842 GSF_LE_SET_GUINT16 (data + 2, topLeft.row);
843 GSF_LE_SET_GUINT16 (data + 4, topLeft.col);
844 GSF_LE_SET_GUINT32 (data + 6, biff_pat_col);
845 GSF_LE_SET_GUINT16 (data + 10, 0x1); /* print preview 100% */
846 GSF_LE_SET_GUINT16 (data + 12, 0x0); /* FIXME : why 0? */
847 GSF_LE_SET_GUINT32 (data + 14, 0x0); /* reserved 0 */
849 ms_biff_put_commit (bp);
851 style_color_unref (sheet_auto);
852 style_color_unref (default_auto);
854 if (gnm_sheet_view_is_frozen (sv)) {
855 data = ms_biff_put_len_next (bp, BIFF_PANE, 10);
857 if (sv->unfrozen_top_left.col > 0)
858 freeze_type = (sv->unfrozen_top_left.row > 0) ? 0 : 1;
859 else
860 freeze_type = (sv->unfrozen_top_left.row > 0) ? 2 : 3;
862 GSF_LE_SET_GUINT16 (data + 0, frozen_width);
863 GSF_LE_SET_GUINT16 (data + 2, frozen_height);
864 GSF_LE_SET_GUINT16 (data + 4, frozen_topLeft.row);
865 GSF_LE_SET_GUINT16 (data + 6, frozen_topLeft.col);
866 GSF_LE_SET_GUINT16 (data + 8, freeze_type);
868 ms_biff_put_commit (bp);
873 * No documentation exists for this record, but this makes
874 * sense given the other record formats.
876 static void
877 excel_write_MERGECELLs (BiffPut *bp, ExcelWriteSheet *esheet)
879 guint8 *record, *ptr;
880 GSList *merged;
881 guint16 len;
882 int remainder = 0;
883 int const max_records = (ms_biff_max_record_len (bp) - 2) / 8;
885 /* Find the set of regions that we can safely export */
886 for (merged = esheet->gnum_sheet->list_merged; merged != NULL ; merged = merged->next) {
887 /* TODO : Add a warning entry in the log about ignoring the missing elements */
888 GnmRange const *r = merged->data;
889 if (r->start.row <= USHRT_MAX && r->end.row <= USHRT_MAX &&
890 r->start.col <= UCHAR_MAX && r->end.col <= UCHAR_MAX)
891 remainder++;
894 /* Do not even write the record if there are no merged regions */
895 if (remainder <= 0)
896 return;
898 merged = esheet->gnum_sheet->list_merged;
900 for (; remainder > 0 ; remainder -= max_records) {
901 len = (remainder > max_records) ? max_records : remainder;
903 record = ms_biff_put_len_next (bp, BIFF_MERGECELLS, 2+8*len);
904 GSF_LE_SET_GUINT16 (record, len);
905 ptr = record + 2;
906 for (; merged != NULL && len-- > 0 ; merged = merged->next) {
907 GnmRange const *r = merged->data;
908 if (r->start.row <= USHRT_MAX && r->end.row <= USHRT_MAX &&
909 r->start.col <= UCHAR_MAX && r->end.col <= UCHAR_MAX) {
910 GSF_LE_SET_GUINT16 (ptr+0, r->start.row);
911 GSF_LE_SET_GUINT16 (ptr+2, r->end.row);
912 GSF_LE_SET_GUINT16 (ptr+4, r->start.col);
913 GSF_LE_SET_GUINT16 (ptr+6, r->end.col);
914 ptr += 8;
917 ms_biff_put_commit (bp);
921 /****************************************************************************/
923 static void
924 xl_le_set_range (guint8 *data, GnmRange const *r)
926 GSF_LE_SET_GUINT16 (data+0, r->start.row);
927 GSF_LE_SET_GUINT16 (data+2, r->end.row >= XLS_MaxRow_V8 ? (XLS_MaxRow_V8-1) : r->end.row);
928 GSF_LE_SET_GUINT16 (data+4, r->start.col);
929 GSF_LE_SET_GUINT16 (data+6, r->end.col >= XLS_MaxCol ? XLS_MaxCol-1 : r->end.col);
932 typedef struct {
933 GnmRange bb;
934 GSList *ranges;
935 } CondDetails;
937 static gboolean
938 write_border (ExcelWriteSheet const *esheet,
939 GnmStyle const *s, GnmStyleElement elem,
940 guint32 d[2],
941 unsigned pat_offset, unsigned colour_offset)
943 unsigned c;
944 GnmBorder *b;
946 if (!gnm_style_is_element_set (s, elem) ||
947 NULL == (b = gnm_style_get_border(s, elem)))
948 return TRUE;
950 d[pat_offset / 32] |=
951 (map_border_to_xl (b->line_type, esheet->ewb->bp->version)
952 << (pat_offset & 31));
954 c = map_color_to_palette (&esheet->ewb->base,
955 b->color, PALETTE_AUTO_PATTERN);
956 d[colour_offset / 32] |= (c << (colour_offset & 31));
958 return FALSE;
961 static int
962 map_underline_to_xl (GnmUnderline const ul)
964 switch (ul) {
965 default :
966 case UNDERLINE_NONE : return 0;
967 case UNDERLINE_SINGLE : return 1;
968 case UNDERLINE_DOUBLE : return 2;
969 case UNDERLINE_SINGLE_LOW : return 0x21;
970 case UNDERLINE_DOUBLE_LOW : return 0x22;
974 static int
975 map_style_underline_to_xl (GnmStyle const *style)
977 return map_underline_to_xl (gnm_style_get_font_uline (style));
982 static int
983 map_script_to_xl (GnmStyle const *style)
985 switch (gnm_style_get_font_script (style)) {
986 case GO_FONT_SCRIPT_SUB : return 2;
987 default :
988 case GO_FONT_SCRIPT_STANDARD : return 0;
989 case GO_FONT_SCRIPT_SUPER : return 1;
993 static guint
994 halign_to_excel (GnmHAlign halign)
996 guint ialign;
998 switch (halign) {
999 case GNM_HALIGN_GENERAL:
1000 ialign = MS_BIFF_H_A_GENERAL;
1001 break;
1002 case GNM_HALIGN_LEFT:
1003 ialign = MS_BIFF_H_A_LEFT;
1004 break;
1005 case GNM_HALIGN_RIGHT:
1006 ialign = MS_BIFF_H_A_RIGHT;
1007 break;
1008 case GNM_HALIGN_CENTER:
1009 ialign = MS_BIFF_H_A_CENTER;
1010 break;
1011 case GNM_HALIGN_FILL:
1012 ialign = MS_BIFF_H_A_FILL;
1013 break;
1014 case GNM_HALIGN_JUSTIFY:
1015 ialign = MS_BIFF_H_A_JUSTIFTY;
1016 break;
1017 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
1018 ialign = MS_BIFF_H_A_CENTER_ACROSS_SELECTION;
1019 break;
1020 case GNM_HALIGN_DISTRIBUTED:
1021 ialign = MS_BIFF_H_A_DISTRIBUTED;
1022 break;
1023 default:
1024 ialign = MS_BIFF_H_A_GENERAL;
1027 return ialign;
1030 static guint
1031 valign_to_excel (GnmVAlign valign)
1033 guint ialign;
1035 switch (valign) {
1036 case GNM_VALIGN_TOP:
1037 ialign = MS_BIFF_V_A_TOP;
1038 break;
1039 case GNM_VALIGN_BOTTOM:
1040 ialign = MS_BIFF_V_A_BOTTOM;
1041 break;
1042 case GNM_VALIGN_CENTER:
1043 ialign = MS_BIFF_V_A_CENTER;
1044 break;
1045 case GNM_VALIGN_JUSTIFY:
1046 ialign = MS_BIFF_V_A_JUSTIFY;
1047 break;
1048 case GNM_VALIGN_DISTRIBUTED:
1049 ialign = MS_BIFF_V_A_DISTRIBUTED;
1050 break;
1051 default:
1052 ialign = MS_BIFF_V_A_TOP;
1055 return ialign;
1058 static guint
1059 rotation_to_excel_v7 (int rotation)
1061 if (rotation < 0)
1062 return 1;
1063 if (rotation == 0)
1064 return 0;
1065 if (rotation <= 45)
1066 return 0;
1067 if (rotation <= 135)
1068 return 2;
1069 if (rotation <= 225)
1070 return 0;
1071 if (rotation <= 315)
1072 return 3;
1073 return 0;
1075 static guint
1076 rotation_to_excel_v8 (int rotation)
1078 if (rotation < 0)
1079 return 0xff;
1080 rotation = rotation % 360;
1081 if (rotation > 90)
1082 return 360 + 90 - rotation;
1083 return rotation;
1086 static void
1087 cb_write_condition (GnmStyleConditions const *sc, CondDetails *cd,
1088 ExcelWriteSheet *esheet)
1090 GSList *ptr;
1091 BiffPut *bp = esheet->ewb->bp;
1092 guint16 range_count;
1093 guint8 buf[14], type, op;
1094 unsigned i, expr0_len, expr1_len, header_pos;
1095 GPtrArray const *details = gnm_style_conditions_details (sc);
1096 unsigned det_len = details ? details->len : 0;
1098 /* The parent record */
1099 ms_biff_put_var_next (bp, BIFF_CONDFMT);
1100 GSF_LE_SET_GUINT16 (buf+0, det_len);
1101 GSF_LE_SET_GUINT16 (buf+2, 1); /* force a redraw */
1102 xl_le_set_range (buf+4, &cd->bb);
1103 range_count = g_slist_length (cd->ranges);
1104 GSF_LE_SET_GUINT16 (buf+12, range_count);
1105 ms_biff_put_var_write (bp, buf, 14);
1106 for (ptr = cd->ranges; ptr != NULL ; ptr = ptr->next) {
1107 xl_le_set_range (buf, ptr->data);
1108 ms_biff_put_var_write (bp, buf, 8);
1110 ms_biff_put_commit (bp);
1111 g_slist_free (cd->ranges);
1113 /* The individual conditions */
1114 for (i = 0 ; i < det_len ; i++) {
1115 GnmStyleCond const *cond = g_ptr_array_index (details, i);
1116 GnmStyle const *s = cond->overlay;
1117 GnmExprTop const *alt_texpr;
1118 guint32 flags = 0x00300380; /* these are always true */
1119 guint16 flags2 = 0x02; /* these are always true */
1121 ms_biff_put_var_next (bp, BIFF_CF);
1122 header_pos = bp->curpos;
1123 ms_biff_put_var_seekto (bp, header_pos+12);
1125 if (gnm_style_is_element_set (s, MSTYLE_FORMAT)) {
1126 GOFormat const *fmt = gnm_style_get_format (s);
1127 const char *xlfmt = go_format_as_XL (fmt);
1128 guint16 bytes;
1129 unsigned afterpos, lenpos = bp->curpos;
1131 /* Write as DXFNumUsr structure. */
1132 ms_biff_put_var_seekto (bp, lenpos + 2);
1133 bytes = excel_write_string
1134 (bp, STR_TWO_BYTE_LENGTH, xlfmt);
1136 afterpos = bp->curpos;
1137 ms_biff_put_var_seekto (bp, lenpos);
1138 GSF_LE_SET_GUINT16 (buf, bytes + 2);
1139 ms_biff_put_var_write (bp, buf, 2);
1140 ms_biff_put_var_seekto (bp, afterpos);
1142 flags |= 0x02000000; /* has dxfnum record */
1143 flags2 |= 0x1;
1144 } else
1145 flags |= 0x00080000; /* ignore number format */
1147 if (gnm_style_is_element_set (s, MSTYLE_FONT_COLOR) ||
1148 gnm_style_is_element_set (s, MSTYLE_FONT_NAME) ||
1149 gnm_style_is_element_set (s, MSTYLE_FONT_BOLD) ||
1150 gnm_style_is_element_set (s, MSTYLE_FONT_ITALIC) ||
1151 gnm_style_is_element_set (s, MSTYLE_FONT_UNDERLINE ) ||
1152 gnm_style_is_element_set (s, MSTYLE_FONT_STRIKETHROUGH) ||
1153 gnm_style_is_element_set (s, MSTYLE_FONT_SCRIPT) ||
1154 gnm_style_is_element_set (s, MSTYLE_FONT_SIZE)) {
1155 guint8 fbuf[118];
1156 guint32 tmp, font_flags = 0x18;
1157 guint32 written = 0;
1159 memset (fbuf, 0, sizeof (fbuf));
1161 if (gnm_style_is_element_set (s, MSTYLE_FONT_NAME)) {
1162 char *font = g_strdup (gnm_style_get_font_name (s));
1163 size_t bytes;
1164 guint charlen = excel_strlen (font, &bytes);
1165 guint maxlen = (bytes == charlen) ? 62 : 30;
1167 if (charlen > maxlen)
1168 g_utf8_offset_to_pointer (font, maxlen)[0] = 0;
1170 written = excel_write_string
1171 (bp, STR_ONE_BYTE_LENGTH, font);
1172 g_free (font);
1173 GSF_LE_SET_GUINT16 (fbuf+116, 1);
1176 if (gnm_style_is_element_set (s, MSTYLE_FONT_SIZE))
1177 tmp = (int) (gnm_style_get_font_size (s) * 20. + .5);
1178 else
1179 tmp = 0xFFFFFFFF;
1180 GSF_LE_SET_GUINT32 (fbuf+64, tmp);
1182 if (gnm_style_is_element_set (s, MSTYLE_FONT_BOLD)) {
1183 guint16 weight = gnm_style_get_font_bold (s) ? 0x2bc : 0x190;
1184 GSF_LE_SET_GUINT16 (fbuf+72, weight);
1185 } else
1186 GSF_LE_SET_GUINT32 (fbuf+100, 1);
1188 tmp = 0;
1189 if (gnm_style_is_element_set (s, MSTYLE_FONT_ITALIC)) {
1190 if (gnm_style_get_font_italic (s))
1191 tmp |= 2;
1192 else
1193 tmp |= 0;
1194 } else
1195 font_flags |= 2;
1197 if (gnm_style_is_element_set (s, MSTYLE_FONT_STRIKETHROUGH)) {
1198 if (gnm_style_get_font_strike (s))
1199 tmp |= 0x80;
1200 } else
1201 font_flags |= 0x80;
1202 GSF_LE_SET_GUINT32 (fbuf+68, tmp);
1204 if (gnm_style_is_element_set (s, MSTYLE_FONT_UNDERLINE)) {
1205 tmp = map_style_underline_to_xl (s);
1206 GSF_LE_SET_GUINT32 (fbuf+76, tmp);
1207 } else
1208 GSF_LE_SET_GUINT32 (fbuf+96, 1); /* flag as unused */
1210 if (gnm_style_is_element_set (s, MSTYLE_FONT_SCRIPT)) {
1211 guint16 script = map_script_to_xl (s);
1212 GSF_LE_SET_GUINT16 (fbuf+74, script);
1213 } else
1214 GSF_LE_SET_GUINT32 (fbuf+92, 1); /* flag as unused */
1216 GSF_LE_SET_GUINT32 (fbuf+104, 1); /*always 1 */
1218 if (gnm_style_is_element_set (s, MSTYLE_FONT_COLOR))
1219 tmp = map_color_to_palette (&esheet->ewb->base,
1220 gnm_style_get_font_color (s), PALETTE_AUTO_FONT);
1221 else
1222 tmp = 0xFFFFFFFF;
1223 GSF_LE_SET_GUINT32 (fbuf+80, tmp);
1225 GSF_LE_SET_GUINT8 (fbuf + 88, font_flags);
1227 ms_biff_put_var_write (bp,
1228 fbuf + written,
1229 sizeof (fbuf) - written);
1230 flags |= 0x04000000;
1233 if (gnm_style_is_element_set (s, MSTYLE_ALIGN_H) ||
1234 gnm_style_is_element_set (s, MSTYLE_ALIGN_V) ||
1235 gnm_style_is_element_set (s, MSTYLE_WRAP_TEXT) ||
1236 gnm_style_is_element_set (s, MSTYLE_ROTATION) ||
1237 /* what is just-last? */
1238 gnm_style_is_element_set (s, MSTYLE_INDENT) ||
1239 gnm_style_is_element_set (s, MSTYLE_SHRINK_TO_FIT) ||
1240 gnm_style_is_element_set (s, MSTYLE_TEXT_DIR)) {
1241 guint16 d1 = 0;
1242 guint16 d2 = 0;
1243 guint16 d3 = 0;
1245 if (gnm_style_is_element_set (s, MSTYLE_ALIGN_H))
1246 d1 |= (halign_to_excel (gnm_style_get_align_h (s)) << 0);
1247 else
1248 flags |= 1;
1250 if (gnm_style_is_element_set (s, MSTYLE_ALIGN_V))
1251 d1 |= (valign_to_excel (gnm_style_get_align_v (s)) << 4);
1252 else
1253 flags |= 2;
1255 if (gnm_style_is_element_set (s, MSTYLE_WRAP_TEXT))
1256 d1 |= ((gnm_style_get_wrap_text (s) ? 1 : 0) << 3);
1257 else
1258 flags |= 4;
1260 if (gnm_style_is_element_set (s, MSTYLE_ROTATION))
1261 d1 |= (bp->version >= MS_BIFF_V8
1262 ? rotation_to_excel_v8 (gnm_style_get_rotation (s))
1263 : rotation_to_excel_v7 (gnm_style_get_rotation (s))) << 8;
1264 else
1265 flags |= 8;
1267 flags |= 0x10; /* just-last? */
1269 if (gnm_style_is_element_set (s, MSTYLE_INDENT)) {
1270 d2 |= ((gnm_style_get_indent (s) & 0xf) << 0);
1271 d3 = 0xffff;
1272 } else
1273 flags |= 0x20;
1275 if (gnm_style_is_element_set (s, MSTYLE_SHRINK_TO_FIT))
1276 d2 |= ((gnm_style_get_shrink_to_fit (s) ? 1 : 0) << 4);
1277 else
1278 flags |= 0x40;
1280 if (gnm_style_is_element_set (s, MSTYLE_TEXT_DIR)) {
1281 switch (gnm_style_get_text_dir (s)) {
1282 case GNM_TEXT_DIR_RTL: d2 |= (2 << 6); break;
1283 case GNM_TEXT_DIR_LTR: d2 |= (1 << 6); break;
1284 case GNM_TEXT_DIR_CONTEXT: d2 |= (0 << 6); break;
1285 default: break;
1287 } else
1288 flags |= 0x80000000;
1290 flags |= 0x08000000;
1292 GSF_LE_SET_GUINT16 (buf+0, d1);
1293 GSF_LE_SET_GUINT16 (buf+2, d2);
1294 GSF_LE_SET_GUINT16 (buf+6, d3);
1295 GSF_LE_SET_GUINT16 (buf+6, 0);
1296 ms_biff_put_var_write (bp, buf, 8);
1299 if (gnm_style_is_element_set (s, MSTYLE_BORDER_LEFT) ||
1300 gnm_style_is_element_set (s, MSTYLE_BORDER_RIGHT) ||
1301 gnm_style_is_element_set (s, MSTYLE_BORDER_TOP) ||
1302 gnm_style_is_element_set (s, MSTYLE_BORDER_BOTTOM) ||
1303 gnm_style_is_element_set (s, MSTYLE_BORDER_DIAGONAL) ||
1304 gnm_style_is_element_set (s, MSTYLE_BORDER_REV_DIAGONAL)) {
1305 guint32 d[2] = { 0, 0 };
1306 if (write_border (esheet, s, MSTYLE_BORDER_LEFT, d, 0, 16))
1307 flags |= 0x0400;
1308 if (write_border (esheet, s, MSTYLE_BORDER_RIGHT, d, 4, 23))
1309 flags |= 0x0800;
1310 if (write_border (esheet, s, MSTYLE_BORDER_TOP, d, 8, 32))
1311 flags |= 0x1000;
1312 if (write_border (esheet, s, MSTYLE_BORDER_BOTTOM, d, 12, 39))
1313 flags |= 0x2000;
1316 * MS-XLS defines fields for diagonals, but Excel does not seem to
1317 * read them, not does the GUI allows creating them.
1319 if (write_border (esheet, s, MSTYLE_BORDER_DIAGONAL, d, 53, 46))
1320 flags |= 0x4000;
1321 else
1322 d[0] |= 0x80000000;
1323 if (write_border (esheet, s, MSTYLE_BORDER_REV_DIAGONAL, d, 53, 46))
1324 flags |= 0x8000;
1325 else
1326 d[0] |= 0x40000000;
1328 GSF_LE_SET_GUINT32 (buf+0, d[0]);
1329 GSF_LE_SET_GUINT32 (buf+4, d[1]);
1330 ms_biff_put_var_write (bp, buf, 8);
1331 flags |= 0x10000000;
1332 } else
1333 flags |= 0xFC00;
1335 if (gnm_style_is_element_set (s, MSTYLE_PATTERN) ||
1336 gnm_style_is_element_set (s, MSTYLE_COLOR_BACK) ||
1337 gnm_style_is_element_set (s, MSTYLE_COLOR_PATTERN)) {
1338 guint32 background = 0;
1340 /* Gnumeric requires pattern = 1, whereas XL can store
1341 * just the color. undo the mapping we made on import. */
1342 if (gnm_style_is_element_set (s, MSTYLE_PATTERN))
1343 background = map_pattern_to_xl (
1344 gnm_style_get_pattern (s)) << 10;
1345 else
1346 flags |= 0x10000;
1347 if (gnm_style_is_element_set (s, MSTYLE_COLOR_PATTERN)) {
1348 unsigned idx = palette_get_index (&esheet->ewb->base,
1349 gnm_color_to_bgr (
1350 gnm_style_get_pattern_color (s)));
1351 background |= idx << 16;
1352 } else
1353 flags |= 0x20000;
1354 if (gnm_style_is_element_set (s, MSTYLE_COLOR_BACK)) {
1355 unsigned idx = palette_get_index (&esheet->ewb->base,
1356 gnm_color_to_bgr (
1357 gnm_style_get_back_color (s)));
1358 background |= idx << 23;
1359 } else
1360 flags |= 0x40000;
1362 GSF_LE_SET_GUINT32 (buf, background);
1363 ms_biff_put_var_write (bp, buf, 4);
1364 flags |= 0x20000000;
1365 } else
1366 flags |= 0x70000;
1368 switch (cond->op) {
1369 case GNM_STYLE_COND_CONTAINS_STR:
1370 case GNM_STYLE_COND_NOT_CONTAINS_STR:
1371 case GNM_STYLE_COND_BEGINS_WITH_STR:
1372 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
1373 case GNM_STYLE_COND_ENDS_WITH_STR:
1374 case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
1375 case GNM_STYLE_COND_CONTAINS_ERR:
1376 case GNM_STYLE_COND_NOT_CONTAINS_ERR:
1377 case GNM_STYLE_COND_CONTAINS_BLANKS:
1378 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS:
1379 alt_texpr = gnm_style_cond_get_alternate_expr (cond);
1380 break;
1381 default:
1382 alt_texpr = NULL;
1385 expr0_len = (alt_texpr || gnm_style_cond_get_expr (cond, 0))
1386 ? excel_write_formula (esheet->ewb,
1387 alt_texpr
1388 ? alt_texpr
1389 : gnm_style_cond_get_expr (cond, 0),
1390 esheet->gnum_sheet, 0, 0,
1391 EXCEL_CALLED_FROM_CONDITION)
1392 : 0;
1393 expr1_len = gnm_style_cond_get_expr (cond, 1)
1394 ? excel_write_formula (esheet->ewb,
1395 gnm_style_cond_get_expr (cond, 1),
1396 esheet->gnum_sheet, 0, 0,
1397 EXCEL_CALLED_FROM_CONDITION)
1398 : 0;
1400 type = 1;
1401 switch (cond->op) {
1402 case GNM_STYLE_COND_BETWEEN: op = 0x01; break;
1403 case GNM_STYLE_COND_NOT_BETWEEN:op = 0x02; break;
1404 case GNM_STYLE_COND_EQUAL: op = 0x03; break;
1405 case GNM_STYLE_COND_NOT_EQUAL: op = 0x04; break;
1406 case GNM_STYLE_COND_GT: op = 0x05; break;
1407 case GNM_STYLE_COND_LT: op = 0x06; break;
1408 case GNM_STYLE_COND_GTE: op = 0x07; break;
1409 case GNM_STYLE_COND_LTE: op = 0x08; break;
1411 default:
1412 if (alt_texpr)
1413 gnm_expr_top_unref (alt_texpr);
1414 else
1415 g_warning ("unknown condition %d", cond->op);
1416 case GNM_STYLE_COND_CUSTOM:
1417 op = 0; type = 2; break;
1420 ms_biff_put_var_seekto (bp, header_pos);
1421 GSF_LE_SET_GUINT8 (buf+0, type);
1422 GSF_LE_SET_GUINT8 (buf+1, op);
1423 GSF_LE_SET_GUINT16 (buf+2, expr0_len);
1424 GSF_LE_SET_GUINT16 (buf+4, expr1_len);
1425 GSF_LE_SET_GUINT32 (buf+6, flags);
1426 GSF_LE_SET_GUINT16 (buf+10, flags2);
1427 ms_biff_put_var_write (bp, buf, 12);
1429 ms_biff_put_commit (bp);
1433 static void
1434 excel_write_conditions (BiffPut *bp, ExcelWriteSheet *esheet)
1436 GnmStyleRegion const *sr;
1437 GnmStyleConditions *sc;
1438 GnmStyleList *ptr;
1439 CondDetails *cd;
1440 GHashTable *group;
1442 ptr = esheet->conditions;
1443 if (ptr == NULL)
1444 return;
1446 group = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
1447 for (; ptr != NULL ; ptr = ptr->next) {
1448 sr = ptr->data;
1449 /* Clip here to avoid creating a CF record if there are no regions */
1450 if (sr->range.start.col >= esheet->max_col ||
1451 sr->range.start.row >= esheet->max_row)
1452 continue;
1453 sc = gnm_style_get_conditions (sr->style);
1454 cd = g_hash_table_lookup (group, sc);
1455 if (cd == NULL) {
1456 cd = g_new (CondDetails, 1);
1457 cd->ranges = NULL;
1458 cd->bb = sr->range;
1459 g_hash_table_insert (group, sc, cd);
1460 } else
1461 cd->bb = range_union (&cd->bb, &sr->range);
1462 cd->ranges = g_slist_prepend (cd->ranges, (gpointer)&sr->range);
1465 g_hash_table_foreach (group, (GHFunc) cb_write_condition, esheet);
1466 g_hash_table_destroy (group);
1469 static void
1470 excel_write_DV (XLValInputPair const *vip, gpointer dummy, ExcelWriteSheet *esheet)
1472 GSList *ptr;
1473 BiffPut *bp = esheet->ewb->bp;
1474 guint16 range_count;
1475 guint32 options;
1476 guint8 data[8];
1477 int col, row;
1478 GnmRange const *r;
1479 ExcelFuncContext context = EXCEL_CALLED_FROM_VALIDATION;
1481 ms_biff_put_var_next (bp, BIFF_DV);
1483 options = 0;
1484 if (vip->v != NULL) {
1485 switch (vip->v->type) {
1486 case GNM_VALIDATION_TYPE_ANY: options = 0; break;
1487 case GNM_VALIDATION_TYPE_AS_INT: options = 1; break;
1488 case GNM_VALIDATION_TYPE_AS_NUMBER: options = 2; break;
1489 case GNM_VALIDATION_TYPE_IN_LIST: options = 3; context = EXCEL_CALLED_FROM_VALIDATION_LIST; break;
1490 case GNM_VALIDATION_TYPE_AS_DATE: options = 4; break;
1491 case GNM_VALIDATION_TYPE_AS_TIME: options = 5; break;
1492 case GNM_VALIDATION_TYPE_TEXT_LENGTH: options = 6; break;
1493 case GNM_VALIDATION_TYPE_CUSTOM: options = 7; break;
1494 default : g_warning ("EXCEL : Unknown constraint type %d",
1495 vip->v->type);
1498 switch (vip->v->style) {
1499 case GNM_VALIDATION_STYLE_NONE: break;
1500 case GNM_VALIDATION_STYLE_STOP: options |= (0 << 4); break;
1501 case GNM_VALIDATION_STYLE_WARNING: options |= (1 << 4); break;
1502 case GNM_VALIDATION_STYLE_INFO: options |= (2 << 4); break;
1503 default : g_warning ("EXCEL : Unknown validation style %d",
1504 vip->v->style);
1507 switch (vip->v->op) {
1508 case GNM_VALIDATION_OP_NONE:
1509 case GNM_VALIDATION_OP_BETWEEN: options |= (0 << 20); break;
1510 case GNM_VALIDATION_OP_NOT_BETWEEN: options |= (1 << 20); break;
1511 case GNM_VALIDATION_OP_EQUAL: options |= (2 << 20); break;
1512 case GNM_VALIDATION_OP_NOT_EQUAL: options |= (3 << 20); break;
1513 case GNM_VALIDATION_OP_GT: options |= (4 << 20); break;
1514 case GNM_VALIDATION_OP_LT: options |= (5 << 20); break;
1515 case GNM_VALIDATION_OP_GTE: options |= (6 << 20); break;
1516 case GNM_VALIDATION_OP_LTE: options |= (7 << 20); break;
1517 default : g_warning ("EXCEL : Unknown constraint operator %d",
1518 vip->v->op);
1520 if (vip->v->allow_blank)
1521 options |= 0x100;
1522 /* XL suppresses the dropdown rather than vice versa */
1523 if (!vip->v->use_dropdown)
1524 options |= 0x200;
1525 if (vip->v->style != GNM_VALIDATION_STYLE_NONE)
1526 options |= 0x80000;
1529 if (vip->msg != NULL)
1530 options |= 0x40000;
1532 GSF_LE_SET_GUINT32 (data, options);
1533 ms_biff_put_var_write (bp, data, 4);
1535 excel_write_string (bp, STR_TWO_BYTE_LENGTH,
1536 vip->msg ? gnm_input_msg_get_title (vip->msg) : "");
1537 excel_write_string (bp, STR_TWO_BYTE_LENGTH,
1538 (vip->v && vip->v->title) ? vip->v->title->str : "");
1539 excel_write_string (bp, STR_TWO_BYTE_LENGTH,
1540 vip->msg ? gnm_input_msg_get_msg (vip->msg) : "");
1541 excel_write_string (bp, STR_TWO_BYTE_LENGTH,
1542 (vip->v && vip->v->msg) ? vip->v->msg->str : "");
1544 /* Things seem to parse relative to the top left of the first range */
1545 r = vip->ranges->data;
1546 col = r->start.col;
1547 row = r->start.row;
1549 GSF_LE_SET_GUINT16 (data , 0); /* bogus len fill in later */
1550 GSF_LE_SET_GUINT16 (data+2, 0); /* Undocumented, use 0 for now */
1551 ms_biff_put_var_write (bp, data, 4);
1553 if (vip->v != NULL && vip->v->deps[0].texpr != NULL) {
1554 unsigned pos = bp->curpos;
1555 guint16 len = excel_write_formula (esheet->ewb,
1556 vip->v->deps[0].texpr,
1557 esheet->gnum_sheet, col, row,
1558 context);
1559 unsigned end_pos = bp->curpos;
1560 ms_biff_put_var_seekto (bp, pos-4);
1561 GSF_LE_SET_GUINT16 (data, len);
1562 ms_biff_put_var_write (bp, data, 2);
1563 ms_biff_put_var_seekto (bp, end_pos);
1566 GSF_LE_SET_GUINT16 (data , 0); /* bogus len fill in later */
1567 GSF_LE_SET_GUINT16 (data+2, 0); /* Undocumented, use 0 for now */
1568 ms_biff_put_var_write (bp, data, 4);
1569 if (vip->v != NULL && vip->v->deps[1].texpr != NULL) {
1570 unsigned pos = bp->curpos;
1571 guint16 len = excel_write_formula (esheet->ewb,
1572 vip->v->deps[1].texpr,
1573 esheet->gnum_sheet, col, row,
1574 context);
1575 unsigned end_pos = bp->curpos;
1576 ms_biff_put_var_seekto (bp, pos-4);
1577 GSF_LE_SET_GUINT16 (data, len);
1578 ms_biff_put_var_write (bp, data, 2);
1579 ms_biff_put_var_seekto (bp, end_pos);
1582 range_count = g_slist_length (vip->ranges);
1583 GSF_LE_SET_GUINT16 (data, range_count);
1584 ms_biff_put_var_write (bp, data, 2);
1585 for (ptr = vip->ranges; ptr != NULL ; ptr = ptr->next) {
1586 xl_le_set_range (data, ptr->data);
1587 ms_biff_put_var_write (bp, data, 8);
1589 ms_biff_put_commit (bp);
1592 static void
1593 excel_write_HLINKs (BiffPut *bp, ExcelWriteSheet *esheet)
1595 static guint8 const stdlink_guid[] = {
1596 0xd0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
1597 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
1598 /* unknown */
1599 0x02, 0x00, 0x00, 0x00
1601 GnmStyleList *ptr;
1602 GnmStyleRegion const *sr;
1603 GnmHLink *hlink = NULL;
1604 char const *target;
1605 char const *tip;
1606 guint8 data [16];
1607 GType t;
1609 for (ptr = esheet->hlinks ; ptr != NULL ; ptr = ptr->next) {
1610 sr = ptr->data;
1611 /* Clip here to avoid creating a CF record if there are no regions */
1612 if (sr->range.start.col >= esheet->max_col ||
1613 sr->range.start.row >= esheet->max_row)
1614 continue;
1615 if (NULL == (hlink = gnm_style_get_hlink (sr->style)))
1616 continue; /* should not happen */
1617 ms_biff_put_var_next (bp, BIFF_HLINK);
1618 GSF_LE_SET_GUINT16 (data + 0, sr->range.start.row);
1619 GSF_LE_SET_GUINT16 (data + 2, sr->range.end.row);
1620 GSF_LE_SET_GUINT16 (data + 4, sr->range.start.col);
1621 GSF_LE_SET_GUINT16 (data + 6, sr->range.end.col);
1622 ms_biff_put_var_write (bp, data, 8);
1623 ms_biff_put_var_write (bp, stdlink_guid, sizeof (stdlink_guid));
1625 target = gnm_hlink_get_target (hlink);
1626 t = G_OBJECT_TYPE (hlink);
1627 if (t == gnm_hlink_cur_wb_get_type ()) {
1628 /* -label, -target_base, +mark && type = 0 */
1629 GSF_LE_SET_GUINT16 (data, 0x8);
1630 ms_biff_put_var_write (bp, data, 4);
1631 excel_write_string (bp,
1632 STR_FOUR_BYTE_LENGTH | STR_SUPPRESS_HEADER | STR_TRAILING_NULL,
1633 target);
1634 } else if (t == gnm_hlink_url_get_type () ||
1635 t == gnm_hlink_email_get_type ()) {
1636 static guint8 const magic[] = {
1637 /* -label, -target_base, -mark && type = 3 */
1638 0x3, 0, 0, 0,
1640 /* guid */
1641 0xe0, 0xc9, 0xea, 0x79, 0xf9, 0xba, 0xce, 0x11,
1642 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b,
1644 ms_biff_put_var_write (bp, magic, sizeof magic);
1645 excel_write_string (bp,
1646 STR_FOUR_BYTE_LENGTH | STR_SUPPRESS_HEADER | STR_TRAILING_NULL | STR_LEN_IN_BYTES,
1647 target);
1648 } else if (t == gnm_hlink_external_get_type ()) {
1649 if (NULL != target &&
1650 0 == strncmp (target, "\\\\", 2)) {
1651 GSF_LE_SET_GUINT32 (data, 0x117);
1652 ms_biff_put_var_write (bp, data, 4);
1653 excel_write_string (bp,
1654 STR_FOUR_BYTE_LENGTH | STR_SUPPRESS_HEADER | STR_TRAILING_NULL,
1655 target);
1656 /* Yes twice */
1657 excel_write_string (bp,
1658 STR_FOUR_BYTE_LENGTH | STR_SUPPRESS_HEADER | STR_TRAILING_NULL,
1659 target);
1660 } else {
1661 static guint8 const magic[] = {
1662 /* -label, -target_base, -mark && type = 3 */
1663 0x3, 0, 0, 0,
1665 /* guid */
1666 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1667 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
1669 /* No leading ../ to path */
1670 0, 0,
1672 /* Store an empty short path incl trailing null */
1673 1, 0, 0, 0,
1674 0, /* the empty path */
1676 /* unknown magic */
1677 0xff, 0xff, 0xad, 0xde, 0, 0, 0, 0,
1678 0, 0, 0, 0, 0, 0, 0, 0,
1679 0, 0, 0, 0, 0, 0, 0, 0
1681 unsigned int len;
1682 ms_biff_put_var_write (bp, magic, sizeof magic);
1684 len = g_utf8_strlen (target, -1);
1685 GSF_LE_SET_GUINT32 (data+0, len*2 + 6);
1686 GSF_LE_SET_GUINT32 (data+4, len*2);
1687 GSF_LE_SET_GUINT16 (data+8, 3);
1688 ms_biff_put_var_write (bp, data, 10);
1689 excel_write_string (bp,
1690 STR_NO_LENGTH | STR_SUPPRESS_HEADER,
1691 target);
1695 ms_biff_put_commit (bp);
1697 if (NULL != (tip = gnm_hlink_get_tip (hlink))) {
1698 ms_biff_put_var_next (bp, BIFF_LINK_TIP);
1699 GSF_LE_SET_GUINT16 (data + 0, 0x800);
1700 GSF_LE_SET_GUINT16 (data + 2, sr->range.start.row);
1701 GSF_LE_SET_GUINT16 (data + 4, sr->range.end.row);
1702 GSF_LE_SET_GUINT16 (data + 6, sr->range.start.col);
1703 GSF_LE_SET_GUINT16 (data + 8, sr->range.end.col);
1704 ms_biff_put_var_write (bp, data, 10);
1706 excel_write_string (bp,
1707 STR_NO_LENGTH | STR_SUPPRESS_HEADER | STR_TRAILING_NULL,
1708 tip);
1709 ms_biff_put_commit (bp);
1714 static void
1715 excel_write_DVALs (BiffPut *bp, ExcelWriteSheet *esheet)
1717 GnmStyleList *ptr;
1718 GHashTable *group;
1719 guint8 *data;
1720 unsigned i;
1722 if (NULL == (ptr = esheet->validations))
1723 return;
1725 group = xls_collect_validations (ptr,
1726 esheet->max_col, esheet->max_row);
1728 i = g_hash_table_size (group);
1729 data = ms_biff_put_len_next (bp, BIFF_DVAL, 18);
1730 GSF_LE_SET_GUINT16 (data + 0, 0); /* !cached, for mystery 2 bytes ? */
1731 GSF_LE_SET_GUINT32 (data + 2, 0); /* input X coord */
1732 GSF_LE_SET_GUINT32 (data + 6, 0); /* input Y coord */
1733 /* lie and say there is no drop down, this will require the user to
1734 * move off the cell and back on to see it I think */
1735 GSF_LE_SET_GUINT32 (data + 10, 0xffffffff);
1736 GSF_LE_SET_GUINT32 (data + 14, i); /* how many validations */
1737 ms_biff_put_commit (bp);
1739 g_hash_table_foreach (group, (GHFunc) excel_write_DV, esheet);
1740 g_hash_table_destroy (group);
1743 static void
1744 excel_write_SHEETPROTECTION (BiffPut *bp, Sheet *sheet)
1746 static guint8 const data[] = {
1747 0x67, 0x08,
1748 0, 0, 0, 0, 0, 0, 0, 0,
1749 0, 0, 2, 0,
1751 0xff, 0xff, 0xff, 0xff
1753 guint16 flags;
1754 guint8 *buf =ms_biff_put_len_next (bp, BIFF_SHEETPROTECTION, sizeof(data) + 4);
1756 flags = 0;
1757 if (sheet->protected_allow.edit_objects) flags |= (1 << 0);
1758 if (sheet->protected_allow.edit_scenarios) flags |= (1 << 1);
1759 if (sheet->protected_allow.cell_formatting) flags |= (1 << 2);
1760 if (sheet->protected_allow.column_formatting) flags |= (1 << 3);
1761 if (sheet->protected_allow.row_formatting) flags |= (1 << 4);
1762 if (sheet->protected_allow.insert_columns) flags |= (1 << 5);
1763 if (sheet->protected_allow.insert_rows) flags |= (1 << 6);
1764 if (sheet->protected_allow.insert_hyperlinks) flags |= (1 << 7);
1765 if (sheet->protected_allow.delete_columns) flags |= (1 << 8);
1766 if (sheet->protected_allow.delete_rows) flags |= (1 << 9);
1767 if (sheet->protected_allow.select_locked_cells) flags |= (1 << 10);
1768 if (sheet->protected_allow.sort_ranges) flags |= (1 << 11);
1769 if (sheet->protected_allow.edit_auto_filters) flags |= (1 << 12);
1770 if (sheet->protected_allow.edit_pivottable) flags |= (1 << 13);
1771 if (sheet->protected_allow.select_unlocked_cells) flags |= (1 << 14);
1773 memcpy (buf, data, sizeof data);
1774 GSF_LE_SET_GUINT16 (buf + sizeof (data) + 0, flags);
1775 GSF_LE_SET_GUINT16 (buf + sizeof (data) + 2, 0);
1777 ms_biff_put_commit (bp);
1780 /******************************************************************/
1782 /* Look for sheet references in conditional formats */
1783 static void
1784 excel_write_prep_conditions (ExcelWriteSheet *esheet)
1786 GnmStyleList *ptr = esheet->conditions;
1787 GnmStyleRegion const *sr;
1788 GPtrArray const *conds;
1789 unsigned i;
1791 for (; ptr != NULL ; ptr = ptr->next) {
1792 sr = ptr->data;
1793 if (!gnm_style_is_element_set (sr->style, MSTYLE_CONDITIONS) ||
1794 NULL == gnm_style_get_conditions (sr->style))
1795 continue;
1796 conds = gnm_style_conditions_details (
1797 gnm_style_get_conditions (sr->style));
1798 for (i = 0 ; i < (conds ? conds->len : 0) ; i++) {
1799 GnmStyleCond const *cond = g_ptr_array_index (conds, i);
1801 switch (cond->op) {
1802 case GNM_STYLE_COND_CONTAINS_STR:
1803 case GNM_STYLE_COND_NOT_CONTAINS_STR:
1804 case GNM_STYLE_COND_BEGINS_WITH_STR:
1805 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
1806 case GNM_STYLE_COND_ENDS_WITH_STR:
1807 case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
1808 case GNM_STYLE_COND_CONTAINS_ERR:
1809 case GNM_STYLE_COND_NOT_CONTAINS_ERR:
1810 case GNM_STYLE_COND_CONTAINS_BLANKS:
1811 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS: {
1812 GnmExprTop const *alt_texpr =
1813 gnm_style_cond_get_alternate_expr (cond);
1814 if (alt_texpr) {
1815 excel_write_prep_expr (esheet->ewb, alt_texpr);
1816 gnm_expr_top_unref (alt_texpr);
1818 break;
1820 default:
1821 ; /* Nothing */
1824 if (gnm_style_cond_get_expr (cond, 0) != NULL)
1825 excel_write_prep_expr (esheet->ewb, gnm_style_cond_get_expr (cond, 0));
1826 if (gnm_style_cond_get_expr (cond, 1) != NULL)
1827 excel_write_prep_expr (esheet->ewb, gnm_style_cond_get_expr (cond, 1));
1832 /* Look for sheet references in validation expressions */
1833 static void
1834 excel_write_prep_validations (ExcelWriteSheet *esheet)
1836 GnmStyleList *ptr = esheet->validations;
1838 for (; ptr != NULL ; ptr = ptr->next) {
1839 GnmStyleRegion const *sr = ptr->data;
1840 GnmValidation const *v = gnm_style_get_validation (sr->style);
1841 if (!v)
1842 continue;
1843 if (v->deps[0].texpr != NULL)
1844 excel_write_prep_expr (esheet->ewb, v->deps[0].texpr);
1845 if (v->deps[1].texpr != NULL)
1846 excel_write_prep_expr (esheet->ewb, v->deps[1].texpr);
1850 static int
1851 excel_write_builtin_name (char const *ptr, MsBiffVersion version)
1853 static char const *builtins [] = {
1854 "Consolidate_Area", "Auto_Open",
1855 "Auto_Close", "Extract",
1856 "Database", "Criteria",
1857 "Print_Area", "Print_Titles",
1858 "Recorder", "Data_Form",
1859 "Auto_Activate", "Auto_Deactivate",
1860 "Sheet_Title", "_FilterDatabase"
1862 int i = G_N_ELEMENTS (builtins);
1864 /* _FilterDatabase does not seem to be in 95 */
1865 if (version < MS_BIFF_V8)
1866 i--;
1867 while (i-- > 0)
1868 if (!strcmp (builtins[i], ptr))
1869 return i;
1870 return -1;
1873 static void
1874 excel_write_NAME (G_GNUC_UNUSED gpointer key,
1875 GnmNamedExpr *nexpr, ExcelWriteState *ewb)
1877 guint8 data [16];
1878 guint16 expr_len, flags = 0;
1879 size_t name_len;
1880 char const *name;
1881 int builtin_index;
1883 g_return_if_fail (nexpr != NULL);
1885 ms_biff_put_var_next (ewb->bp, BIFF_NAME_v0); /* yes v0 */
1886 memset (data, 0, sizeof data);
1888 name = expr_name_name (nexpr);
1889 if (nexpr->pos.sheet != NULL) { /* sheet local */
1890 GSF_LE_SET_GUINT16 (data + 8,
1891 nexpr->pos.sheet->index_in_wb + 1);
1892 GSF_LE_SET_GUINT16 (data + 6,
1893 nexpr->pos.sheet->index_in_wb + 1);
1896 builtin_index = excel_write_builtin_name (name, ewb->bp->version);
1897 if (nexpr->is_hidden)
1898 flags |= 0x01;
1899 if (builtin_index >= 0)
1900 flags |= 0x20;
1901 GSF_LE_SET_GUINT16 (data + 0, flags);
1902 if (builtin_index >= 0) {
1903 GSF_LE_SET_GUINT8 (data + 3, 1); /* name_len */
1904 if (ewb->bp->version >= MS_BIFF_V8) {
1905 GSF_LE_SET_GUINT8 (data + 15, builtin_index);
1906 ms_biff_put_var_write (ewb->bp, data, 16);
1907 } else {
1908 GSF_LE_SET_GUINT8 (data + 14, builtin_index);
1909 ms_biff_put_var_write (ewb->bp, data, 15);
1911 } else {
1912 name_len = excel_strlen (name, NULL);
1913 GSF_LE_SET_GUINT8 (data + 3, name_len); /* name_len */
1914 ms_biff_put_var_write (ewb->bp, data, 14);
1915 excel_write_string (ewb->bp, STR_NO_LENGTH, name);
1918 if (!nexpr->texpr || expr_name_is_placeholder (nexpr))
1919 expr_len = 0;
1920 else
1921 expr_len = excel_write_formula (ewb, nexpr->texpr,
1922 nexpr->pos.sheet, 0, 0,
1923 EXCEL_CALLED_FROM_NAME);
1924 ms_biff_put_var_seekto (ewb->bp, 4);
1925 GSF_LE_SET_GUINT16 (data, expr_len);
1926 ms_biff_put_var_write (ewb->bp, data, 2);
1927 ms_biff_put_commit (ewb->bp);
1931 excel_write_get_externsheet_idx (ExcelWriteState *ewb,
1932 Sheet *sheeta,
1933 Sheet *sheetb)
1935 ExcelSheetPair key, *sp;
1936 key.a = sheeta;
1937 key.b = sheetb ? sheetb : sheeta;
1939 sp = g_hash_table_lookup (ewb->sheet_pairs, &key);
1941 g_return_val_if_fail (sp != NULL, 0);
1943 return sp->idx_a;
1946 /* Returns stream position of start **/
1947 static guint32
1948 excel_write_BOUNDSHEET (BiffPut *bp, Sheet *sheet)
1950 guint32 pos;
1951 guint8 data[16];
1953 ms_biff_put_var_next (bp, BIFF_BOUNDSHEET);
1954 pos = bp->streamPos;
1956 GSF_LE_SET_GUINT32 (data, 0xdeadbeef); /* To be stream start pos */
1958 /* NOTE : MS Docs appear wrong. It is visibility _then_ type */
1959 GSF_LE_SET_GUINT8 (data+4, sheet->visibility);
1961 switch (sheet->sheet_type) {
1962 default:
1963 g_warning ("unknown sheet type %d (assuming WorkSheet)", sheet->sheet_type);
1964 break;
1965 case GNM_SHEET_DATA : GSF_LE_SET_GUINT8 (data+5, 0); break;
1966 case GNM_SHEET_OBJECT : GSF_LE_SET_GUINT8 (data+5, 2); break;
1967 case GNM_SHEET_XLM : GSF_LE_SET_GUINT8 (data+5, 1); break;
1968 /* case MS_BIFF_TYPE_VBModule : GSF_LE_SET_GUINT8 (data+5, 6); break; */
1970 ms_biff_put_var_write (bp, data, 6);
1971 excel_write_string (bp, STR_ONE_BYTE_LENGTH, sheet->name_unquoted);
1972 ms_biff_put_commit (bp);
1973 return pos;
1977 * Update a previously written record with the correct
1978 * stream position.
1980 static void
1981 excel_fix_BOUNDSHEET (GsfOutput *output, guint32 pos, unsigned streamPos)
1983 guint8 data[4];
1984 gsf_off_t oldpos;
1985 g_return_if_fail (output);
1987 oldpos = gsf_output_tell (output);
1988 gsf_output_seek (output, pos+4, G_SEEK_SET);
1989 GSF_LE_SET_GUINT32 (data, streamPos);
1990 gsf_output_write (output, 4, data);
1991 gsf_output_seek (output, oldpos, G_SEEK_SET);
1994 /***************************************************************************/
1997 * Convert ExcelPaletteEntry to guint representation used in BIFF file
1999 inline static guint
2000 palette_color_to_int (ExcelPaletteEntry const *c)
2002 return (c->b << 16) + (c->g << 8) + (c->r << 0);
2006 static void
2007 log_put_color (guint c, gboolean was_added, gint index, char const *tmpl)
2009 d(2, if (was_added) g_printerr (tmpl, index, c););
2012 static void
2013 palette_init (XLExportBase *ewb)
2015 int i;
2016 ExcelPaletteEntry const *epe;
2017 guint num;
2019 ewb->pal.two_way_table =
2020 two_way_table_new (g_direct_hash, g_direct_equal,
2021 0, NULL);
2022 /* Ensure that black and white can't be swapped out */
2024 for (i = 0; i < EXCEL_DEF_PAL_LEN; i++) {
2025 /* Just use biff8 palette. We're going to dump a custom
2026 * palette anyway so it does not really matter */
2027 epe = &excel_default_palette_v8 [i];
2028 num = palette_color_to_int (epe);
2029 two_way_table_put (ewb->pal.two_way_table,
2030 GUINT_TO_POINTER (num), FALSE,
2031 (AfterPutFunc) log_put_color,
2032 "Default color %d - 0x%06.6x\n");
2033 if ((i == PALETTE_BLACK) || (i == PALETTE_WHITE))
2034 ewb->pal.entry_in_use[i] = TRUE;
2035 else
2036 ewb->pal.entry_in_use[i] = FALSE;
2040 static void
2041 palette_free (XLExportBase *ewb)
2043 if (ewb->pal.two_way_table != NULL) {
2044 two_way_table_free (ewb->pal.two_way_table);
2045 ewb->pal.two_way_table = NULL;
2050 * palette_get_index
2051 * @ewb: #XLExportBase
2052 * @c: color
2054 * Get index of color
2055 * The color index to use is *not* simply the index into the palette.
2056 * See comment to excel_palette_get in ms-excel-read.c
2058 gint
2059 palette_get_index (XLExportBase const *ewb, guint c)
2061 gint idx;
2063 if (c == 0)
2064 return PALETTE_BLACK;
2065 if (c == 0xffffff)
2066 return PALETTE_WHITE;
2068 idx = two_way_table_key_to_idx (ewb->pal.two_way_table, GUINT_TO_POINTER (c));
2069 if (idx < 0) {
2070 g_warning ("Unknown color (#%06x), converting it to black\n", c);
2071 return PALETTE_BLACK;
2074 if (idx >= EXCEL_DEF_PAL_LEN) {
2075 g_warning ("We lost colour #%d (#%06x), converting it to black\n", idx, c);
2076 return PALETTE_BLACK;
2078 return idx + 8;
2081 static void
2082 put_color_bgr (XLExportBase *ewb, guint32 bgr)
2084 TwoWayTable *twt = ewb->pal.two_way_table;
2085 gpointer pc = GUINT_TO_POINTER (bgr);
2086 gint idx = two_way_table_put (twt, pc, TRUE,
2087 (AfterPutFunc) log_put_color,
2088 "Found unique color %d - 0x%06.6x\n");
2089 if (idx >= 0 && idx < EXCEL_DEF_PAL_LEN)
2090 ewb->pal.entry_in_use [idx] = TRUE; /* Default entry in use */
2093 static void
2094 put_color_gnm (XLExportBase *ewb, GnmColor const *c)
2096 put_color_bgr (ewb, gnm_color_to_bgr (c));
2099 static void
2100 put_color_go_color (XLExportBase *ewb, GOColor c)
2102 put_color_bgr (ewb, go_color_to_bgr (c));
2106 * Add colors in mstyle to palette
2108 static void
2109 put_colors (ExcelStyleVariant const *esv, gpointer dummy, XLExportBase *ewb)
2111 unsigned i, j;
2112 GnmBorder const *b;
2113 GnmStyle const *st = esv->style;
2115 put_color_gnm (ewb, gnm_style_get_font_color (st));
2116 put_color_gnm (ewb, gnm_style_get_back_color (st));
2117 put_color_gnm (ewb, gnm_style_get_pattern_color (st));
2119 /* Borders */
2120 for (i = STYLE_TOP; i < STYLE_ORIENT_MAX; i++) {
2121 GnmBorder const *b = gnm_style_get_border (st, MSTYLE_BORDER_TOP + i);
2122 if (b && b->color)
2123 put_color_gnm (ewb, b->color);
2125 if (gnm_style_is_element_set (st, MSTYLE_CONDITIONS) &&
2126 NULL != gnm_style_get_conditions (st)) {
2127 GPtrArray const *conds = gnm_style_conditions_details (
2128 gnm_style_get_conditions (st));
2129 for (i = 0 ; i < (conds ? conds->len : 0) ; i++) {
2130 GnmStyleCond const *cond = g_ptr_array_index (conds, i);
2131 st = cond->overlay;
2132 if (gnm_style_is_element_set (st, MSTYLE_FONT_COLOR))
2133 put_color_gnm (ewb, gnm_style_get_font_color (st));
2134 if (gnm_style_is_element_set (st, MSTYLE_COLOR_BACK))
2135 put_color_gnm (ewb, gnm_style_get_back_color (st));
2136 if (gnm_style_is_element_set (st, MSTYLE_COLOR_PATTERN))
2137 put_color_gnm (ewb, gnm_style_get_pattern_color (st));
2139 /* Borders */
2140 for (j = STYLE_TOP; j < STYLE_ORIENT_MAX; j++)
2141 if (gnm_style_is_element_set (st, MSTYLE_BORDER_TOP + i) &&
2142 NULL != (b = gnm_style_get_border (st, MSTYLE_BORDER_TOP + i)) &&
2143 NULL != b->color)
2144 put_color_gnm (ewb, b->color);
2150 * gather_palette
2151 * @ewb workbook
2153 * Add all colors in workbook to palette.
2155 * The palette is apparently limited to EXCEL_DEF_PAL_LEN. We start
2156 * with the default palette in the table. Next, we traverse all colors
2157 * in all styles. When we find a default color, we note that it is in
2158 * use. When we find a custom colors, we add it to the table. This
2159 * yields an oversize palette.
2160 * Finally, we knock out unused entries in the default palette, to make
2161 * room for the custom colors. We don't touch 0 or 1 (white/black)
2163 * FIXME:
2164 * We are not able to recognize that we are actually using a color in the
2165 * default palette. The causes seem to be elsewhere in gnumeric and gnome.
2167 static void
2168 gather_palette (XLExportBase *xle)
2170 TwoWayTable *twt = xle->xf.two_way_table;
2171 int i, j;
2172 int upper_limit = EXCEL_DEF_PAL_LEN;
2173 guint color;
2175 /* For each color in each style, get color index from hash. If
2176 none, it is not there yet, and we enter it. */
2177 g_hash_table_foreach (twt->unique_keys,
2178 (GHFunc) put_colors, xle);
2180 twt = xle->pal.two_way_table;
2181 for (i = twt->idx_to_key->len - 1; i >= EXCEL_DEF_PAL_LEN; i--) {
2182 color = GPOINTER_TO_UINT (two_way_table_idx_to_key (twt, i));
2183 for (j = upper_limit - 1; j > 1; j--) {
2184 if (!xle->pal.entry_in_use[j]) {
2185 /* Replace table entry with color. */
2186 d (2, g_printerr ("Custom color %d (0x%x)"
2187 " moved to unused index %d\n",
2188 i, color, j););
2189 two_way_table_move (twt, j, i);
2190 upper_limit = j;
2191 xle->pal.entry_in_use[j] = TRUE;
2192 break;
2196 if (j <= 1) {
2197 g_warning ("uh oh, we're going to lose a colour");
2203 * write_palette
2204 * @bp BIFF buffer
2205 * @ewb workbook
2207 * Write palette to file
2209 static void
2210 write_palette (BiffPut *bp, ExcelWriteState *ewb)
2212 TwoWayTable *twt = ewb->base.pal.two_way_table;
2213 guint8 data[8];
2214 guint num, i;
2216 ms_biff_put_var_next (bp, BIFF_PALETTE);
2218 GSF_LE_SET_GUINT16 (data, EXCEL_DEF_PAL_LEN); /* Entries */
2220 ms_biff_put_var_write (bp, data, 2);
2221 for (i = 0; i < EXCEL_DEF_PAL_LEN; i++) {
2222 num = GPOINTER_TO_UINT (two_way_table_idx_to_key (twt, i));
2223 GSF_LE_SET_GUINT32 (data, num);
2224 ms_biff_put_var_write (bp, data, 4);
2227 ms_biff_put_commit (bp);
2230 /***************************************************************************/
2232 #ifndef NO_DEBUG_EXCEL
2234 * Return string description of font to print in debug log
2236 static char *
2237 excel_font_to_string (ExcelWriteFont const *f)
2239 static char buf[96];
2240 guint nused;
2242 nused = g_snprintf (buf, sizeof buf, "%s, %g", f->font_name, f->size_pts);
2244 if (nused < sizeof buf && f->is_bold)
2245 nused += snprintf (buf + nused, sizeof buf - nused, ", %s",
2246 "bold");
2247 if (nused < sizeof buf && f->is_italic)
2248 nused += snprintf (buf + nused, sizeof buf - nused, ", %s",
2249 "italic");
2250 if (nused < sizeof buf) {
2251 if ((GnmUnderline) f->underline == UNDERLINE_SINGLE)
2252 nused += snprintf (buf + nused, sizeof buf - nused,
2253 ", %s", "single underline");
2254 else if ((GnmUnderline) f->underline == UNDERLINE_DOUBLE)
2255 nused += snprintf (buf + nused, sizeof buf - nused,
2256 ", %s", "double underline");
2257 else if ((GnmUnderline) f->underline == UNDERLINE_SINGLE_LOW)
2258 nused += snprintf (buf + nused, sizeof buf - nused,
2259 ", %s", "single low underline");
2260 else if ((GnmUnderline) f->underline == UNDERLINE_DOUBLE_LOW)
2261 nused += snprintf (buf + nused, sizeof buf - nused,
2262 ", %s", "double low underline");
2264 if (nused < sizeof buf && f->strikethrough)
2265 nused += snprintf (buf + nused, sizeof buf - nused, ", %s",
2266 "strikethrough");
2268 return buf;
2270 #endif
2272 static void
2273 excel_font_free (ExcelWriteFont *efont)
2275 /* FONT_SKIP has value == NULL */
2276 if (efont != NULL) {
2277 d (3, g_printerr ("freeing %s\n", excel_font_to_string (efont)););
2278 g_free (efont->font_name_copy);
2279 g_free (efont);
2283 static ExcelWriteFont *
2284 excel_font_new (GnmStyle const *base_style)
2286 ExcelWriteFont *efont;
2287 GnmColor *c;
2289 if (base_style == NULL)
2290 return NULL;
2292 efont = g_new (ExcelWriteFont, 1);
2293 efont->font_name = gnm_style_get_font_name (base_style);
2294 efont->font_name_copy = NULL;
2295 efont->size_pts = gnm_style_get_font_size (base_style);
2296 efont->is_bold = gnm_style_get_font_bold (base_style);
2297 efont->is_italic = gnm_style_get_font_italic (base_style);
2298 efont->underline = gnm_style_get_font_uline (base_style);
2299 efont->strikethrough = gnm_style_get_font_strike (base_style);
2300 efont->script = map_script_to_xl (base_style);
2302 c = gnm_style_get_font_color (base_style);
2303 efont->color = gnm_color_to_bgr (c);
2304 efont->is_auto = c->is_auto;
2306 return efont;
2309 static void
2310 excel_font_overlay_pango (ExcelWriteFont *efont, GSList *pango)
2312 GSList *ptr;
2314 for (ptr = pango ; ptr != NULL ; ptr = ptr->next) {
2315 PangoAttribute *attr = ptr->data;
2316 PangoColor const *c;
2318 switch (attr->klass->type) {
2319 case PANGO_ATTR_FAMILY :
2320 g_free (efont->font_name_copy);
2321 efont->font_name = efont->font_name_copy =
2322 g_strdup (((PangoAttrString *)attr)->value);
2323 break;
2324 case PANGO_ATTR_SIZE : efont->size_pts =
2325 (double )(((PangoAttrInt *)attr)->value) / PANGO_SCALE;
2326 break;
2327 case PANGO_ATTR_STYLE : efont->is_italic =
2328 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC;
2329 break;
2330 case PANGO_ATTR_WEIGHT : efont->is_bold =
2331 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD;
2332 break;
2333 case PANGO_ATTR_STRIKETHROUGH : efont->strikethrough =
2334 ((PangoAttrInt *)attr)->value != 0;
2335 break;
2336 case PANGO_ATTR_SCALE :
2337 break; /* ignored */
2338 case PANGO_ATTR_RISE :
2339 if (((PangoAttrInt *)attr)->value < 0)
2340 efont->script = 2;
2341 else if (((PangoAttrInt *)attr)->value > 0)
2342 efont->script = 1;
2343 else
2344 efont->script = 0;
2345 break;
2347 case PANGO_ATTR_UNDERLINE :
2348 efont->underline = gnm_translate_underline_from_pango
2349 (((PangoAttrInt *)attr)->value);
2350 break;
2352 case PANGO_ATTR_FOREGROUND :
2353 c = &((PangoAttrColor *)attr)->color;
2354 efont->is_auto = FALSE;
2355 efont->color = ((c->blue & 0xff00) << 8) + (c->green & 0xff00) + (c->red >> 8);
2356 break;
2357 default :
2358 if (attr->klass->type ==
2359 go_pango_attr_subscript_get_attr_type ())
2360 efont->script = ((GOPangoAttrSubscript *)attr)->val
2361 ? 2 : 0;
2362 else if (attr->klass->type ==
2363 go_pango_attr_superscript_get_attr_type ())
2364 efont->script = ((GOPangoAttrSuperscript *)attr)->val
2365 ? 1 : 0;
2366 break; /* ignored */
2369 pango_attribute_destroy (attr);
2371 g_slist_free (pango);
2374 static guint
2375 excel_font_hash (gconstpointer f)
2377 guint res = 0;
2378 ExcelWriteFont *font = (ExcelWriteFont *) f;
2380 if (f)
2381 res = (guint)(font->size_pts + g_str_hash (font->font_name))
2382 ^ font->color
2383 ^ font->is_auto
2384 ^ (font->underline << 1)
2385 ^ (font->strikethrough << 2)
2386 ^ (font->script << 3);
2388 return res;
2390 static gint
2391 excel_font_equal (gconstpointer a, gconstpointer b)
2393 gint res;
2395 if (a == b)
2396 res = TRUE;
2397 else if (!a || !b)
2398 res = FALSE; /* Recognize junk - inelegant, I know! */
2399 else {
2400 ExcelWriteFont const *fa = (ExcelWriteFont const *) a;
2401 ExcelWriteFont const *fb = (ExcelWriteFont const *) b;
2402 res = !strcmp (fa->font_name, fb->font_name)
2403 && (fa->size_pts == fb->size_pts)
2404 && (fa->is_bold == fb->is_bold)
2405 && (fa->is_italic == fb->is_italic)
2406 && (fa->color == fb->color)
2407 && (fa->is_auto == fb->is_auto)
2408 && (fa->underline == fb->underline)
2409 && (fa->strikethrough == fb->strikethrough)
2410 && (fa->script == fb->script);
2413 return res;
2416 static ExcelWriteFont *
2417 fonts_get_font (XLExportBase *ewb, gint idx)
2419 return two_way_table_idx_to_key (ewb->fonts.two_way_table, idx);
2422 static void
2423 after_put_font (ExcelWriteFont *f, gboolean was_added, gint index, gconstpointer dummy)
2425 if (was_added) {
2426 d (1, g_printerr ("Found unique font %d - %s\n",
2427 index, excel_font_to_string (f)););
2428 } else
2429 excel_font_free (f);
2433 * put_efont :
2434 * @efont: #ExcelWriteFont
2435 * @xle: #XLExportBase
2437 * Absorbs ownership of @efont potentially freeing it.
2439 * Returns the index of the font
2441 static inline gint
2442 put_efont (ExcelWriteFont *efont, XLExportBase *xle)
2444 TwoWayTable *twt = xle->fonts.two_way_table;
2446 d (2, g_printerr ("adding %s\n", excel_font_to_string (efont)););
2448 /* Occupy index FONT_SKIP with junk - Excel skips it */
2449 if (twt->idx_to_key->len == FONT_SKIP)
2450 two_way_table_put (twt, NULL, FALSE, NULL, NULL);
2452 return two_way_table_put (twt, efont, TRUE, (AfterPutFunc) after_put_font, NULL);
2456 excel_font_from_go_font (XLExportBase *ewb, GOFont const *font)
2458 ExcelWriteFont *efont;
2460 efont = g_new (ExcelWriteFont, 1);
2461 efont->font_name = pango_font_description_get_family (font->desc);
2462 if (!efont->font_name) {
2463 /* Shouldn't happen, but don't crash. 575318*/
2464 efont->font_name = "Sans";
2467 efont->font_name_copy = NULL;
2468 efont->size_pts = (double) pango_font_description_get_size (font->desc) / PANGO_SCALE;
2469 efont->is_bold = pango_font_description_get_weight (font->desc) > PANGO_WEIGHT_NORMAL;
2470 efont->is_italic = pango_font_description_get_style (font->desc) != PANGO_STYLE_NORMAL;
2471 /* FIXME: implement when supported */
2472 efont->underline = UNDERLINE_NONE;
2473 efont->strikethrough = FALSE;
2474 efont->script = 0;
2475 efont->color = go_color_to_bgr (GO_COLOR_BLACK);
2476 efont->is_auto = FALSE;
2478 return put_efont (efont, ewb);
2481 static void
2482 put_style_font (ExcelStyleVariant const *esv, gconstpointer dummy, XLExportBase *xle)
2484 put_efont (excel_font_new (esv->style), xle);
2487 static void
2488 excel_write_FONT (ExcelWriteState *ewb, ExcelWriteFont const *f)
2490 guint8 data[64];
2491 guint32 size_pts = f->size_pts * 20;
2492 guint16 grbit = 0;
2493 guint16 color;
2495 guint16 boldstyle = 0x190; /* Normal boldness */
2497 /* 0: Normal, 1; Super, 2: Sub script*/
2498 guint16 subsuper = f->script;
2500 guint8 underline = (guint8) map_underline_to_xl (f->underline);
2502 guint8 family = 0;
2503 guint8 charset = 0; /* Seems OK. */
2504 char const *font_name = f->font_name;
2506 color = f->is_auto
2507 ? PALETTE_AUTO_FONT
2508 : palette_get_index (&ewb->base, f->color);
2509 d (1, g_printerr ("Writing font %s, color idx %u\n",
2510 excel_font_to_string (f), color););
2512 if (f->is_bold) {
2513 boldstyle = 0x2bc;
2514 grbit |= 1 << 0; /* undocumented */
2516 if (f->is_italic)
2517 grbit |= 1 << 1;
2518 if (f->strikethrough)
2519 grbit |= 1 << 3;
2521 ms_biff_put_var_next (ewb->bp, BIFF_FONT_v0); /* yes v0 */
2522 GSF_LE_SET_GUINT16 (data + 0, size_pts);
2523 GSF_LE_SET_GUINT16 (data + 2, grbit);
2524 GSF_LE_SET_GUINT16 (data + 4, color);
2525 GSF_LE_SET_GUINT16 (data + 6, boldstyle);
2526 GSF_LE_SET_GUINT16 (data + 8, subsuper);
2527 GSF_LE_SET_GUINT8 (data + 10, underline);
2528 GSF_LE_SET_GUINT8 (data + 11, family);
2529 GSF_LE_SET_GUINT8 (data + 12, charset);
2530 GSF_LE_SET_GUINT8 (data + 13, 0);
2531 ms_biff_put_var_write (ewb->bp, data, 14);
2532 excel_write_string (ewb->bp, STR_ONE_BYTE_LENGTH, font_name);
2533 ms_biff_put_commit (ewb->bp);
2537 * excel_write_FONTs
2538 * @bp BIFF buffer
2539 * @ewb workbook
2541 * Write all fonts to file
2543 static void
2544 excel_write_FONTs (BiffPut *bp, ExcelWriteState *ewb)
2546 TwoWayTable *twt = ewb->base.fonts.two_way_table;
2547 int nfonts = twt->idx_to_key->len;
2548 int i;
2549 ExcelWriteFont *f;
2551 for (i = 0; i < nfonts; i++) {
2552 if (i != FONT_SKIP) { /* FONT_SKIP is invalid, skip it */
2553 f = fonts_get_font (&ewb->base, i);
2554 excel_write_FONT (ewb, f);
2558 if (nfonts < FONTS_MINIMUM + 1) { /* Add 1 to account for skip */
2559 /* Fill up until we've got the minimum number */
2560 f = fonts_get_font (&ewb->base, 0);
2561 for (; i < FONTS_MINIMUM + 1; i++) {
2562 if (i != FONT_SKIP) {
2563 /* FONT_SKIP is invalid, skip it */
2564 excel_write_FONT (ewb, f);
2570 /***************************************************************************/
2573 * after_put_format
2574 * @format format
2575 * @was_added true if format was added
2576 * @index index of format
2577 * @tmpl g_printerr template
2579 * Callback called when putting format to table. Print to debug log when
2580 * format is added. Free resources when it was already there.
2582 static void
2583 after_put_format (GOFormat *format, gboolean was_added, gint index,
2584 char const *tmpl)
2586 if (was_added) {
2587 d (2, g_printerr (tmpl, index, format););
2588 } else {
2589 go_format_unref (format);
2593 static void
2594 formats_init (XLExportBase *ewb)
2596 int i;
2597 char const *fmt;
2599 ewb->formats.two_way_table
2600 = two_way_table_new (g_direct_hash, g_direct_equal, 0,
2601 (GDestroyNotify)go_format_unref);
2603 /* Add built-in formats to format table */
2604 for (i = 0; i < EXCEL_BUILTIN_FORMAT_LEN; i++) {
2605 fmt = excel_builtin_formats[i];
2606 if (!fmt || strlen (fmt) == 0)
2607 fmt = "General";
2608 two_way_table_put (ewb->formats.two_way_table,
2609 go_format_new_from_XL (fmt),
2610 FALSE, /* Not unique */
2611 (AfterPutFunc) after_put_format,
2612 "Magic format %d - 0x%x\n");
2616 static void
2617 formats_free (XLExportBase *ewb)
2619 if (ewb->formats.two_way_table != NULL) {
2620 two_way_table_free (ewb->formats.two_way_table);
2621 ewb->formats.two_way_table = NULL;
2625 static GOFormat const *
2626 formats_get_format (XLExportBase *ewb, gint idx)
2628 return two_way_table_idx_to_key (ewb->formats.two_way_table, idx);
2631 static gint
2632 formats_get_index (XLExportBase *xle, GOFormat const *format)
2634 return two_way_table_key_to_idx (xle->formats.two_way_table, format);
2637 static void
2638 put_format (ExcelStyleVariant const *esv, gpointer dummy, XLExportBase *xle)
2640 two_way_table_put (xle->formats.two_way_table,
2641 go_format_ref (gnm_style_get_format (esv->style)), TRUE,
2642 (AfterPutFunc) after_put_format,
2643 "Found unique format %d - 0x%x\n");
2646 static void
2647 set_ifmt (G_GNUC_UNUSED GOFormat *format, G_GNUC_UNUSED gboolean was_added,
2648 gint index, int *tmpl)
2650 *tmpl = index;
2654 excel_write_add_object_format (ExcelWriteState *ewb, GOFormat *format)
2656 int ifmt;
2657 two_way_table_put (ewb->base.formats.two_way_table,
2658 /* cast the int* to char* until the API accepts void* there */
2659 format, TRUE, (AfterPutFunc) set_ifmt, (char*) &ifmt);
2660 return ifmt;
2663 static void
2664 excel_write_FORMAT (ExcelWriteState *ewb, int fidx)
2666 guint8 data[64];
2667 GOFormat const *sf = formats_get_format (&ewb->base, fidx);
2669 const char *format = go_format_as_XL (sf);
2671 d (1, g_printerr ("Writing format 0x%x: %s\n", fidx, format););
2673 if (ewb->bp->version >= MS_BIFF_V7)
2674 ms_biff_put_var_next (ewb->bp, BIFF_FORMAT_v4);
2675 else
2676 ms_biff_put_var_next (ewb->bp, BIFF_FORMAT_v0);
2678 GSF_LE_SET_GUINT16 (data, fidx);
2679 ms_biff_put_var_write (ewb->bp, data, 2);
2680 excel_write_string (ewb->bp, (ewb->bp->version >= MS_BIFF_V8)
2681 ? STR_TWO_BYTE_LENGTH : STR_ONE_BYTE_LENGTH, format);
2682 ms_biff_put_commit (ewb->bp);
2686 * excel_write_FORMATs
2687 * @bp BIFF buffer
2688 * @ewb workbook
2690 * Write all formats to file.
2691 * Although we do, the formats apparently don't have to be written out in order
2693 static void
2694 excel_write_FORMATs (ExcelWriteState *ewb)
2696 TwoWayTable *twt = ewb->base.formats.two_way_table;
2697 guint nformats = twt->idx_to_key->len;
2698 int magic_num [] = { 5, 6, 7, 8, 0x2a, 0x29, 0x2c, 0x2b };
2699 guint i;
2701 /* The built-in formats which get localized */
2702 for (i = 0; i < G_N_ELEMENTS (magic_num); i++)
2703 excel_write_FORMAT (ewb, magic_num [i]);
2705 /* The custom formats */
2706 for (i = EXCEL_BUILTIN_FORMAT_LEN; i < nformats; i++)
2707 excel_write_FORMAT (ewb, i);
2710 static void
2711 after_put_esv (ExcelStyleVariant *esv, gboolean was_added, gint index,
2712 gpointer user)
2714 if (was_added) {
2716 } else {
2717 g_free (esv);
2722 * Initialize XF/GnmStyle table.
2724 * The table records MStyles. For each GnmStyle, an XF record will be
2725 * written to file.
2727 static void
2728 xf_init (XLExportBase *xle)
2730 ExcelStyleVariant *esv;
2732 /* Excel starts at XF_RESERVED for user defined xf */
2733 xle->xf.two_way_table = two_way_table_new
2734 ((GHashFunc)excel_style_variant_hash,
2735 (GCompareFunc)excel_style_variant_equal,
2736 XF_RESERVED, g_free);
2738 /* We store the default style for the workbook on xls import, use it if
2739 * it's available. While we have a default style per sheet, we don't
2740 * have one for the workbook.
2742 * NOTE : This is extremely important to get right. Columns use
2743 * the font from the default style (which becomes XF 0) for sizing */
2744 xle->xf.default_style = g_object_get_data (G_OBJECT (xle->wb),
2745 "xls-default-style");
2746 if (xle->xf.default_style == NULL)
2747 xle->xf.default_style = gnm_style_new_default ();
2748 else {
2749 gnm_style_ref (xle->xf.default_style);
2752 xle->xf.value_fmt_styles = g_hash_table_new_full (
2753 g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)gnm_style_unlink);
2754 /* Register default style, its font and format */
2755 esv = g_new (ExcelStyleVariant, 1);
2756 esv->style = xle->xf.default_style;
2757 esv->variant = 0;
2758 two_way_table_put (xle->xf.two_way_table, esv, TRUE,
2759 (AfterPutFunc)after_put_esv, NULL);
2760 put_style_font (esv, NULL, xle);
2761 put_format (esv, NULL, xle);
2763 xle->xf.cell_style_variant = g_hash_table_new (g_direct_hash, g_direct_equal);
2766 static void
2767 xf_free (XLExportBase *ewb)
2769 if (ewb->xf.two_way_table != NULL) {
2770 two_way_table_free (ewb->xf.two_way_table);
2771 ewb->xf.two_way_table = NULL;
2772 gnm_style_unref (ewb->xf.default_style);
2773 ewb->xf.default_style = NULL;
2774 g_hash_table_destroy (ewb->xf.value_fmt_styles);
2776 g_hash_table_destroy (ewb->xf.cell_style_variant);
2780 static const ExcelStyleVariant *
2781 xf_get_mstyle (XLExportBase *xle, gint idx)
2783 return two_way_table_idx_to_key (xle->xf.two_way_table, idx);
2786 static GArray *
2787 txomarkup_new (ExcelWriteState *ewb,
2788 const char *str,
2789 const PangoAttrList *markup,
2790 GnmStyle const *style)
2792 PangoAttrIterator *iter =
2793 pango_attr_list_get_iterator ((PangoAttrList*)markup);
2794 GArray *txo = g_array_sized_new (FALSE, FALSE, sizeof (int), 8);
2795 gboolean noattrs = TRUE;
2796 gint slen = strlen (str);
2798 do {
2799 gint start, end;
2800 GSList *attrs;
2802 pango_attr_iterator_range (iter, &start, &end);
2803 if (start >= end) {
2804 /* Pango bug #166700. */
2805 /* Carry previous noattrs over. */
2806 break;
2808 if (start >= slen)
2809 break;
2811 attrs = pango_attr_iterator_get_attrs (iter);
2812 noattrs = (attrs == NULL);
2814 if (txo->len == 0 && noattrs) {
2815 /* trim start */
2816 } else {
2817 ExcelWriteFont *efont = excel_font_new (style);
2818 gint tmp[2];
2820 excel_font_overlay_pango (efont, attrs);
2821 tmp[0] = start; /* in bytes! */
2822 tmp[1] = put_efont (efont, &ewb->base);
2823 g_array_append_vals (txo, (gpointer)tmp, 2);
2825 } while (pango_attr_iterator_next (iter));
2826 /* trim end */
2827 if (txo->len > 2 && noattrs &&
2828 g_array_index (txo, gint, txo->len - 2) >= slen)
2829 g_array_set_size (txo, txo->len - 2);
2830 pango_attr_iterator_destroy (iter);
2832 return txo;
2835 /***************************************************************************/
2837 static void
2838 cb_cell_pre_pass (GnmCell const *cell, ExcelWriteState *ewb)
2840 GnmStyle const *style;
2841 GOFormat const *fmt;
2842 gboolean use_sst;
2844 if (gnm_cell_has_expr (cell) || cell->value == NULL)
2845 return;
2847 use_sst = VALUE_IS_STRING (cell->value) && ewb->sst.strings != NULL;
2848 style = gnm_cell_get_style (cell);
2850 if ((fmt = VALUE_FMT (cell->value)) != NULL) {
2851 /* Collect unique fonts in rich text */
2852 if (VALUE_IS_STRING (cell->value) &&
2853 go_format_is_markup (fmt)) {
2854 GArray *txo = txomarkup_new
2855 (ewb,
2856 value_peek_string (cell->value),
2857 go_format_get_markup (fmt),
2858 style);
2860 g_hash_table_insert (ewb->cell_markup, (gpointer)cell, txo);
2861 /* we use RSTRING, no need to add to SST */
2862 use_sst = FALSE;
2865 /* XL has no notion of value format. We need to create
2866 * imaginary styles with the value format substituted into the
2867 * current style. Otherwise an entry like '10:00' gets loaded
2868 * as a raw number. */
2869 else if (go_format_is_general (gnm_style_get_format (style))) {
2870 GnmStyle *tmp = gnm_style_dup (style);
2871 gnm_style_set_format (tmp, fmt);
2872 style = sheet_style_find (cell->base.sheet, tmp);
2873 g_hash_table_insert (ewb->base.xf.value_fmt_styles,
2874 (gpointer)cell,
2875 (gpointer)style);
2879 /* Collect strings for the SST if we need them */
2880 if (use_sst) {
2881 GOString *str = cell->value->v_str.val;
2882 if (!g_hash_table_lookup_extended (ewb->sst.strings, str, NULL, NULL)) {
2883 int index = ewb->sst.indicies->len;
2884 g_ptr_array_add (ewb->sst.indicies, str);
2885 g_hash_table_insert (ewb->sst.strings, str,
2886 GINT_TO_POINTER (index));
2890 if (VALUE_IS_STRING (cell->value)) {
2892 * We need to know if Excel's parser would consider the string
2893 * a non-string if edited. We use our parser as a proxy for
2894 * that.
2896 char *text = gnm_cell_get_entered_text (cell);
2897 gboolean quoted = (text[0] == '\'');
2898 /* No need to synthesize wrap-text if it is already set! */
2899 gboolean wrapped = (NULL != strchr (text, '\n')) &&
2900 !gnm_style_get_wrap_text (style);
2901 g_free (text);
2903 if (quoted || wrapped) {
2904 int xf;
2905 ExcelStyleVariant *esv = g_new (ExcelStyleVariant, 1);
2906 esv->variant = (quoted ? 1 : 0) | (wrapped ? 4 : 0);
2907 esv->style = style;
2908 g_hash_table_insert (ewb->base.xf.cell_style_variant,
2909 (gpointer)cell,
2910 GINT_TO_POINTER (esv->variant));
2911 xf = two_way_table_key_to_idx (ewb->base.xf.two_way_table, esv);
2912 if (xf < 0)
2913 xf = two_way_table_put (ewb->base.xf.two_way_table,
2914 esv, FALSE,
2915 (AfterPutFunc)after_put_esv, NULL);
2916 else
2917 g_free (esv);
2922 static void
2923 cb_accum_styles (GnmStyle const *st, XLExportBase *xle)
2925 ExcelStyleVariant *esv = g_new (ExcelStyleVariant, 1);
2926 esv->style = st;
2927 esv->variant = 0;
2928 two_way_table_put (xle->xf.two_way_table, esv, TRUE,
2929 (AfterPutFunc)after_put_esv, NULL);
2932 static void
2933 gather_styles (ExcelWriteState *ewb)
2935 unsigned i;
2937 for (i = 0; i < ewb->esheets->len; i++) {
2938 ExcelWriteSheet *esheet = g_ptr_array_index (ewb->esheets, i);
2939 Sheet *sheet = esheet->gnum_sheet;
2940 int col, cols = MIN (XLS_MaxCol, gnm_sheet_get_max_cols(sheet));
2941 GPtrArray *cells = sheet_cells (sheet, NULL);
2942 g_ptr_array_foreach (cells,
2943 (GFunc)cb_cell_pre_pass,
2944 &ewb->base);
2945 g_ptr_array_free (cells, TRUE);
2946 sheet_style_foreach (sheet, (GFunc)cb_accum_styles, &ewb->base);
2947 for (col = 0; col < cols; col++) {
2948 ExcelStyleVariant esv;
2949 esv.style = esheet->col_style[col];
2950 esv.variant = 0;
2951 esheet->col_xf[col] = two_way_table_key_to_idx
2952 (ewb->base.xf.two_way_table, &esv);
2958 * get_xf_differences
2959 * @ewb workbook
2960 * @xfd XF data
2961 * @parentst parent style (Not used at present)
2963 * Fill out map of differences to parent style
2965 * FIXME
2966 * At present, we are using a fixed XF record 0, which is the parent of all
2967 * others. Can we use the actual default style as XF 0?
2969 static void
2970 get_xf_differences (BiffXFData *xfd, GnmStyle const *parentst)
2972 int i;
2974 xfd->differences = 0;
2976 if (xfd->format_idx != FORMAT_MAGIC)
2977 xfd->differences |= 1 << MS_BIFF_D_FORMAT_BIT;
2978 if (xfd->font_idx != FONT_MAGIC)
2979 xfd->differences |= 1 << MS_BIFF_D_FONT_BIT;
2980 /* hmm. documentation doesn't say that alignment bit is
2981 affected by vertical alignment, but it's a reasonable guess */
2982 if (xfd->halign != GNM_HALIGN_GENERAL || xfd->valign != GNM_VALIGN_TOP
2983 || xfd->wrap_text)
2984 xfd->differences |= 1 << MS_BIFF_D_ALIGN_BIT;
2985 for (i = 0; i < STYLE_ORIENT_MAX; i++) {
2986 /* Should we also test colors? */
2987 if (xfd->border_type[i] != BORDER_MAGIC) {
2988 xfd->differences |= 1 << MS_BIFF_D_BORDER_BIT;
2989 break;
2992 if (xfd->pat_foregnd_col != PALETTE_AUTO_PATTERN
2993 || (xfd->pat_backgnd_col) != PALETTE_AUTO_BACK
2994 || xfd->fill_pattern_idx != FILL_MAGIC)
2995 xfd->differences |= 1 << MS_BIFF_D_FILL_BIT;
2996 if (xfd->hidden || !xfd->locked)
2997 xfd->differences |= 1 << MS_BIFF_D_LOCK_BIT;
3000 #ifndef NO_DEBUG_EXCEL
3002 * Log XF data for a record about to be written
3004 static void
3005 log_xf_data (XLExportBase *xle, BiffXFData *xfd, int idx)
3007 int i;
3008 ExcelWriteFont *f = fonts_get_font (xle, xfd->font_idx);
3009 /* Formats are saved using the 'C' locale number format */
3010 const char *desc = go_format_as_XL (xfd->style_format);
3012 g_printerr ("Writing xf 0x%x : font 0x%x (%s), format 0x%x (%s)\n",
3013 idx, xfd->font_idx, excel_font_to_string (f),
3014 xfd->format_idx, desc);
3016 g_printerr (" hor align 0x%x, ver align 0x%x, wrap_text %s\n",
3017 xfd->halign, xfd->valign, xfd->wrap_text ? "on" : "off");
3018 g_printerr (" fill fg color idx %d, fill bg color idx %d"
3019 ", pattern (Excel) %d\n",
3020 xfd->pat_foregnd_col, xfd->pat_backgnd_col,
3021 xfd->fill_pattern_idx);
3022 for (i = STYLE_TOP; i < STYLE_ORIENT_MAX; i++) {
3023 if (xfd->border_type[i] != GNM_STYLE_BORDER_NONE) {
3024 g_printerr (" border_type[%d] : 0x%x"
3025 " border_color[%d] : 0x%x\n",
3026 i, xfd->border_type[i],
3027 i, xfd->border_color[i]);
3030 g_printerr (" difference bits: 0x%x\n", xfd->differences);
3032 gnm_style_dump (xfd->mstyle);
3034 #endif
3036 static void
3037 build_xf_data (XLExportBase *xle, BiffXFData *xfd, const ExcelStyleVariant *esv)
3039 ExcelWriteFont *f;
3040 GnmBorder const *b;
3041 int i;
3042 GnmStyle const *st = esv->style;
3044 memset (xfd, 0, sizeof *xfd);
3046 xfd->parentstyle = XF_MAGIC;
3047 xfd->mstyle = (gpointer)st;
3049 f = excel_font_new (st);
3050 xfd->font_idx = two_way_table_key_to_idx (xle->fonts.two_way_table, f);
3051 excel_font_free (f);
3053 xfd->style_format = gnm_style_get_format (st);
3054 xfd->format_idx = formats_get_index (xle, xfd->style_format);
3056 xfd->locked = gnm_style_get_contents_locked (st);
3057 xfd->hidden = gnm_style_get_contents_hidden (st);
3058 xfd->format = (esv->variant & 1) ? MS_BIFF_F_LOTUS : MS_BIFF_F_MS;
3059 xfd->halign = gnm_style_get_align_h (st);
3060 xfd->valign = gnm_style_get_align_v (st);
3061 xfd->wrap_text = gnm_style_get_wrap_text (st) || (esv->variant & 4);
3062 xfd->indent = gnm_style_get_indent (st);
3063 xfd->rotation = gnm_style_get_rotation (st);
3064 xfd->text_dir = gnm_style_get_text_dir (st);
3066 /* Borders */
3067 for (i = STYLE_TOP; i < STYLE_ORIENT_MAX; i++) {
3068 xfd->border_type[i] = GNM_STYLE_BORDER_NONE;
3069 xfd->border_color[i] = 0;
3070 b = gnm_style_get_border (st, MSTYLE_BORDER_TOP + i);
3071 if (b) {
3072 xfd->border_type[i] = b->line_type;
3073 xfd->border_color[i] = map_color_to_palette (xle,
3074 b->color, PALETTE_AUTO_PATTERN);
3078 xfd->fill_pattern_idx = map_pattern_to_xl (gnm_style_get_pattern (st));
3079 xfd->pat_foregnd_col = map_color_to_palette (xle,
3080 gnm_style_get_pattern_color (st), PALETTE_AUTO_PATTERN);
3081 xfd->pat_backgnd_col = map_color_to_palette (xle,
3082 gnm_style_get_back_color (st), PALETTE_AUTO_BACK);
3084 /* Solid patterns seem to reverse the meaning */
3085 if (xfd->fill_pattern_idx == FILL_SOLID) {
3086 guint16 c = xfd->pat_backgnd_col;
3087 xfd->pat_backgnd_col = xfd->pat_foregnd_col;
3088 xfd->pat_foregnd_col = c;
3091 get_xf_differences (xfd, xle->xf.default_style);
3094 static void
3095 excel_write_XF (BiffPut *bp, ExcelWriteState *ewb, BiffXFData *xfd)
3097 guint8 data[XLS_MaxCol];
3098 guint16 tmp16;
3099 guint32 tmp32;
3100 int btype;
3101 int diag;
3103 memset (data, 0, sizeof (data));
3105 if (bp->version >= MS_BIFF_V7)
3106 ms_biff_put_var_next (bp, BIFF_XF);
3107 else
3108 ms_biff_put_var_next (bp, BIFF_XF_OLD_v4);
3110 if (bp->version >= MS_BIFF_V8) {
3111 GSF_LE_SET_GUINT16 (data+0, xfd->font_idx);
3112 GSF_LE_SET_GUINT16 (data+2, xfd->format_idx);
3114 /*********** Byte 4&5 */
3115 tmp16 = 0;
3116 if (xfd->locked)
3117 tmp16 |= (1 << 0);
3118 if (xfd->hidden)
3119 tmp16 |= (1 << 1);
3121 tmp16 |= (0 << 2); /* GnmCell style */
3122 tmp16 |= (xfd->format << 3);
3123 /* tmp16 |= (0 << 4); style 0 == parent */
3124 GSF_LE_SET_GUINT16(data+4, tmp16);
3126 /*********** Byte 6&7 */
3127 tmp16 = halign_to_excel (xfd->halign) & 0x7;
3128 if (xfd->wrap_text)
3129 tmp16 |= (1 << 3);
3130 tmp16 |= (valign_to_excel (xfd->valign) << 4) & 0x70;
3131 /* tmp16 |= (0 << 7); fjustLast from far east ? */
3132 tmp16 |= (rotation_to_excel_v8 (xfd->rotation) << 8) & 0xff00;
3133 GSF_LE_SET_GUINT16(data+6, tmp16);
3135 /*********** Byte 8&9 */
3136 tmp16 = xfd->indent & 0xf;
3137 if (xfd->shrink_to_fit)
3138 tmp16 |= (1 << 4);
3139 /* tmp16 |= (0 << 5); flag merges in MERGECELL */
3140 switch (xfd->text_dir) {
3141 default :
3142 case GNM_TEXT_DIR_CONTEXT: tmp16 |= (0 << 6); break;
3143 case GNM_TEXT_DIR_LTR: tmp16 |= (1 << 6); break;
3144 case GNM_TEXT_DIR_RTL: tmp16 |= (2 << 6); break;
3146 tmp16 |= 0xFC00; /* we store everything */
3147 GSF_LE_SET_GUINT16 (data+8, tmp16);
3149 /*********** Byte 10&11 */
3150 tmp16 = 0;
3151 btype = xfd->border_type[STYLE_LEFT];
3152 if (btype != GNM_STYLE_BORDER_NONE)
3153 tmp16 |= (map_border_to_xl (btype, bp->version) & 0xf) << 0;
3154 btype = xfd->border_type[STYLE_RIGHT];
3155 if (btype != GNM_STYLE_BORDER_NONE)
3156 tmp16 |= (map_border_to_xl (btype, bp->version) & 0xf) << 4;
3157 btype = xfd->border_type[STYLE_TOP];
3158 if (btype != GNM_STYLE_BORDER_NONE)
3159 tmp16 |= (map_border_to_xl (btype, bp->version) & 0xf) << 8;
3160 btype = xfd->border_type[STYLE_BOTTOM];
3161 if (btype != GNM_STYLE_BORDER_NONE)
3162 tmp16 |= (map_border_to_xl (btype, bp->version) & 0xf) << 12;
3163 GSF_LE_SET_GUINT16 (data+10, tmp16);
3165 /*********** Byte 12&13 */
3166 tmp16 = 0;
3167 btype = xfd->border_type[STYLE_LEFT];
3168 if (btype != GNM_STYLE_BORDER_NONE)
3169 tmp16 |= (xfd->border_color[STYLE_LEFT] & 0x7f) << 0;
3170 btype = xfd->border_type[STYLE_RIGHT];
3171 if (btype != GNM_STYLE_BORDER_NONE)
3172 tmp16 |= (xfd->border_color[STYLE_RIGHT] & 0x7f) << 7;
3174 diag = 0;
3175 btype = xfd->border_type[STYLE_DIAGONAL];
3176 if (btype != GNM_STYLE_BORDER_NONE)
3177 diag |= 2;
3178 btype = xfd->border_type[STYLE_REV_DIAGONAL];
3179 if (btype != GNM_STYLE_BORDER_NONE)
3180 diag |= 1;
3182 tmp16 |= diag << 14;
3183 GSF_LE_SET_GUINT16 (data+12, tmp16);
3185 /*********** Byte 14-17 */
3186 tmp32 = 0;
3187 btype = xfd->border_type[STYLE_TOP];
3188 if (btype != GNM_STYLE_BORDER_NONE)
3189 tmp32 |= (xfd->border_color[STYLE_TOP] & 0x7f) << 0;
3190 btype = xfd->border_type[STYLE_BOTTOM];
3191 if (btype != GNM_STYLE_BORDER_NONE)
3192 tmp32 |= (xfd->border_color[STYLE_BOTTOM] & 0x7f) << 7;
3194 diag = (diag & 2) ? STYLE_DIAGONAL : ((diag & 1) ? STYLE_REV_DIAGONAL : 0);
3195 if (diag != 0) {
3196 btype = xfd->border_type [diag];
3197 if (btype != GNM_STYLE_BORDER_NONE) {
3198 tmp32 |= (xfd->border_color[diag] & 0x7f) << 14;
3199 tmp32 |= (map_border_to_xl (btype, bp->version) & 0xf) << 21;
3202 /* tmp32 |= 0 | << 25; reservered 0 */
3203 tmp32 |= (xfd->fill_pattern_idx & 0x3f) << 26;
3204 GSF_LE_SET_GUINT32 (data+14, tmp32);
3206 tmp16 = 0;
3207 /* Fill pattern foreground color */
3208 tmp16 |= xfd->pat_foregnd_col & 0x7f;
3209 /* Fill pattern background color */
3210 tmp16 |= (xfd->pat_backgnd_col << 7) & 0x3f80;
3211 GSF_LE_SET_GUINT16(data+18, tmp16);
3213 ms_biff_put_var_write (bp, data, 20);
3214 } else {
3215 GSF_LE_SET_GUINT16 (data+0, xfd->font_idx);
3216 GSF_LE_SET_GUINT16 (data+2, xfd->format_idx);
3218 tmp16 = 0x0001;
3219 if (xfd->hidden)
3220 tmp16 |= 1 << 1;
3221 if (xfd->locked)
3222 tmp16 |= 1;
3223 tmp16 |= (xfd->parentstyle << 4) & 0xFFF0; /* Parent style */
3224 GSF_LE_SET_GUINT16(data+4, tmp16);
3226 /* Horizontal alignment */
3227 tmp16 = halign_to_excel (xfd->halign) & 0x7;
3228 if (xfd->wrap_text) /* Wrapping */
3229 tmp16 |= 1 << 3;
3230 /* Vertical alignment */
3231 tmp16 |= (valign_to_excel (xfd->valign) << 4) & 0x70;
3232 tmp16 |= (rotation_to_excel_v7 (xfd->rotation) << 8)
3233 & 0x300;
3234 tmp16 |= xfd->differences & 0xFC00; /* Difference bits */
3235 GSF_LE_SET_GUINT16(data+6, tmp16);
3237 /* Documentation is wrong - the fSxButton bit is 1 bit higher.
3238 * which is consistent with biff8. */
3239 tmp16 = 0;
3240 /* Fill pattern foreground color */
3241 tmp16 |= xfd->pat_foregnd_col & 0x7f;
3242 /* Fill pattern background color */
3243 tmp16 |= (xfd->pat_backgnd_col << 7) & 0x3f80;
3244 GSF_LE_SET_GUINT16(data+8, tmp16);
3246 tmp16 = xfd->fill_pattern_idx & 0x3f;
3248 /* Borders */
3249 btype = xfd->border_type[STYLE_BOTTOM];
3250 if (btype != GNM_STYLE_BORDER_NONE) {
3251 tmp16 |= (map_border_to_xl (btype, bp->version) << 6)
3252 & 0x1c0;
3253 tmp16 |= (xfd->border_color[STYLE_BOTTOM] << 9)
3254 & 0xfe00;
3256 GSF_LE_SET_GUINT16(data+10, tmp16);
3258 tmp16 = 0;
3259 btype = xfd->border_type[STYLE_TOP];
3260 if (btype != GNM_STYLE_BORDER_NONE) {
3261 tmp16 |= map_border_to_xl (btype, bp->version) & 0x7;
3262 tmp16 |= (xfd->border_color[STYLE_TOP] << 9) & 0xfe00;
3264 tmp16 |= (map_border_to_xl (xfd->border_type[STYLE_LEFT],
3265 bp->version)
3266 << 3) & 0x38;
3267 tmp16 |= (map_border_to_xl (xfd->border_type[STYLE_RIGHT],
3268 bp->version)
3269 << 6) & 0x1c0;
3270 GSF_LE_SET_GUINT16(data+12, tmp16);
3272 tmp16 = 0;
3273 if (xfd->border_type[STYLE_LEFT] != GNM_STYLE_BORDER_NONE)
3274 tmp16 |= xfd->border_color[STYLE_LEFT] & 0x7f;
3275 if (xfd->border_type[STYLE_RIGHT] != GNM_STYLE_BORDER_NONE)
3276 tmp16 |= (xfd->border_color[STYLE_RIGHT] << 7) & 0x3f80;
3277 GSF_LE_SET_GUINT16(data+14, tmp16);
3279 ms_biff_put_var_write (bp, data, 16);
3281 ms_biff_put_commit (bp);
3284 static void
3285 excel_write_XFs (ExcelWriteState *ewb)
3287 TwoWayTable *twt = ewb->base.xf.two_way_table;
3288 unsigned nxf = twt->idx_to_key->len;
3289 unsigned i;
3290 BiffXFData xfd;
3292 /* it is more compact to just spew the default representations than
3293 * to store a readable form, and generate the constant data. */
3294 static guint8 const builtin_xf_biff8 [XF_RESERVED][20] = {
3295 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3296 { 1, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3297 { 1, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3298 { 2, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3299 { 2, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3300 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3301 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3302 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3303 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3304 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3305 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3306 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3307 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3308 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3309 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf4, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3310 { 0, 0, 0, 0, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3311 { 1, 0, 0x2b, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3312 { 1, 0, 0x29, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3313 { 1, 0, 0x2c, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3314 { 1, 0, 0x2a, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 },
3315 { 1, 0, 0x09, 0, 0xf5, 0xff, 0x20, 0, 0, 0xf8, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x20 }
3318 static guint8 const builtin_xf_biff7 [XF_RESERVED][16] = {
3319 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3320 { 1, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3321 { 1, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3322 { 2, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3323 { 2, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3324 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3325 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3326 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3327 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3328 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3329 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3330 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3331 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3332 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3333 { 0, 0, 0, 0, 0xf5, 0xff, 0x20, 0xf4, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3334 { 0, 0, 0, 0, 1, 0, 0x20, 0, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3335 { 1, 0, 0x2b, 0, 0xf5, 0xff, 0x20, 0xf8, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3336 { 1, 0, 0x29, 0, 0xf5, 0xff, 0x20, 0xf8, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3337 { 1, 0, 0x2c, 0, 0xf5, 0xff, 0x20, 0xf8, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3338 { 1, 0, 0x2a, 0, 0xf5, 0xff, 0x20, 0xf8, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 },
3339 { 1, 0, 0x09, 0, 0xf5, 0xff, 0x20, 0xf8, 0xc0, 0x20, 0, 0, 0, 0, 0, 0 }
3342 static guint8 const builtin_style[][6] = {
3343 { 0x10, 0x80, 0x03, 0xff },
3344 { 0x11, 0x80, 0x06, 0xff },
3345 { 0x12, 0x80, 0x04, 0xff },
3346 { 0x13, 0x80, 0x07, 0xff },
3347 { 0x00, 0x80, 0x00, 0xff },
3348 { 0x14, 0x80, 0x05, 0xff }
3351 /* write the builtins style */
3352 for (i = 0; i < XF_RESERVED; i++) {
3353 ms_biff_put_var_next (ewb->bp, BIFF_XF);
3354 if (ewb->bp->version >= MS_BIFF_V8)
3355 ms_biff_put_var_write (ewb->bp, builtin_xf_biff8[i], 20);
3356 else
3357 ms_biff_put_var_write (ewb->bp, builtin_xf_biff7[i], 16);
3358 ms_biff_put_commit (ewb->bp);
3361 /* now write our styles */
3362 for (; i < nxf + twt->base; i++) {
3363 build_xf_data (&ewb->base, &xfd, xf_get_mstyle (&ewb->base, i));
3364 d (3, log_xf_data (&ewb->base, &xfd, i););
3365 excel_write_XF (ewb->bp, ewb, &xfd);
3368 /* and the trailing STYLE records (not used) */
3369 for (i = 0; i < 6; i++) {
3370 ms_biff_put_var_next (ewb->bp, 0x200|BIFF_STYLE);
3371 ms_biff_put_var_write (ewb->bp, builtin_style[i], 4);
3372 ms_biff_put_commit (ewb->bp);
3377 excel_write_map_errcode (GnmValue const *v)
3379 switch (value_error_classify (v)) {
3380 case GNM_ERROR_NULL: return 0;
3381 case GNM_ERROR_DIV0: return 7;
3382 case GNM_ERROR_VALUE: return 15;
3383 case GNM_ERROR_REF: return 23;
3384 default:
3385 /* Map non-excel errors to #!NAME */
3386 case GNM_ERROR_NAME: return 29;
3387 case GNM_ERROR_NUM: return 36;
3388 case GNM_ERROR_NA: return 42;
3392 static void
3393 excel_write_value (ExcelWriteState *ewb, GnmValue *v, guint32 col, guint32 row, guint16 xf)
3395 switch (v->v_any.type) {
3397 case VALUE_EMPTY: {
3398 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_BLANK_v2, 6);
3399 EX_SETROW(data, row);
3400 EX_SETCOL(data, col);
3401 EX_SETXF (data, xf);
3402 ms_biff_put_commit (ewb->bp);
3403 break;
3405 case VALUE_BOOLEAN:
3406 case VALUE_ERROR: {
3407 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_BOOLERR_v2, 8);
3408 EX_SETROW(data, row);
3409 EX_SETCOL(data, col);
3410 EX_SETXF (data, xf);
3411 if (VALUE_IS_ERROR (v)) {
3412 GSF_LE_SET_GUINT8 (data + 6, excel_write_map_errcode (v));
3413 GSF_LE_SET_GUINT8 (data + 7, 1); /* Mark as a err */
3414 } else {
3415 GSF_LE_SET_GUINT8 (data + 6, value_get_as_int (v));
3416 GSF_LE_SET_GUINT8 (data + 7, 0); /* Mark as a bool */
3418 ms_biff_put_commit (ewb->bp);
3419 break;
3421 case VALUE_FLOAT: {
3422 gnm_float val = value_get_as_float (v);
3423 gboolean is_int = (val >= G_MININT32 / 4 &&
3424 val <= G_MAXINT32 / 4 &&
3425 val == gnm_floor (val));
3427 d (3, g_printerr ("Writing %g is (%g %g) is int ? %d\n",
3428 (double)val,
3429 (double)(1.0 * gnm_floor (val)),
3430 (double)(1.0 * (val - gnm_floor (val))),
3431 is_int););
3433 /* FIXME : Add test for double of form i/100.0
3434 * and represent it as a mode 3 RK (val*100) construct */
3435 if (is_int) {
3436 guint8 *data = ms_biff_put_len_next (ewb->bp, (0x200 | BIFF_RK), 10);
3437 /* Double cast needed per C99 standard. */
3438 guint32 ival = (guint32)(gint32)val;
3439 EX_SETROW(data, row);
3440 EX_SETCOL(data, col);
3441 EX_SETXF (data, xf);
3442 /* Integers can always be represented as integers.
3443 * Use RK form 2 */
3444 GSF_LE_SET_GUINT32 (data + 6, (ival << 2) + 2);
3445 ms_biff_put_commit (ewb->bp);
3446 } else if (ewb->bp->version >= MS_BIFF_V7) {
3447 guint8 *data =ms_biff_put_len_next (ewb->bp, BIFF_NUMBER_v2, 14);
3448 EX_SETROW(data, row);
3449 EX_SETCOL(data, col);
3450 EX_SETXF (data, xf);
3451 gsf_le_set_double (data + 6, val);
3452 ms_biff_put_commit (ewb->bp);
3453 } else {
3454 guint8 data[16];
3456 ms_biff_put_var_next (ewb->bp, (0x200 | BIFF_RK));
3457 gsf_le_set_double (data+6-4, val);
3458 EX_SETROW(data, row);
3459 EX_SETCOL(data, col);
3460 EX_SETXF (data, xf);
3461 data[6] &= 0xfc;
3462 ms_biff_put_var_write (ewb->bp, data, 10); /* Yes loose it. */
3463 ms_biff_put_commit (ewb->bp);
3465 break;
3467 case VALUE_STRING:
3468 g_return_if_fail (v->v_str.val->str);
3470 /* Use LABEL */
3471 if (ewb->bp->version < MS_BIFF_V8) {
3472 guint8 data[6];
3474 ms_biff_put_var_next (ewb->bp, BIFF_LABEL_v2);
3476 EX_SETXF (data, xf);
3477 EX_SETCOL(data, col);
3478 EX_SETROW(data, row);
3479 ms_biff_put_var_write (ewb->bp, data, 6);
3480 excel_write_string (ewb->bp, STR_TWO_BYTE_LENGTH,
3481 v->v_str.val->str);
3482 ms_biff_put_commit (ewb->bp);
3483 } else {
3484 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_LABELSST, 10);
3485 gpointer indx = g_hash_table_lookup (ewb->sst.strings, v->v_str.val);
3486 EX_SETXF (data, xf);
3487 EX_SETCOL(data, col);
3488 EX_SETROW(data, row);
3489 GSF_LE_SET_GUINT32 (data+6, GPOINTER_TO_INT (indx));
3490 ms_biff_put_commit (ewb->bp);
3492 break;
3494 default:
3495 g_printerr ("Unhandled value type %d\n", v->v_any.type);
3496 break;
3500 static void
3501 excel_write_FORMULA (ExcelWriteState *ewb, ExcelWriteSheet *esheet, GnmCell const *cell, gint16 xf)
3503 guint8 data[22];
3504 guint8 lendat[2];
3505 guint32 len;
3506 gboolean string_result = FALSE;
3507 gint col, row;
3508 GnmValue *v;
3509 GnmExprTop const *texpr;
3511 g_return_if_fail (ewb);
3512 g_return_if_fail (cell);
3513 g_return_if_fail (esheet);
3514 g_return_if_fail (gnm_cell_has_expr (cell));
3515 g_return_if_fail (cell->value);
3517 col = cell->pos.col;
3518 row = cell->pos.row;
3519 v = cell->value;
3520 texpr = cell->base.texpr;
3522 ms_biff_put_var_next (ewb->bp, BIFF_FORMULA_v0);
3523 EX_SETROW (data, row);
3524 EX_SETCOL (data, col);
3525 EX_SETXF (data, xf);
3526 switch (v->v_any.type) {
3527 case VALUE_FLOAT:
3528 gsf_le_set_double (data + 6, value_get_as_float (v));
3529 break;
3531 case VALUE_STRING:
3532 GSF_LE_SET_GUINT32 (data + 6, 0x00000000);
3533 GSF_LE_SET_GUINT32 (data + 10, 0xffff0000);
3534 string_result = TRUE;
3535 break;
3537 case VALUE_BOOLEAN:
3538 GSF_LE_SET_GUINT32 (data + 6,
3539 value_get_as_checked_bool (v) ? 0x10001 : 0x1);
3540 GSF_LE_SET_GUINT32 (data + 10, 0xffff0000);
3541 break;
3543 case VALUE_ERROR:
3544 GSF_LE_SET_GUINT32 (data + 6,
3545 0x00000002 | (excel_write_map_errcode (v) << 16));
3546 GSF_LE_SET_GUINT32 (data + 10, 0xffff0000);
3547 break;
3549 case VALUE_EMPTY:
3550 GSF_LE_SET_GUINT32 (data + 6, 0x00000003);
3551 GSF_LE_SET_GUINT32 (data + 10, 0xffff0000);
3552 break;
3554 default:
3555 g_warning ("Unhandled value->type (%d) in excel_write_FORMULA.", v->v_any.type);
3558 GSF_LE_SET_GUINT16 (data + 14, /* alwaysCalc & calcOnLoad */
3559 (cell->base.flags & DEPENDENT_HAS_DYNAMIC_DEPS) ? 1 : 0);
3561 /*** This is why XL produces a warning when exiting with files we generate
3562 * and complains about them being from 'older' versions. The numbers
3563 * have no obvious pattern but I have not looked terribly hard. */
3564 GSF_LE_SET_GUINT32 (data + 16, 0x0);
3566 GSF_LE_SET_GUINT16 (data + 20, 0x0); /* bogus len, fill in later */
3567 ms_biff_put_var_write (ewb->bp, data, 22);
3568 len = excel_write_formula (ewb, texpr, esheet->gnum_sheet,
3569 col, row, EXCEL_CALLED_FROM_CELL); /* unshared for now */
3571 ms_biff_put_var_seekto (ewb->bp, 20);
3572 GSF_LE_SET_GUINT16 (lendat, len);
3573 ms_biff_put_var_write (ewb->bp, lendat, 2);
3575 ms_biff_put_commit (ewb->bp);
3577 if (gnm_expr_top_is_array_corner (texpr)) {
3578 GnmCellPos c_in, r_in;
3579 int cols, rows;
3581 gnm_expr_top_get_array_size (texpr, &cols, &rows);
3583 if (gnm_expr_is_data_table (gnm_expr_top_get_array_expr (texpr), &c_in, &r_in)) {
3584 guint16 flags = 0;
3585 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_TABLE_v2, 16);
3586 GSF_LE_SET_GUINT16 (data + 0, cell->pos.row);
3587 GSF_LE_SET_GUINT16 (data + 2, cell->pos.row + rows-1);
3588 GSF_LE_SET_GUINT16 (data + 4, cell->pos.col);
3589 GSF_LE_SET_GUINT16 (data + 5, cell->pos.col + cols-1);
3591 if ((c_in.col != 0 || c_in.row != 0) &&
3592 (r_in.col != 0 || r_in.row != 0)) {
3593 GSF_LE_SET_GUINT16 (data + 8, r_in.row + cell->pos.row);
3594 GSF_LE_SET_GUINT16 (data +10, r_in.col + cell->pos.col);
3595 GSF_LE_SET_GUINT16 (data +12, c_in.row + cell->pos.row);
3596 GSF_LE_SET_GUINT16 (data +14, c_in.col + cell->pos.col);
3597 flags |= 0xC;
3598 } else {
3599 if (c_in.col != 0 || c_in.row != 0) {
3600 GSF_LE_SET_GUINT16 (data + 8, c_in.row + cell->pos.row);
3601 GSF_LE_SET_GUINT16 (data +10, c_in.col + cell->pos.col);
3602 /* it seems arbitrary, but this is what XL generates */
3603 GSF_LE_SET_GUINT16 (data +12, 0x401c);
3604 /* flags |= 0; */
3605 } else {
3606 GSF_LE_SET_GUINT16 (data + 8, r_in.row + cell->pos.row);
3607 GSF_LE_SET_GUINT16 (data +10, r_in.col + cell->pos.col);
3608 /* it seems arbitrary, but this is what XL generates */
3609 GSF_LE_SET_GUINT16 (data +12, 0x4037);
3610 flags |= 4;
3612 GSF_LE_SET_GUINT16 (data +14, 0);
3614 GSF_LE_SET_GUINT16 (data + 6, flags);
3615 ms_biff_put_commit (ewb->bp);
3616 } else {
3617 ms_biff_put_var_next (ewb->bp, BIFF_ARRAY_v2);
3618 GSF_LE_SET_GUINT16 (data+0, cell->pos.row);
3619 GSF_LE_SET_GUINT16 (data+2, cell->pos.row + rows-1);
3620 GSF_LE_SET_GUINT16 (data+4, cell->pos.col);
3621 GSF_LE_SET_GUINT16 (data+5, cell->pos.col + cols-1);
3622 GSF_LE_SET_GUINT16 (data+6, 0x0); /* alwaysCalc & calcOnLoad */
3623 GSF_LE_SET_GUINT32 (data+8, 0);
3624 GSF_LE_SET_GUINT16 (data+12, 0); /* bogus len, fill in later */
3625 ms_biff_put_var_write (ewb->bp, data, 14);
3626 len = excel_write_array_formula (ewb, texpr,
3627 esheet->gnum_sheet, col, row);
3629 ms_biff_put_var_seekto (ewb->bp, 12);
3630 GSF_LE_SET_GUINT16 (lendat, len);
3631 ms_biff_put_var_write (ewb->bp, lendat, 2);
3632 ms_biff_put_commit (ewb->bp);
3636 if (string_result) {
3637 char const *str = value_peek_string (v);
3638 ms_biff_put_var_next (ewb->bp, BIFF_STRING_v2);
3639 excel_write_string (ewb->bp, STR_TWO_BYTE_LENGTH, str);
3640 ms_biff_put_commit (ewb->bp);
3644 #define MAX_BIFF_NOTE_CHUNK 2048
3646 /* biff7 and earlier */
3647 static void
3648 excel_write_comments_biff7 (BiffPut *bp, ExcelWriteSheet *esheet)
3650 GSList *l;
3652 for (l = esheet->comments; l; l = l->next) {
3653 GnmComment const *cc = l->data;
3654 GnmRange const *pos = sheet_object_get_range (GNM_SO (cc));
3655 char const *in = cell_comment_text_get (cc);
3656 size_t out_bytes, o;
3657 gpointer convstr;
3659 g_return_if_fail (in != NULL);
3660 g_return_if_fail (pos != NULL);
3662 /* gsf_mem_dump (in, strlen (in)); */
3663 convstr = excel_convert_string (bp, in, &out_bytes);
3664 /* gsf_mem_dump (convstr, out_bytes); */
3666 for (o = 0; o < out_bytes; o += MAX_BIFF_NOTE_CHUNK) {
3667 guint8 data[6];
3668 size_t this_len = MIN (MAX_BIFF_NOTE_CHUNK, out_bytes - o);
3670 ms_biff_put_var_next (bp, BIFF_NOTE);
3671 GSF_LE_SET_GUINT16 (data + 0, o ? 0xffff : pos->start.row);
3672 GSF_LE_SET_GUINT16 (data + 2, o ? 0 : pos->start.col);
3673 GSF_LE_SET_GUINT16 (data + 4, o ? this_len : out_bytes);
3674 ms_biff_put_var_write (bp, data, 6);
3676 ms_biff_put_var_write (bp, (char *)convstr + o, this_len);
3677 ms_biff_put_commit (bp);
3679 g_free (convstr);
3683 static void
3684 excel_write_RSTRING (ExcelWriteState *ewb, GnmCell const *cell, unsigned xf)
3686 GArray *txo = g_hash_table_lookup (ewb->cell_markup, cell);
3687 const char *txt = value_peek_string (cell->value);
3688 size_t txtlen = strlen (txt);
3689 guint8 buf [6];
3690 unsigned i, n;
3692 g_return_if_fail (txo != NULL);
3694 ms_biff_put_var_next (ewb->bp, BIFF_RSTRING);
3695 EX_SETROW (buf, cell->pos.row);
3696 EX_SETCOL (buf, cell->pos.col);
3697 EX_SETXF (buf, xf);
3698 ms_biff_put_var_write (ewb->bp, buf, 6);
3699 excel_write_string (ewb->bp, STR_TWO_BYTE_LENGTH, txt);
3701 n = txo->len / 2;
3702 if (ewb->bp->version < MS_BIFF_V8) {
3703 GSF_LE_SET_GUINT8 (buf, n);
3704 ms_biff_put_var_write (ewb->bp, buf, 1);
3705 for (i = 0; i < n ; i++) {
3706 guint bpos = g_array_index (txo, guint, i*2);
3707 gint cpos = g_utf8_pointer_to_offset (txt, txt + MIN (bpos, txtlen));
3708 GSF_LE_SET_GUINT8 (buf, cpos);
3709 GSF_LE_SET_GUINT8 (buf + 1,
3710 g_array_index (txo, guint, i*2+1));
3711 ms_biff_put_var_write (ewb->bp, buf, 2);
3713 } else {
3714 GSF_LE_SET_GUINT16 (buf, n);
3715 ms_biff_put_var_write (ewb->bp, buf, 2);
3716 for (i = 0; i < n ; i++) {
3717 guint bpos = g_array_index (txo, guint, i*2);
3718 gint cpos = g_utf8_pointer_to_offset (txt, txt + MIN (bpos, txtlen));
3719 GSF_LE_SET_GUINT16 (buf, cpos);
3720 GSF_LE_SET_GUINT16 (buf + 2,
3721 g_array_index (txo, guint, i*2+1));
3722 ms_biff_put_var_write (ewb->bp, buf, 4);
3726 ms_biff_put_commit (ewb->bp);
3729 static void
3730 excel_write_cell (ExcelWriteState *ewb, ExcelWriteSheet *esheet,
3731 GnmCell const *cell, unsigned xf)
3733 GnmValue *v;
3735 d (2, {
3736 GnmParsePos tmp;
3737 g_printerr ("Writing cell at %s '%s' = '%s', xf = 0x%x\n",
3738 cell_name (cell),
3739 (gnm_cell_has_expr (cell)
3740 ? gnm_expr_top_as_string (cell->base.texpr,
3741 parse_pos_init_cell (&tmp, cell),
3742 gnm_conventions_default)
3743 : "none"),
3744 (cell->value ?
3745 value_get_as_string (cell->value) : "empty"), xf);
3748 if (gnm_cell_has_expr (cell))
3749 excel_write_FORMULA (ewb, esheet, cell, xf);
3750 else if ((v = cell->value) != NULL) {
3751 if (VALUE_IS_STRING (v) &&
3752 VALUE_FMT (v) != NULL &&
3753 go_format_is_markup (VALUE_FMT (v)))
3754 excel_write_RSTRING (ewb, cell, xf);
3755 else
3756 excel_write_value (ewb, cell->value, cell->pos.col, cell->pos.row, xf);
3761 * write_mulblank
3762 * @bp BIFF buffer
3763 * @esheet sheet
3764 * @end_col last blank column
3765 * @row row
3766 * @xf_list list of XF indices - one per cell
3767 * @run number of blank cells
3769 * Write multiple blanks to file
3771 static void
3772 write_mulblank (BiffPut *bp, ExcelWriteSheet *esheet, guint32 end_col, guint32 row,
3773 guint16 const *xf_list, int run)
3775 guint16 xf;
3776 g_return_if_fail (bp);
3777 g_return_if_fail (run);
3778 g_return_if_fail (esheet);
3780 if (run == 1) {
3781 guint8 *data;
3783 xf = xf_list [0];
3784 d (2, g_printerr ("Writing blank at %s, xf = 0x%x\n",
3785 cell_coord_name (end_col, row), xf););
3787 data = ms_biff_put_len_next (bp, BIFF_BLANK_v2, 6);
3788 EX_SETXF (data, xf);
3789 EX_SETCOL(data, end_col);
3790 EX_SETROW(data, row);
3791 } else {
3792 guint8 *ptr, *data;
3793 guint32 len = 4 + 2*run + 2;
3794 int i;
3796 d (2, {
3797 /* Strange looking code because the second
3798 * cell_coord_name call overwrites the result of the
3799 * first */
3800 g_printerr ("Writing multiple blanks %s",
3801 cell_coord_name (end_col + 1 - run, row));
3802 g_printerr (":%s\n", cell_coord_name (end_col, row));
3805 data = ms_biff_put_len_next (bp, BIFF_MULBLANK, len);
3807 EX_SETCOL (data, end_col + 1 - run);
3808 EX_SETROW (data, row);
3809 GSF_LE_SET_GUINT16 (data + len - 2, end_col);
3810 ptr = data + 4;
3811 for (i = 0 ; i < run ; i++) {
3812 xf = xf_list [i];
3813 d (3, g_printerr (" xf(%s) = 0x%x\n",
3814 cell_coord_name (end_col + 1 - i, row),
3815 xf););
3816 GSF_LE_SET_GUINT16 (ptr, xf);
3817 ptr += 2;
3820 d (3, g_printerr ("\n"););
3823 ms_biff_put_commit (bp);
3827 * excel_write_GUTS
3828 * @bp: BIFF buffer
3829 * @esheet: sheet
3831 * Write information about outline mode gutters.
3833 static void
3834 excel_write_GUTS (BiffPut *bp, ExcelWriteSheet *esheet)
3836 guint8 *data = ms_biff_put_len_next (bp, BIFF_GUTS, 8);
3837 int row_level = MIN (esheet->gnum_sheet->rows.max_outline_level, 0x7);
3838 int col_level = MIN (esheet->gnum_sheet->cols.max_outline_level, 0x7);
3839 int row_size = 0, col_size = 0;
3841 /* This seems to be what the default is */
3842 if (row_level > 0) {
3843 row_level++;
3844 row_size = 5 + 12 * row_level;
3846 if (col_level > 0) {
3847 col_level++;
3848 col_size = 5 + 12 * col_level;
3850 GSF_LE_SET_GUINT16 (data+0, row_size);
3851 GSF_LE_SET_GUINT16 (data+2, col_size);
3852 GSF_LE_SET_GUINT16 (data+4, row_level);
3853 GSF_LE_SET_GUINT16 (data+6, col_level);
3854 ms_biff_put_commit (bp);
3857 static void
3858 excel_write_DEFAULT_ROW_HEIGHT (BiffPut *bp, ExcelWriteSheet *esheet)
3860 guint8 *data;
3861 double def_height;
3862 guint16 options = 0x0;
3863 guint16 height;
3865 def_height = sheet_row_get_default_size_pts (esheet->gnum_sheet);
3866 height = (guint16) (20. * def_height + 1e-6);
3867 d (1, g_printerr ("Default row height 0x%x;\n", height););
3868 data = ms_biff_put_len_next (bp, BIFF_DEFAULTROWHEIGHT_v2, 4);
3869 GSF_LE_SET_GUINT16 (data + 0, options);
3870 GSF_LE_SET_GUINT16 (data + 2, height);
3871 ms_biff_put_commit (bp);
3874 static void
3875 excel_write_margin (BiffPut *bp, guint16 op, double points)
3877 guint8 *data = ms_biff_put_len_next (bp, op, 8);
3878 gsf_le_set_double (data, points_to_inches (points));
3880 ms_biff_put_commit (bp);
3883 static XL_font_width const *
3884 xl_find_fontspec (ExcelWriteSheet *esheet, double *scale)
3886 /* Use the 'Normal' Style which is by definition the 0th */
3887 GnmStyle const *def_style = esheet->ewb->base.xf.default_style;
3888 *scale = gnm_style_get_font_size (def_style) / 10.;
3889 return xl_lookup_font_specs (gnm_style_get_font_name (def_style));
3892 static void
3893 excel_write_DEFCOLWIDTH (BiffPut *bp, ExcelWriteSheet *esheet)
3895 guint16 charwidths;
3896 double width, scale;
3897 XL_font_width const *spec = xl_find_fontspec (esheet, &scale);
3899 /* pts to avoid problems when zooming */
3900 width = sheet_col_get_default_size_pts (esheet->gnum_sheet);
3901 width *= 96./72.; /* pixels at 96dpi */
3903 charwidths = (guint16)((width / (scale * spec->defcol_unit)) + .5);
3905 d (1, g_printerr ("Default column width %hu characters (%f XL pixels)\n", charwidths, width););
3907 ms_biff_put_2byte (bp, BIFF_DEFCOLWIDTH, charwidths);
3911 * excel_write_COLINFO
3912 * @bp: BIFF buffer
3913 * @esheet:
3914 * @ci: the descriptor of the first col (possibly NULL)
3915 * @first_col: the index of the last contiguous identical col
3916 * @last_col: the index of the last contiguous identical col
3917 * @xf_index: the style index to the entire col (< 0 for none)
3919 * Write column info for a run of identical columns
3921 static void
3922 excel_write_COLINFO (BiffPut *bp, ExcelWriteSheet *esheet, ColRowInfo const *ci,
3923 int first_col, int last_col, guint16 xf_index)
3925 guint8 *data;
3926 guint16 charwidths, options = 0;
3927 double width, scale;
3928 double def_width = esheet->gnum_sheet->cols.default_style.size_pts;
3929 XL_font_width const *spec;
3931 if (NULL != ci) {
3932 width = ci->size_pts; /* pts to avoid problems when zooming */
3933 if (!ci->visible)
3934 options = 1;
3935 if (ci->hard_size)
3936 options |= 2;
3937 /* not user assigned but width != default is 'bestFit' */
3938 else if (fabs (def_width - width) > .1)
3939 options |= 6;
3941 options |= (MIN (ci->outline_level, 0x7) << 8);
3942 if (ci->is_collapsed)
3943 options |= 0x1000;
3944 } else {
3945 if (xf_index == 0) /* do not export cols with the default style */
3946 return;
3947 width = esheet->gnum_sheet->cols.default_style.size_pts;
3950 /* center the measurement on the known default size */
3951 spec = xl_find_fontspec (esheet, &scale);
3952 width /= scale * 72. / 96; /* pixels at 96dpi */
3953 charwidths = (guint16)((width - 8. * spec->defcol_unit) * spec->colinfo_step +
3954 spec->colinfo_baseline + .5);
3955 d (1, {
3956 g_printerr ("Column Formatting %s!%s of width "
3957 "%hu/256 characters\n",
3958 esheet->gnum_sheet->name_quoted,
3959 cols_name (first_col, last_col), charwidths);
3960 g_printerr ("Options %hd, default style %hd\n", options, xf_index);
3963 /* NOTE : Docs are wrong, length is 12 not 11 */
3964 data = ms_biff_put_len_next (bp, BIFF_COLINFO, 12);
3965 GSF_LE_SET_GUINT16 (data + 0, first_col); /* 1st col formatted */
3966 GSF_LE_SET_GUINT16 (data + 2, last_col); /* last col formatted */
3967 GSF_LE_SET_GUINT16 (data + 4, charwidths);
3968 GSF_LE_SET_GUINT16 (data + 6, xf_index);
3969 GSF_LE_SET_GUINT16 (data + 8, options);
3970 GSF_LE_SET_GUINT16 (data + 10, 0);
3971 ms_biff_put_commit (bp);
3974 static void
3975 excel_write_colinfos (BiffPut *bp, ExcelWriteSheet *esheet)
3977 ColRowInfo const *ci, *info;
3978 int first_col = 0, i;
3979 guint16 new_xf, xf = 0;
3980 int cols = MIN (XLS_MaxCol, gnm_sheet_get_max_cols (esheet->gnum_sheet));
3982 info = sheet_col_get (esheet->gnum_sheet, 0);
3983 xf = esheet->col_xf [0];
3984 for (i = 1; i < cols; i++) {
3985 ci = sheet_col_get (esheet->gnum_sheet, i);
3986 new_xf = esheet->col_xf [i];
3987 if (xf != new_xf || !col_row_info_equal (info, ci)) {
3988 excel_write_COLINFO (bp, esheet, info, first_col, i-1, xf);
3989 info = ci;
3990 xf = new_xf;
3991 first_col = i;
3994 excel_write_COLINFO (bp, esheet, info, first_col, i-1, xf);
3997 static char const *
3998 excel_write_DOPER (GnmFilterCondition const *cond, int i, guint8 *buf)
4000 char const *str = NULL;
4001 GnmValue const *v = cond->value[i];
4003 if (cond->op[i] == GNM_FILTER_UNUSED)
4004 return NULL;
4005 switch (v->v_any.type) {
4006 case VALUE_BOOLEAN:
4007 buf[0] = 8;
4008 buf[2] = 0;
4009 buf[3] = value_get_as_int (v);
4010 break;
4012 case VALUE_FLOAT: {
4013 gnm_float f = value_get_as_float (v);
4014 if (f < G_MININT32 / 4 ||
4015 f > G_MAXINT32 / 4 ||
4016 f != gnm_floor (f)) {
4017 buf[0] = 4;
4018 gsf_le_set_double (buf + 2, f);
4019 } else {
4020 guint32 i = (guint32)f;
4021 buf[0] = 2;
4022 GSF_LE_SET_GUINT32 (buf + 2, (i << 2) | 2);
4023 break;
4025 break;
4028 case VALUE_ERROR:
4029 buf[0] = 8;
4030 buf[2] = 1;
4031 buf[3] = excel_write_map_errcode (v);
4032 break;
4034 case VALUE_STRING:
4035 buf[0] = 6;
4036 str = value_peek_string (v);
4037 buf[6] = excel_strlen (str, NULL);
4038 break;
4039 default :
4040 /* ignore arrays, ranges, empties */
4041 break;
4044 switch (cond->op[0]) {
4045 case GNM_FILTER_OP_EQUAL: buf[1] = 2; break;
4046 case GNM_FILTER_OP_GT: buf[1] = 4; break;
4047 case GNM_FILTER_OP_LT: buf[1] = 1; break;
4048 case GNM_FILTER_OP_GTE: buf[1] = 6; break;
4049 case GNM_FILTER_OP_LTE: buf[1] = 3; break;
4050 case GNM_FILTER_OP_NOT_EQUAL: buf[1] = 5; break;
4051 default :
4052 g_warning ("how did this happen");
4055 return str;
4058 static void
4059 excel_write_AUTOFILTERINFO (BiffPut *bp, ExcelWriteSheet *esheet)
4061 GnmFilterCondition const *cond;
4062 GnmFilter const *filter;
4063 unsigned count, i;
4064 char const *str0 = NULL, *str1 = NULL;
4066 if (esheet->gnum_sheet->filters == NULL)
4067 return;
4068 filter = esheet->gnum_sheet->filters->data;
4070 ms_biff_put_empty (bp, BIFF_FILTERMODE);
4072 /* Write the autofilter flag */
4073 count = range_width (&filter->r);
4074 ms_biff_put_2byte (bp, BIFF_AUTOFILTERINFO, count);
4076 /* the fields */
4077 for (i = 0; i < filter->fields->len ; i++) {
4078 guint8 buf[24];
4080 /* filter unused or bucket filters in excel5 */
4081 if (NULL == (cond = gnm_filter_get_condition (filter, i)) ||
4082 cond->op[0] == GNM_FILTER_UNUSED ||
4083 ((cond->op[0] & GNM_FILTER_OP_TYPE_MASK) == GNM_FILTER_OP_TOP_N &&
4084 bp->version < MS_BIFF_V8))
4085 continue;
4087 ms_biff_put_var_next (bp, BIFF_AUTOFILTER);
4088 memset (buf, 0, sizeof buf);
4090 switch (cond->op[0]) {
4091 case GNM_FILTER_OP_BLANKS:
4092 buf[2] = 0x04; /* "fSimple1" */
4093 buf[5] = 0x0c;
4094 break;
4096 case GNM_FILTER_OP_NON_BLANKS:
4097 buf[2] = 0x04; /* "fSimple1" */
4098 buf[5] = 0x0E;
4099 break;
4101 case GNM_FILTER_OP_TOP_N:
4102 case GNM_FILTER_OP_BOTTOM_N:
4103 case GNM_FILTER_OP_TOP_N_PERCENT:
4104 case GNM_FILTER_OP_BOTTOM_N_PERCENT: {
4105 guint16 flags = 0x10; /* top/bottom n */
4106 /* The limit of 500 is actually documented. */
4107 int count = CLAMP (cond->count, 1, 500);
4109 flags |= (count << 7);
4110 if ((cond->op[0] & GNM_FILTER_OP_BOTTOM_MASK) == 0)
4111 flags |= 0x20;
4112 if ((cond->op[0] & GNM_FILTER_OP_PERCENT_MASK) != 0)
4113 flags |= 0x40;
4114 GSF_LE_SET_GUINT16 (buf+2, flags);
4115 break;
4118 default :
4119 str0 = excel_write_DOPER (cond, 0, buf + 4);
4120 str1 = excel_write_DOPER (cond, 1, buf + 14);
4121 GSF_LE_SET_GUINT16 (buf+2, cond->is_and ? 0 : 1);
4124 GSF_LE_SET_GUINT16 (buf, i);
4125 ms_biff_put_var_write (bp, buf, sizeof buf);
4127 if (str0 != NULL)
4128 excel_write_string (bp, STR_NO_LENGTH, str0);
4129 if (str1 != NULL)
4130 excel_write_string (bp, STR_NO_LENGTH, str1);
4132 ms_biff_put_commit (bp);
4136 static void
4137 excel_write_autofilter_names (ExcelWriteState *ewb)
4139 unsigned i;
4141 for (i = 0; i < ewb->esheets->len; i++) {
4142 ExcelWriteSheet const *esheet =
4143 g_ptr_array_index (ewb->esheets, i);
4144 Sheet *sheet = esheet->gnum_sheet;
4145 if (sheet->filters != NULL) {
4146 GnmFilter const *filter = sheet->filters->data;
4147 GnmParsePos pp;
4148 GnmNamedExpr *nexpr;
4149 const char *name = "_FilterDatabase";
4150 gboolean is_new;
4152 parse_pos_init_sheet (&pp, sheet);
4154 nexpr = expr_name_lookup (&pp, name);
4155 is_new = (nexpr == NULL);
4156 if (is_new)
4157 nexpr = expr_name_new (name);
4159 nexpr->is_hidden = TRUE;
4160 expr_name_set_is_placeholder (nexpr, FALSE);
4161 expr_name_set_pos (nexpr, &pp);
4162 expr_name_set_expr (nexpr,
4163 gnm_expr_top_new_constant
4164 (value_new_cellrange_r (sheet, &filter->r)));
4165 excel_write_NAME (NULL, nexpr, ewb);
4167 if (NULL != sheet->filters->next) {
4168 /* TODO Warn of lost autofilters */
4171 if (is_new)
4172 expr_name_remove (nexpr);
4177 static void
4178 excel_write_anchor (guint8 *buf, SheetObjectAnchor const *anchor)
4180 GSF_LE_SET_GUINT16 (buf + 0, anchor->cell_bound.start.col);
4181 GSF_LE_SET_GUINT16 (buf + 2, (guint16)(anchor->offset[0]*1024. + .5));
4182 GSF_LE_SET_GUINT16 (buf + 4, anchor->cell_bound.start.row);
4183 GSF_LE_SET_GUINT16 (buf + 6, (guint16)(anchor->offset[1]*256. + .5));
4184 GSF_LE_SET_GUINT16 (buf + 8, anchor->cell_bound.end.col);
4185 GSF_LE_SET_GUINT16 (buf + 10, (guint16)(anchor->offset[2]*1024. + .5));
4186 GSF_LE_SET_GUINT16 (buf + 12, anchor->cell_bound.end.row);
4187 GSF_LE_SET_GUINT16 (buf + 14, (guint16)(anchor->offset[3]*256. + .5));
4190 static guint32
4191 excel_write_start_drawing (ExcelWriteSheet *esheet)
4193 if (esheet->cur_obj++ > 0)
4194 ms_biff_put_var_next (esheet->ewb->bp, BIFF_MS_O_DRAWING);
4195 return 0x400*esheet->ewb->cur_obj_group + esheet->cur_obj;
4198 static gsize
4199 excel_write_autofilter_objs (ExcelWriteSheet *esheet)
4201 static guint8 const std_obj_v7[] = {
4202 0, 0, 0, 0, /* count */
4203 0x14, 0, /* its a combo (XL calls it dropdown) */
4204 0, 0, /* ID (we just use count) */
4205 0x12, 0x82, /* autosize, locked, visible, infilter */
4206 0, 0, /* start column */
4207 0, 0, /* start column offset */
4208 0, 0, /* start row */
4209 0, 0, /* start row offset */
4210 1, 0, /* end col */
4211 0, 0, /* end col offset */
4212 1, 0, /* end row */
4213 0, 0, /* end row offset */
4214 0, 0,
4215 0, 0, 0, 0, 0, 0,
4217 9, 9, 1, 0, 8, 0xff, 1, 0, 0, 0, 0, 0, 0, 0,
4218 0, 0, 0, 0, 0x64, 0, 1, 0, 0x0a, 0, 0, 0, 0x10, 0, 1, 0,
4219 0, 0, 0, 0, 0, 0, 1, 0, 1, 3, 0, 0, 0, 0, 0, 0,
4220 0, 0, 5, 0, 0, 0, 0, 0, 2, 0, 8, 0, 0x57, 0, 0, 0,
4221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4222 0, 0, 0, 0, 0, 0, 1, 0, 1, 3, 0, 0,
4223 2, 0, /* 0x000a if it has active condition */
4224 8, 0, 0x57, 0, 0, 0
4227 static guint8 const obj_v8[] = {
4228 /* SpContainer */ 0xf, 0, 4, 0xf0, 0x52, 0, 0, 0,
4229 /* Sp */ 0x92, 0xc, 0xa, 0xf0, 8, 0, 0, 0,
4230 0, 0, 0, 0, /* fill in spid */
4231 0, 0xa, 0, 0,
4233 /* OPT */ 0x43, 0, 0xb, 0xf0, 0x18, 0, 0, 0,
4234 0x7f, 0, 4, 1, 4, 1, /* bool LockAgainstGrouping 127 = 0x1040104; */
4235 0xbf, 0, 8, 0, 8, 0, /* bool fFitTextToShape 191 = 0x80008; */
4236 0xff, 1, 0, 0, 8, 0, /* bool fNoLineDrawDash 511 = 0x80000; */
4237 0xbf, 3, 0, 0, 2, 0, /* bool fPrint 959 = 0x20000; */
4239 /* ClientAnchor */ 0, 0, 0x10, 0xf0, 0x12, 0, 0, 0, 1,0,
4240 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
4241 /* ClientData */ 0, 0, 0x11, 0xf0, 0, 0, 0, 0
4244 guint8 *data, buf [sizeof obj_v8];
4245 GnmFilter const *filter;
4246 GnmFilterCondition const *cond;
4247 BiffPut *bp = esheet->ewb->bp;
4248 unsigned i;
4249 SheetObjectAnchor anchor;
4250 GnmRange r;
4251 gsize draw_len = 0;
4253 if (esheet->gnum_sheet->filters == NULL)
4254 return 0;
4255 filter = esheet->gnum_sheet->filters->data;
4256 r.end.row = 1 + (r.start.row = filter->r.start.row);
4258 /* write combos for the fields */
4259 for (i = 0; i < filter->fields->len ; i++) {
4260 cond = gnm_filter_get_condition (filter, i);
4262 r.end.col = 1 + (r.start.col = filter->r.start.col + i);
4263 sheet_object_anchor_init (&anchor, &r, NULL,
4264 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4265 if (bp->version >= MS_BIFF_V8) {
4266 guint32 id = excel_write_start_drawing (esheet);
4267 memcpy (buf, obj_v8, sizeof obj_v8);
4268 GSF_LE_SET_GUINT32 (buf + 16, id);
4269 excel_write_anchor (buf + 66, &anchor);
4270 ms_biff_put_var_write (bp, buf, sizeof obj_v8);
4271 draw_len += sizeof (obj_v8);
4272 ms_biff_put_commit (bp);
4274 ms_biff_put_var_next (bp, BIFF_OBJ);
4275 /* autofill, locked, with undocumented flag 0x100 that
4276 * I am guessing is tied to the fact that XL created
4277 * this. not the user*/
4278 ms_objv8_write_common (bp,
4279 esheet->cur_obj, MSOT_COMBO, 0x2101);
4280 ms_objv8_write_scrollbar_old (bp);
4281 ms_objv8_write_listbox (bp, 3, cond != NULL); /* acts as an end */
4282 } else {
4283 data = ms_biff_put_len_next (bp, BIFF_OBJ, sizeof std_obj_v7);
4284 memcpy (data, std_obj_v7, sizeof std_obj_v7);
4286 esheet->cur_obj++;
4287 GSF_LE_SET_GUINT32 (data + 0, esheet->cur_obj);
4288 GSF_LE_SET_GUINT16 (data + 6, esheet->cur_obj);
4289 excel_write_anchor (data + 10, &anchor);
4290 if (cond != NULL)
4291 GSF_LE_SET_GUINT16 (data + 124, 0xa);
4293 /* No drawing here, it seems. */
4295 ms_biff_put_commit (bp);
4298 return draw_len;
4301 static gsize
4302 excel_write_chart_v8 (ExcelWriteSheet *esheet, SheetObject *so)
4304 BiffPut *bp = esheet->ewb->bp;
4305 GString *escher = g_string_new (NULL);
4306 GString *extra;
4307 guint32 spflags;
4308 gsize spmark, optmark;
4309 guint32 id = excel_write_start_drawing (esheet);
4310 char *name;
4311 guint8 zero[4] = { 0, 0, 0, 0 };
4312 gsize draw_len = 0;
4313 guint16 shape = 0x92;
4315 g_object_get (so,
4316 "name", &name,
4317 NULL);
4319 spmark = ms_escher_spcontainer_start (escher);
4320 spflags = 0x00000a00; /* fHaveAnchor+fHaveSpt */
4321 ms_escher_sp (escher, id, shape, spflags);
4323 optmark = ms_escher_opt_start (escher);
4324 extra = g_string_new (NULL);
4325 ms_escher_opt_add_bool (escher, optmark, MSEP_LOCKAGAINSTGROUPING, TRUE);
4326 ms_escher_opt_add_bool (escher, optmark, MSEP_FITTEXTTOSHAPE, TRUE);
4327 ms_escher_opt_add_color (escher, optmark, MSEP_FILLCOLOR, 0x0800004);
4328 ms_escher_opt_add_color (escher, optmark, MSEP_FILLBACKCOLOR, 0x0800004d);
4329 ms_escher_opt_add_bool (escher, optmark, MSEP_NOFILLHITTEST, TRUE);
4330 ms_escher_opt_add_color (escher, optmark, MSEP_LINECOLOR, 0x0800004d);
4331 ms_escher_opt_add_bool (escher, optmark, MSEP_NOLINEDRAWDASH, TRUE);
4332 ms_escher_opt_add_bool (escher, optmark, MSEP_SHADOWOBSCURED, TRUE);
4333 if (name)
4334 ms_escher_opt_add_str_wchar (escher, optmark, extra,
4335 MSEP_NAME, name);
4337 go_string_append_gstring (escher, extra);
4338 ms_escher_opt_end (escher, optmark);
4339 g_string_free (extra, TRUE);
4341 ms_escher_clientanchor (escher, sheet_object_get_anchor (so));
4343 ms_escher_clientdata (escher);
4345 ms_escher_spcontainer_end (escher, spmark);
4347 ms_biff_put_var_write (bp, escher->str, escher->len);
4348 ms_biff_put_commit (bp);
4349 draw_len += escher->len;
4351 g_string_free (escher, TRUE);
4353 ms_biff_put_var_next (bp, BIFF_OBJ);
4354 ms_objv8_write_common (bp, esheet->cur_obj, MSOT_CHART, 0x6011);
4355 ms_biff_put_var_write (bp, zero, 4);
4356 ms_biff_put_commit (bp);
4358 ms_excel_chart_write (esheet->ewb, so);
4360 g_free (name);
4362 return draw_len;
4365 /* Return NULL when we cannot export. The NULL will be added to the list to
4366 * indicate that we shouldn't write a DRAWING record for the corresponding
4367 * image. */
4368 static BlipInf *
4369 blipinf_new (SheetObjectImage *soi)
4371 BlipInf *blip;
4372 GOImage *image;
4373 char const *blip_type;
4375 blip = g_new0 (BlipInf, 1);
4376 blip->uncomp_len = -1;
4377 blip->needs_free = FALSE;
4378 blip->so = GNM_SO (soi);
4380 g_object_get (G_OBJECT (soi),
4381 "image-type", &blip->type,
4382 "image", &image,
4383 NULL);
4384 if (image) {
4385 gsize len;
4386 blip->bytes.data = (gpointer)go_image_get_data (image, &len);
4387 blip->bytes.len = len;
4388 } else {
4389 blip->bytes.data = NULL;
4390 blip->bytes.len = 0;
4392 blip_type = blip->type ? blip->type : "?";
4393 g_object_unref (image);
4395 if (strcmp (blip_type, "jpeg") == 0 || /* Raster format */
4396 strcmp (blip_type, "png") == 0 || /* understood by Excel */
4397 strcmp (blip_type, "dib") == 0) {
4398 blip->header_len = BSE_HDR_LEN + RASTER_BLIP_HDR_LEN;
4399 } else if (strcmp (blip_type, "wmf") == 0 || /* Vector format */
4400 strcmp (blip_type, "emf") == 0 || /* - compress */
4401 strcmp (blip_type, "pict") == 0) {
4403 int res;
4404 gulong dest_len = blip->bytes.len * 1.01 + 12;
4405 guint8 *buffer = g_malloc (dest_len);
4407 blip->uncomp_len = blip->bytes.len;
4408 res = compress (buffer, &dest_len,
4409 blip->bytes.data, blip->bytes.len);
4410 if (res != Z_OK) {
4411 g_free (buffer);
4412 g_warning ("compression failure %d;", res);
4413 } else {
4414 blip->needs_free = TRUE;
4415 blip->bytes.data = buffer;
4416 blip->bytes.len = dest_len;
4418 blip->header_len = BSE_HDR_LEN + VECTOR_BLIP_HDR_LEN;
4419 } else {
4420 /* Fall back to png */
4421 GdkPixbuf *pixbuf;
4422 char *buffer = NULL;
4424 g_object_get (G_OBJECT (soi), "pixbuf", &pixbuf, NULL);
4426 if (pixbuf) {
4427 gsize len;
4428 gdk_pixbuf_save_to_buffer (pixbuf,
4429 &buffer,
4430 &len,
4431 "png",
4432 NULL,
4433 NULL);
4434 blip->bytes.len = len;
4435 g_object_unref (pixbuf);
4438 if (buffer) {
4439 g_free (blip->type);
4440 blip->type = g_strdup ("png");
4441 blip->bytes.data = buffer;
4442 blip->needs_free = TRUE;
4443 blip->header_len = BSE_HDR_LEN + RASTER_BLIP_HDR_LEN;
4444 } else {
4445 g_warning
4446 ("Unable to export %s image as png to Excel",
4447 blip_type);
4448 g_free (blip);
4449 blip = NULL;
4453 return blip;
4456 static void
4457 blipinf_free (BlipInf *blip)
4459 if (blip) { /* It is not a bug if blip == NULL */
4460 g_free (blip->type);
4461 if (blip->needs_free) {
4462 g_free (blip->bytes.data);
4463 blip->needs_free = FALSE;
4465 blip->bytes.data = NULL;
4466 g_free (blip);
4470 static void
4471 excel_write_string_in_chunks (BiffPut *bp, const char *label)
4473 size_t maxlen_ascii = ms_biff_max_record_len (bp) - 1;
4474 size_t maxlen_nonascii = maxlen_ascii / 2;
4476 do {
4477 const char *s = label;
4478 size_t thislen = 0;
4479 gboolean ascii = TRUE;
4480 const char *cut = NULL;
4481 char *copy = NULL;
4483 while (*s) {
4484 thislen++;
4485 if ((guchar)*s < 0x80) {
4486 s++;
4487 } else {
4488 ascii = FALSE;
4489 s = g_utf8_next_char (s);
4492 if (thislen == maxlen_nonascii)
4493 cut = s;
4495 if (thislen >= (ascii ? maxlen_ascii : maxlen_nonascii))
4496 break;
4499 if (ascii || !cut)
4500 cut = s;
4502 if (*cut) {
4503 copy = g_malloc (cut - label + 1);
4504 memcpy (copy, label, cut - label);
4505 copy[cut - label] = 0;
4508 ms_biff_put_var_next (bp, BIFF_CONTINUE);
4509 excel_write_string (bp, STR_NO_LENGTH, copy ? copy : label);
4510 ms_biff_put_commit (bp);
4512 g_free (copy);
4513 label = cut;
4514 } while (*label);
4517 static gsize
4518 excel_write_ClientTextbox (ExcelWriteState *ewb, SheetObject *so,
4519 const char *label)
4521 guint8 buf[18];
4522 int txo_len = 18;
4523 int draw_len = 0;
4524 int char_len;
4525 size_t byte_len;
4526 int markuplen;
4527 BiffPut *bp = ewb->bp;
4528 GArray *markup = g_hash_table_lookup (ewb->cell_markup, so);
4530 ms_biff_put_var_next (bp, BIFF_MS_O_DRAWING);
4531 memset (buf, 0, 8);
4532 GSF_LE_SET_GUINT16 (buf + 2, 0xf00d); /* ClientTextbox */
4533 ms_biff_put_var_write (bp, buf, 8);
4534 draw_len += 8;
4535 ms_biff_put_commit (bp);
4537 ms_biff_put_var_next (bp, BIFF_TXO);
4538 memset (buf, 0, txo_len);
4539 GSF_LE_SET_GUINT16 (buf, 0x212); /* end */
4540 if (!label) {
4541 g_warning ("Not sure why label is NULL here for %s at %p.",
4542 g_type_name (G_OBJECT_TYPE (so)), so);
4543 label = "?";
4544 } else if (label[0] == 0) {
4545 /* XL gets very unhappy with empty strings. */
4546 label = " ";
4548 char_len = excel_strlen (label, &byte_len);
4549 GSF_LE_SET_GUINT16 (buf + 10, char_len);
4550 if (markup)
4551 markuplen = 8 + markup->len * 4;
4552 else
4553 markuplen = 16;
4554 GSF_LE_SET_GUINT16 (buf + 12, markuplen);
4555 ms_biff_put_var_write (bp, buf, txo_len);
4556 ms_biff_put_commit (bp);
4558 excel_write_string_in_chunks (bp, label);
4560 ms_biff_put_var_next (bp, BIFF_CONTINUE);
4561 memset (buf, 0, 8);
4562 if (markup && markup->len > 0) {
4563 int n = markup->len / 2;
4564 int i;
4566 for (i = 0; i < n ; i++) {
4567 gint bpos, cpos;
4569 bpos = g_array_index (markup, gint, i*2);
4570 bpos = CLAMP (bpos, 0, (int)byte_len - 1);
4571 cpos = g_utf8_pointer_to_offset (label, label + bpos);
4572 GSF_LE_SET_GUINT16 (buf, cpos);
4573 GSF_LE_SET_GUINT16 (buf + 2,
4574 g_array_index (markup, gint, i*2+1));
4575 ms_biff_put_var_write (ewb->bp, buf, 8);
4577 } else {
4578 ms_biff_put_var_write (ewb->bp, buf, 8);
4580 memset (buf, 0, 8);
4581 GSF_LE_SET_GUINT16 (buf, char_len);
4582 ms_biff_put_var_write (bp, buf, 8);
4583 ms_biff_put_commit (bp);
4585 return draw_len;
4588 static gsize
4589 excel_write_other_v8 (ExcelWriteSheet *esheet,
4590 SheetObject *so)
4592 GString *escher = g_string_new (NULL);
4593 GString *extra;
4594 ExcelWriteState *ewb = esheet->ewb;
4595 BiffPut *bp = ewb->bp;
4596 guint32 id = excel_write_start_drawing (esheet);
4597 SheetObjectAnchor const *real_anchor = sheet_object_get_anchor (so);
4598 SheetObjectAnchor anchor = *real_anchor;
4599 gsf_off_t sppos;
4600 guint32 splen;
4601 gsize spmark, optmark;
4602 guint16 flags;
4603 int type;
4604 guint16 shape = 0xca; /* Textbox */
4605 gboolean do_textbox;
4606 gsize draw_len = 0;
4607 char *name, *label;
4608 GOStyle *style = NULL;
4609 gboolean checkbox_active = FALSE;
4610 GnmNamedExpr *macro_nexpr;
4611 gboolean terminate_obj = TRUE;
4612 gboolean transparent = FALSE;
4613 gboolean is_image = FALSE;
4614 gboolean has_text_prop = NULL != g_object_class_find_property
4615 (G_OBJECT_GET_CLASS (so), "text");
4616 gboolean is_widget = GNM_IS_SOW (so);
4618 if (has_text_prop) {
4619 g_object_get (so,
4620 "name", &name,
4621 "text", &label,
4622 NULL);
4623 } else {
4624 label = NULL;
4625 g_object_get (so, "name", &name, NULL);
4627 do_textbox = (label != NULL && label[0] != 0);
4629 macro_nexpr = is_widget
4630 ? g_hash_table_lookup (esheet->widget_macroname, so)
4631 : NULL;
4633 if (anchor.mode != GNM_SO_ANCHOR_TWO_CELLS) {
4634 double pts[4];
4635 GnmSOAnchorMode mode = anchor.mode;
4636 sheet_object_anchor_to_pts (&anchor, esheet->gnum_sheet, pts);
4637 anchor.mode = GNM_SO_ANCHOR_TWO_CELLS;
4638 sheet_object_pts_to_anchor (&anchor, esheet->gnum_sheet, pts);
4639 anchor.mode = mode; /* this anchor is not valid for gnumeric but is what we need there */
4641 if (GNM_IS_CELL_COMMENT (so)) {
4642 static double const offset[4] = { .5, .5, .5, .5 };
4643 GnmRange r;
4645 r.start = real_anchor->cell_bound.start;
4646 r.start.col++;
4647 r.start.row--;
4648 r.end.col = r.start.col + 2;
4649 r.end.row = r.start.row + 4;
4650 sheet_object_anchor_init (&anchor, &r, offset,
4651 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4652 type = MSOT_COMMENT;
4653 flags = 0x4011; /* not autofilled */
4654 do_textbox = TRUE;
4655 g_hash_table_insert (esheet->commentshash,
4656 so, GINT_TO_POINTER (esheet->cur_obj));
4658 } else if (GNM_IS_SO_FILLED (so)) {
4659 gboolean is_oval;
4661 type = MSOT_TEXTBOX;
4662 flags = 0x6011; /* autofilled */
4664 g_object_get (so,
4665 "is-oval", &is_oval,
4666 "style", &style,
4667 NULL);
4668 if (is_oval) {
4669 shape = 3;
4670 type = MSOT_OVAL;
4671 } else if (!do_textbox) {
4672 shape = 1;
4673 type = MSOT_RECTANGLE;
4675 transparent = !style->fill.auto_type && style->fill.type == GO_STYLE_FILL_NONE;
4676 } else if (GNM_IS_SOW_CHECKBOX (so)) {
4677 shape = 0xc9;
4678 type = MSOT_CHECKBOX;
4679 flags = 0x0011;
4680 g_object_get (so, "active", &checkbox_active, NULL);
4681 } else if (GNM_IS_SOW_RADIO_BUTTON (so)) {
4682 shape = 0xc9;
4683 type = MSOT_OPTION;
4684 flags = 0x0011;
4685 g_object_get (so, "active", &checkbox_active, NULL);
4686 } else if (GNM_IS_SOW_SPINBUTTON (so)) {
4687 shape = 0xc9;
4688 type = MSOT_SPINNER;
4689 flags = 0x0011;
4690 } else if (GNM_IS_SOW_SCROLLBAR (so) || GNM_IS_SOW_SLIDER (so)) {
4691 /* Pretend sliders are scrollbars. */
4692 shape = 0xc9;
4693 type = MSOT_SCROLLBAR;
4694 flags = 0x6011;
4695 } else if (GNM_IS_SOW_LIST (so)) {
4696 shape = 0xc9;
4697 type = MSOT_LIST;
4698 flags = 0x2011;
4699 } else if (GNM_IS_SOW_BUTTON (so)) {
4700 shape = 0xc9;
4701 type = MSOT_BUTTON;
4702 flags = 0x0011;
4703 } else if (GNM_IS_SOW_COMBO (so)) {
4704 shape = 0xc9;
4705 type = MSOT_COMBO;
4706 flags = 0x2011;
4707 } else if (GNM_IS_SO_IMAGE (so)) {
4708 shape = 0x4b;
4709 type = MSOT_PICTURE;
4710 flags = 0x6011;
4711 is_image = TRUE;
4712 } else {
4713 g_assert_not_reached ();
4714 return 0;
4717 spmark = ms_escher_spcontainer_start (escher);
4719 ms_escher_sp (escher, id, shape, 0x0a00); /* fHaveAnchor+fHaveSpt */
4721 optmark = ms_escher_opt_start (escher);
4722 extra = g_string_new (NULL);
4723 if (is_widget)
4724 ms_escher_opt_add_bool (escher, optmark,
4725 MSEP_LOCKROTATION, TRUE);
4726 if (is_image)
4727 ms_escher_opt_add_bool (escher, optmark,
4728 MSEP_LOCKASPECTRATIO, TRUE);
4729 if (has_text_prop) {
4730 ms_escher_opt_add_simple (escher, optmark,
4731 MSEP_TXID, 0x00c600a0);
4732 ms_escher_opt_add_simple (escher, optmark,
4733 MSEP_WRAPTEXT, 1);
4734 ms_escher_opt_add_simple (escher, optmark,
4735 MSEP_TXDIR, 2);
4736 ms_escher_opt_add_bool (escher, optmark, MSEP_SELECTTEXT, TRUE);
4738 if (is_widget) {
4739 ms_escher_opt_add_bool (escher, optmark, MSEP_AUTOTEXTMARGIN, FALSE);
4740 ms_escher_opt_add_bool (escher, optmark, MSEP_SHADOWOK, TRUE);
4741 ms_escher_opt_add_bool (escher, optmark, MSEP_LINEOK, TRUE);
4742 ms_escher_opt_add_bool (escher, optmark, MSEP_FILLOK, TRUE);
4743 } else if (transparent)
4744 ms_escher_opt_add_bool (escher, optmark, MSEP_FILLOK, FALSE);
4745 if (is_image) {
4746 guint32 blip_id = ++(ewb->cur_blip);
4747 ms_escher_opt_add_simple (escher, optmark, MSEP_BLIPINDEX, blip_id);
4749 ms_escher_opt_add_color (escher, optmark, MSEP_FILLCOLOR,
4750 style == NULL || style->fill.auto_back
4751 ? GO_COLOR_WHITE
4752 : style->fill.pattern.back);
4753 if (is_widget || transparent)
4754 ms_escher_opt_add_bool (escher, optmark, MSEP_FILLED, FALSE);
4755 ms_escher_opt_add_bool (escher, optmark, MSEP_NOFILLHITTEST, TRUE);
4756 ms_escher_opt_add_color (escher, optmark, MSEP_LINECOLOR,
4757 style == NULL || style->line.auto_color
4758 ? GO_COLOR_BLACK
4759 : style->line.color);
4760 if (style && style->line.width > 0) {
4761 gint32 w = CLAMP (12700 * style->line.width, 0, G_MAXINT32);
4762 ms_escher_opt_add_simple (escher, optmark, MSEP_LINEWIDTH, w);
4764 if (style) {
4765 int d = ms_escher_line_type_to_xl (style->line.dash_type);
4766 if (d >= 0)
4767 ms_escher_opt_add_simple (escher, optmark,
4768 MSEP_LINEDASHING, d);
4769 else
4770 ms_escher_opt_add_bool (escher, optmark, MSEP_LINE, FALSE);
4772 if (is_widget)
4773 ms_escher_opt_add_bool (escher, optmark, MSEP_LINE, FALSE);
4774 if (name)
4775 ms_escher_opt_add_str_wchar (escher, optmark, extra,
4776 MSEP_NAME, name);
4777 ms_escher_opt_add_bool (escher, optmark, MSEP_ISBUTTON, TRUE);
4778 go_string_append_gstring (escher, extra);
4779 ms_escher_opt_end (escher, optmark);
4780 g_string_free (extra, TRUE);
4782 ms_escher_clientanchor (escher, &anchor);
4784 ms_escher_clientdata (escher);
4786 /* At this point we're still missing the textbox below. */
4788 ms_escher_spcontainer_end (escher, spmark);
4790 sppos = bp->streamPos + bp->curpos + 4;
4791 splen = GSF_LE_GET_GUINT32 (escher->str + spmark + 4);
4793 draw_len += escher->len;
4795 ms_biff_put_var_write (bp, escher->str, escher->len);
4796 ms_biff_put_commit (bp);
4798 g_string_free (escher, TRUE);
4800 /* ---------------------------------------- */
4802 ms_biff_put_var_next (bp, BIFF_OBJ);
4803 ms_objv8_write_common (bp, esheet->cur_obj, type, flags);
4805 switch (type) {
4806 case MSOT_BUTTON: {
4807 ms_objv8_write_button (bp, esheet, macro_nexpr);
4808 break;
4810 case MSOT_CHECKBOX: {
4811 GnmExprTop const *link = sheet_widget_checkbox_get_link (so);
4812 ms_objv8_write_checkbox (bp,
4813 checkbox_active,
4814 esheet,
4815 link,
4816 macro_nexpr);
4817 if (link) gnm_expr_top_unref (link);
4818 break;
4820 case MSOT_OPTION: {
4821 GnmExprTop const *link = sheet_widget_radio_button_get_link (so);
4822 ms_objv8_write_radiobutton (bp,
4823 checkbox_active,
4824 esheet,
4825 link,
4826 macro_nexpr);
4827 if (link) gnm_expr_top_unref (link);
4828 break;
4830 case MSOT_SPINNER: {
4831 GnmExprTop const *link = sheet_widget_adjustment_get_link (so);
4832 GtkAdjustment *adj =
4833 sheet_widget_adjustment_get_adjustment (so);
4834 gboolean horiz = sheet_widget_adjustment_get_horizontal (so);
4835 ms_objv8_write_spinbutton (bp,
4836 esheet,
4837 adj, horiz,
4838 link,
4839 macro_nexpr);
4840 if (link) gnm_expr_top_unref (link);
4841 break;
4843 case MSOT_SCROLLBAR: {
4844 GnmExprTop const *link = sheet_widget_adjustment_get_link (so);
4845 GtkAdjustment *adj =
4846 sheet_widget_adjustment_get_adjustment (so);
4847 gboolean horiz = sheet_widget_adjustment_get_horizontal (so);
4848 ms_objv8_write_scrollbar (bp,
4849 esheet,
4850 adj, horiz,
4851 link,
4852 macro_nexpr);
4853 if (link) gnm_expr_top_unref (link);
4854 break;
4856 case MSOT_LIST:
4857 case MSOT_COMBO: {
4858 GnmExprTop const *res_link =
4859 sheet_widget_list_base_get_result_link (so);
4860 GnmExprTop const *data_link =
4861 sheet_widget_list_base_get_content_link (so);
4862 GtkAdjustment *adj =
4863 sheet_widget_list_base_get_adjustment (so);
4864 ms_objv8_write_list (bp,
4865 esheet,
4866 adj, res_link, data_link,
4867 macro_nexpr);
4868 if (res_link) gnm_expr_top_unref (res_link);
4869 if (data_link) gnm_expr_top_unref (data_link);
4870 g_object_unref (adj);
4871 terminate_obj = FALSE; /* GR_LISTBOX_DATA is strange */
4872 break;
4874 case MSOT_COMMENT:
4875 /* Cell comment. */
4876 ms_objv8_write_note (bp);
4877 break;
4878 default:
4879 /* Nothing */
4880 break;
4883 if (terminate_obj) {
4884 guint8 zero[4] = { 0, 0, 0, 0 };
4885 ms_biff_put_var_write (bp, zero, 4);
4887 ms_biff_put_commit (bp);
4889 /* ---------------------------------------- */
4891 if (do_textbox) {
4892 guint8 spbuf[4];
4893 gsize this_len = excel_write_ClientTextbox (ewb, so, label);
4894 draw_len += this_len;
4895 splen += this_len;
4896 GSF_LE_SET_GUINT32 (spbuf, splen);
4897 ms_biff_put_abs_write (bp, sppos + 4, spbuf, 4);
4900 g_free (name);
4901 g_free (label);
4902 if (style) g_object_unref (style);
4904 return draw_len;
4907 static void
4908 write_arrow (GOArrow const *arrow, double width, GString *escher, gsize optmark,
4909 guint id)
4911 XLArrowType typ;
4912 int l, w;
4914 xls_arrow_to_xl (arrow, width, &typ, &l, &w);
4916 switch (id) {
4917 case MSEP_LINESTARTARROWHEAD:
4918 case MSEP_LINEENDARROWHEAD:
4919 ms_escher_opt_add_simple (escher, optmark, id, typ);
4920 break;
4921 case MSEP_LINESTARTARROWWIDTH:
4922 case MSEP_LINEENDARROWWIDTH:
4923 if (typ && w != 1)
4924 ms_escher_opt_add_simple (escher, optmark, id, w);
4925 break;
4926 case MSEP_LINESTARTARROWLENGTH:
4927 case MSEP_LINEENDARROWLENGTH:
4928 if (typ && l != 1)
4929 ms_escher_opt_add_simple (escher, optmark, id, l);
4930 break;
4931 default:
4932 g_assert_not_reached ();
4937 static gsize
4938 excel_write_line_v8 (ExcelWriteSheet *esheet, SheetObject *so)
4940 GString *escher = g_string_new (NULL);
4941 GString *extra;
4942 ExcelWriteState *ewb = esheet->ewb;
4943 BiffPut *bp = ewb->bp;
4944 guint32 id = excel_write_start_drawing (esheet);
4945 gsize draw_len = 0;
4946 int type = MSOT_LINE;
4947 int shape = 0x14;
4948 int flags = 0x6011; /* autofilled */
4949 gsize spmark, optmark;
4950 char *name;
4951 guint8 zero[4] = { 0, 0, 0, 0 };
4952 GOStyle *style;
4953 GOArrow *start_arrow, *end_arrow;
4954 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
4955 guint32 spflags;
4956 double width;
4958 g_object_get (so,
4959 "start-arrow", &start_arrow,
4960 "end-arrow", &end_arrow,
4961 "name", &name,
4962 "style", &style,
4963 NULL);
4965 spmark = ms_escher_spcontainer_start (escher);
4967 spflags = 0x00000a00; /* fHaveAnchor+fHaveSpt */
4968 if ((anchor->base.direction & GOD_ANCHOR_DIR_H_MASK) == 0)
4969 spflags |= 0x40;
4970 if ((anchor->base.direction & GOD_ANCHOR_DIR_V_MASK) == 0)
4971 spflags |= 0x80;
4972 ms_escher_sp (escher, id, shape, spflags);
4974 optmark = ms_escher_opt_start (escher);
4975 extra = g_string_new (NULL);
4976 ms_escher_opt_add_bool (escher, optmark, MSEP_AUTOTEXTMARGIN, TRUE);
4977 ms_escher_opt_add_simple (escher, optmark, MSEP_SHAPEPATH, 4);
4978 ms_escher_opt_add_bool (escher, optmark, MSEP_FILLOK, FALSE);
4979 ms_escher_opt_add_bool (escher, optmark, MSEP_NOFILLHITTEST, TRUE);
4980 ms_escher_opt_add_bool (escher, optmark, MSEP_FILLED, FALSE);
4981 ms_escher_opt_add_color (escher, optmark, MSEP_LINECOLOR,
4982 style->line.auto_color
4983 ? GO_COLOR_BLACK
4984 : style->line.color);
4985 if (style->line.width > 0) {
4986 gint32 w = CLAMP (12700 * style->line.width, 0, G_MAXINT32);
4987 ms_escher_opt_add_simple (escher, optmark, MSEP_LINEWIDTH, w);
4989 if (!style->line.auto_dash) {
4990 int d = ms_escher_line_type_to_xl (style->line.dash_type);
4991 if (d >= 0)
4992 ms_escher_opt_add_simple (escher, optmark,
4993 MSEP_LINEDASHING, d);
4996 /* The two arrows' attributes are interleaved. */
4997 width = style->line.auto_width ? 0 : style->line.width;
4998 write_arrow (start_arrow, width, escher, optmark, MSEP_LINESTARTARROWHEAD);
4999 write_arrow (end_arrow, width, escher, optmark, MSEP_LINEENDARROWHEAD);
5000 write_arrow (start_arrow, width, escher, optmark, MSEP_LINESTARTARROWWIDTH);
5001 write_arrow (start_arrow, width, escher, optmark, MSEP_LINESTARTARROWLENGTH);
5002 write_arrow (end_arrow, width, escher, optmark, MSEP_LINEENDARROWWIDTH);
5003 write_arrow (end_arrow, width, escher, optmark, MSEP_LINEENDARROWLENGTH);
5004 ms_escher_opt_add_bool (escher, optmark, MSEP_ARROWHEADSOK, TRUE);
5006 if (name)
5007 ms_escher_opt_add_str_wchar (escher, optmark, extra,
5008 MSEP_NAME, name);
5009 ms_escher_opt_add_bool (escher, optmark, MSEP_ISBUTTON, TRUE);
5010 go_string_append_gstring (escher, extra);
5011 ms_escher_opt_end (escher, optmark);
5012 g_string_free (extra, TRUE);
5014 ms_escher_clientanchor (escher, anchor);
5016 ms_escher_clientdata (escher);
5018 ms_escher_spcontainer_end (escher, spmark);
5020 ms_biff_put_var_write (bp, escher->str, escher->len);
5021 ms_biff_put_commit (bp);
5022 draw_len += escher->len;
5024 g_string_free (escher, TRUE);
5026 ms_biff_put_var_next (bp, BIFF_OBJ);
5027 ms_objv8_write_common (bp, esheet->cur_obj, type, flags);
5028 ms_biff_put_var_write (bp, zero, 4);
5030 ms_biff_put_commit (bp);
5032 g_free (name);
5033 g_object_unref (style);
5034 g_free (start_arrow);
5035 g_free (end_arrow);
5037 return draw_len;
5040 static gsize
5041 excel_write_obj_v8 (ExcelWriteSheet *esheet, SheetObject *so)
5043 if (GNM_IS_SO_GRAPH (so))
5044 return excel_write_chart_v8 (esheet, so);
5045 if (GNM_IS_SO_LINE (so))
5046 return excel_write_line_v8 (esheet, so);
5047 return excel_write_other_v8 (esheet, so);
5050 static void
5051 excel_write_DIMENSION (BiffPut *bp, ExcelWriteSheet *esheet)
5053 guint8 *data;
5054 if (bp->version >= MS_BIFF_V8) {
5055 data = ms_biff_put_len_next (bp, BIFF_DIMENSIONS_v2, 14);
5056 GSF_LE_SET_GUINT32 (data + 0, 0);
5057 GSF_LE_SET_GUINT32 (data + 4, esheet->max_row);
5058 GSF_LE_SET_GUINT16 (data + 8, 0);
5059 GSF_LE_SET_GUINT16 (data + 10, esheet->max_col);
5060 GSF_LE_SET_GUINT16 (data + 12, 0x0000);
5061 } else {
5062 data = ms_biff_put_len_next (bp, BIFF_DIMENSIONS_v2, 10);
5063 GSF_LE_SET_GUINT16 (data + 0, 0);
5064 GSF_LE_SET_GUINT16 (data + 2, esheet->max_row);
5065 GSF_LE_SET_GUINT16 (data + 4, 0);
5066 GSF_LE_SET_GUINT16 (data + 6, esheet->max_col);
5067 GSF_LE_SET_GUINT16 (data + 8, 0x0000);
5069 ms_biff_put_commit (bp);
5072 static void
5073 excel_write_COUNTRY (BiffPut *bp)
5075 guint8 *data = ms_biff_put_len_next (bp, BIFF_COUNTRY, 4);
5076 GSF_LE_SET_GUINT16 (data, 1); /* flag as made in US */
5077 GSF_LE_SET_GUINT16 (data+2, 1);
5078 ms_biff_put_commit (bp);
5081 static void
5082 excel_write_WSBOOL (BiffPut *bp, ExcelWriteSheet *esheet)
5084 guint16 flags = 0;
5086 /* 0x0001 automatic page breaks are visible */
5087 flags |= 1;
5088 /* 0x0010 the sheet is a dialog sheet */
5089 /* 0x0020 automatic styles are not applied to an outline */
5090 if (esheet->gnum_sheet->outline_symbols_below) flags |= 0x040;
5091 if (esheet->gnum_sheet->outline_symbols_right) flags |= 0x080;
5092 if (esheet->gnum_sheet->print_info &&
5093 esheet->gnum_sheet->print_info->scaling.type == PRINT_SCALE_FIT_PAGES)
5094 flags |= 0x100;
5095 if (esheet->gnum_sheet->display_outlines) flags |= 0x400;
5097 ms_biff_put_2byte (bp, BIFF_WSBOOL, flags);
5100 static void
5101 excel_write_PAGE_BREAK (BiffPut *bp, GnmPageBreaks const *breaks)
5103 unsigned i, n, step = (bp->version < MS_BIFF_V8) ? 2 : 6;
5104 GnmPageBreak const *binfo;
5105 guint8 *data;
5106 GnmPageBreaks *manual_pbreaks = gnm_page_breaks_dup_non_auto_breaks (breaks);
5107 GArray *details = manual_pbreaks->details;
5108 guint16 const maxima = (guint16)(manual_pbreaks->is_vert ? XLS_MaxRow_V8 : XLS_MaxCol);
5110 /* limit size to ensure no CONTINUE (do we need this ? ) */
5111 if (((n = details->len)*step + 2 + 2) >= ms_biff_max_record_len (bp))
5112 n = (ms_biff_max_record_len (bp) - 2 - 2) / step;
5114 data = ms_biff_put_len_next (bp, manual_pbreaks->is_vert
5115 ? BIFF_VERTICALPAGEBREAKS : BIFF_HORIZONTALPAGEBREAKS,
5116 2 + step * n);
5118 GSF_LE_SET_GUINT16 (data, (guint16)n);
5119 for (data += 2, i = 0 ; i < n ; data += step, i++) {
5120 binfo = &g_array_index (details, GnmPageBreak, i);
5121 GSF_LE_SET_GUINT16 (data, (guint16)binfo->pos);
5122 if (step > 2) {
5123 /* seems to be constant */
5124 GSF_LE_SET_GUINT16 (data + 2, 0);
5125 GSF_LE_SET_GUINT16 (data + 4, maxima);
5129 ms_biff_put_commit (bp);
5130 gnm_page_breaks_free (manual_pbreaks);
5133 static void
5134 excel_write_HEADER_FOOTER (BiffPut *bp, GnmPrintHF const *hf, int id)
5136 char *s = xls_header_footer_export (hf);
5138 ms_biff_put_var_next (bp, id);
5139 excel_write_string (bp,
5140 (bp->version >= MS_BIFF_V8
5141 ? STR_TWO_BYTE_LENGTH
5142 : STR_ONE_BYTE_LENGTH),
5144 ms_biff_put_commit (bp);
5146 g_free (s);
5149 static void
5150 write_sheet_head (BiffPut *bp, ExcelWriteSheet *esheet)
5152 guint8 *data;
5153 GnmPrintInformation *pi;
5154 Sheet const *sheet = esheet->gnum_sheet;
5155 Workbook const *wb = sheet->workbook;
5156 double left;
5157 double right;
5158 double edge_to_below_header;
5159 double edge_to_above_footer;
5161 pi = sheet->print_info;
5162 g_return_if_fail (pi != NULL);
5164 ms_biff_put_2byte (bp, BIFF_CALCMODE, wb->recalc_auto ? 1 : 0);
5165 ms_biff_put_2byte (bp, BIFF_CALCCOUNT, wb->iteration.max_number);
5166 ms_biff_put_2byte (bp, BIFF_REFMODE, sheet->convs->r1c1_addresses ? 0 : 1);
5167 ms_biff_put_2byte (bp, BIFF_ITERATION, wb->iteration.enabled ? 1 : 0);
5169 data = ms_biff_put_len_next (bp, BIFF_DELTA, 8);
5170 gsf_le_set_double (data, wb->iteration.tolerance);
5171 ms_biff_put_commit (bp);
5173 ms_biff_put_2byte (bp, BIFF_SAVERECALC, 0x0001);
5174 ms_biff_put_2byte (bp, BIFF_PRINTHEADERS, pi->print_titles ? 1 : 0);
5175 ms_biff_put_2byte (bp, BIFF_PRINTGRIDLINES, pi->print_grid_lines ? 1 : 0);
5176 ms_biff_put_2byte (bp, BIFF_GRIDSET, 0x0001);
5178 excel_write_GUTS (bp, esheet);
5179 excel_write_DEFAULT_ROW_HEIGHT (bp, esheet);
5180 if (bp->version < MS_BIFF_V8)
5181 excel_write_COUNTRY (bp);
5182 excel_write_WSBOOL (bp, esheet);
5184 if (pi->page_breaks.h)
5185 excel_write_PAGE_BREAK (bp, pi->page_breaks.h);
5186 if (pi->page_breaks.v)
5187 excel_write_PAGE_BREAK (bp, pi->page_breaks.v);
5189 if (pi->header != NULL)
5190 excel_write_HEADER_FOOTER (bp, pi->header, BIFF_HEADER);
5191 if (pi->footer != NULL)
5192 excel_write_HEADER_FOOTER (bp, pi->footer, BIFF_FOOTER);
5194 ms_biff_put_2byte (bp, BIFF_HCENTER, pi->center_horizontally ? 1 : 0);
5195 ms_biff_put_2byte (bp, BIFF_VCENTER, pi->center_vertically ? 1 : 0);
5197 print_info_get_margins (pi, NULL, NULL, &left, &right,
5198 &edge_to_below_header, &edge_to_above_footer);
5199 excel_write_margin (bp, BIFF_LEFT_MARGIN, left);
5200 excel_write_margin (bp, BIFF_RIGHT_MARGIN, right);
5201 excel_write_margin (bp, BIFF_TOP_MARGIN, edge_to_below_header);
5202 excel_write_margin (bp, BIFF_BOTTOM_MARGIN, edge_to_above_footer);
5204 excel_write_SETUP (bp, esheet);
5205 if (bp->version < MS_BIFF_V8) {
5206 /* write externsheets for every sheet in the workbook
5207 * to make our lives easier */
5208 excel_write_externsheets_v7 (esheet->ewb);
5210 if (sheet->is_protected) {
5211 ms_biff_put_2byte (bp, BIFF_PROTECT, 1 );
5212 ms_biff_put_2byte (bp, BIFF_OBJPROTECT, 1);
5213 ms_biff_put_2byte (bp, BIFF_SCENPROTECT, 1);
5214 ms_biff_put_2byte (bp, BIFF_PASSWORD, 0);
5216 excel_write_DEFCOLWIDTH (bp, esheet);
5217 excel_write_colinfos (bp, esheet);
5218 excel_write_AUTOFILTERINFO (bp, esheet);
5219 excel_write_DIMENSION (bp, esheet);
5222 void
5223 excel_write_SCL (BiffPut *bp, double zoom, gboolean force)
5225 guint8 *data;
5226 double whole, fractional = modf (zoom, &whole);
5227 int num, denom;
5229 go_stern_brocot (fractional, 1000, &num, &denom);
5230 num += whole * denom;
5231 d (2, g_printerr ("Zoom %g == %d/%d\n", zoom, num, denom););
5233 if (num == denom && !force)
5234 return;
5236 data = ms_biff_put_len_next (bp, BIFF_SCL, 4);
5237 GSF_LE_SET_GUINT16 (data + 0, (guint16)num);
5238 GSF_LE_SET_GUINT16 (data + 2, (guint16)denom);
5239 ms_biff_put_commit (bp);
5242 static void
5243 excel_write_SELECTION (BiffPut *bp, GSList *selections,
5244 GnmCellPos const *pos, int pane)
5246 int n = g_slist_length (selections);
5247 GSList *ptr;
5248 guint8 *data;
5249 int i_active = n - 1;
5251 /* Write backwards. */
5252 selections = g_slist_reverse (g_slist_copy (selections));
5254 data = ms_biff_put_len_next (bp, BIFF_SELECTION, 9 + 6*n);
5255 GSF_LE_SET_GUINT8 (data + 0, pane);
5256 GSF_LE_SET_GUINT16 (data + 1, pos->row);
5257 GSF_LE_SET_GUINT16 (data + 3, pos->col);
5258 GSF_LE_SET_GUINT16 (data + 5, i_active); /* last due to reverse */
5259 GSF_LE_SET_GUINT16 (data + 7, n);
5261 data += 9;
5262 for (ptr = selections ; ptr != NULL ; ptr = ptr->next, data += 6) {
5263 GnmRange const *r = ptr->data;
5264 GSF_LE_SET_GUINT16 (data + 0, r->start.row);
5265 GSF_LE_SET_GUINT16 (data + 2, r->end.row);
5266 GSF_LE_SET_GUINT8 (data + 4, r->start.col);
5267 GSF_LE_SET_GUINT8 (data + 5, r->end.col);
5269 ms_biff_put_commit (bp);
5271 g_slist_free (selections);
5273 static void
5274 excel_write_selections (BiffPut *bp, ExcelWriteSheet *esheet, SheetView *sv)
5276 GnmRange r;
5277 GnmCellPos pos;
5278 GSList *tmp;
5280 excel_write_SELECTION (bp, sv->selections, &sv->edit_pos, 3);
5282 if (sv->unfrozen_top_left.col > 0) {
5283 pos = sv->edit_pos; /* apparently no bounds check needed */
5284 tmp = g_slist_prepend (NULL, range_init_cellpos (&r, &pos));
5285 excel_write_SELECTION (bp, tmp, &pos, 1);
5286 g_slist_free (tmp);
5288 if (sv->unfrozen_top_left.row > 0) {
5289 pos = sv->edit_pos; /* apparently no bounds check needed */
5290 tmp = g_slist_prepend (NULL, range_init_cellpos (&r, &pos));
5291 excel_write_SELECTION (bp, tmp, &pos, 2);
5292 g_slist_free (tmp);
5294 if (sv->unfrozen_top_left.col > 0 && sv->unfrozen_top_left.row > 0) {
5295 pos = sv->edit_pos; /* apparently no bounds check needed */
5296 tmp = g_slist_prepend (NULL, range_init_cellpos (&r, &pos));
5297 excel_write_SELECTION (bp, tmp, &pos, 0);
5298 g_slist_free (tmp);
5302 static unsigned
5303 excel_write_ROWINFO (BiffPut *bp, ExcelWriteSheet *esheet, guint32 row, guint32 last_col)
5305 guint8 *data;
5306 unsigned pos;
5307 ColRowInfo const *ri = sheet_row_get (esheet->gnum_sheet, row);
5308 guint16 height;
5310 /* FIXME: Find default style for row. Does it have to be common to
5311 * all cells, or can a cell override? Do all cells have to be
5312 * blank. */
5313 guint16 row_xf = 0x000f; /* Magic */
5314 guint16 options = 0x100; /* undocumented magic */
5316 /* FIXME: set bit 12 of row_xf if thick border on top, bit 13 if thick
5317 * border on bottom. */
5319 if (ri == NULL)
5320 return bp->streamPos;
5322 /* We don't worry about standard height. I haven't seen it
5323 * indicated in any actual esheet. */
5324 height = (guint16) (20. * ri->size_pts + 1e-6);
5325 options |= (MIN (ri->outline_level, 0x7));
5326 if (ri->is_collapsed)
5327 options |= 0x10;
5328 if (!ri->visible)
5329 options |= 0x20;
5330 if (ri->hard_size)
5331 options |= 0x40;
5333 d (1, g_printerr ("Row %d height 0x%x;\n", row+1, height););
5335 data = ms_biff_put_len_next (bp, BIFF_ROW_v2, 16);
5336 pos = bp->streamPos;
5337 GSF_LE_SET_GUINT16 (data + 0, row); /* Row number */
5338 GSF_LE_SET_GUINT16 (data + 2, 0); /* first def. col */
5339 GSF_LE_SET_GUINT16 (data + 4, last_col);/* last def. col */
5340 GSF_LE_SET_GUINT16 (data + 6, height); /* height */
5341 GSF_LE_SET_GUINT16 (data + 8, 0x00); /* undocumented */
5342 GSF_LE_SET_GUINT16 (data + 10, 0x00); /* reserved */
5343 GSF_LE_SET_GUINT16 (data + 12, options); /* options */
5344 GSF_LE_SET_GUINT16 (data + 14, row_xf); /* default style */
5345 ms_biff_put_commit (bp);
5347 return pos;
5350 static void
5351 excel_sheet_write_INDEX (ExcelWriteSheet *esheet, gsf_off_t fpos,
5352 GArray *dbcells)
5354 BiffPut *bp = esheet->ewb->bp;
5355 guint32 *data;
5356 unsigned i;
5358 data = g_new (guint32, dbcells->len);
5360 for (i = 0; i < dbcells->len; i++) {
5361 unsigned pos = g_array_index (dbcells, unsigned, i);
5362 GSF_LE_SET_GUINT32 (data + i, pos - esheet->ewb->streamPos);
5363 d (2, g_printerr ("Writing index record"
5364 " 0x%4.4x - 0x%4.4x = 0x%4.4x\n",
5365 pos, esheet->ewb->streamPos,
5366 pos - esheet->ewb->streamPos););
5369 ms_biff_put_abs_write (bp, fpos, data, 4 * dbcells->len);
5371 g_free (data);
5375 * excel_sheet_write_DBCELL
5376 * @bp BIFF buffer
5377 * @esheet
5378 * @ri_start start positions of first 2 rowinfo records
5379 * @rc_start start positions of first row in each cell in block
5380 * @nrows no. of rows in block.
5382 * Write DBCELL (Stream offsets) record for a block of rows.
5384 * See: 'Finding records in BIFF files' and 'DBCELL'
5386 static void
5387 excel_sheet_write_DBCELL (ExcelWriteSheet *esheet,
5388 unsigned *ri_start, unsigned *rc_start, guint32 nrows,
5389 GArray *dbcells)
5391 BiffPut *bp = esheet->ewb->bp;
5392 unsigned pos;
5393 guint32 i;
5394 guint16 offset;
5396 guint8 *data = ms_biff_put_len_next (bp, BIFF_DBCELL, 4 + nrows * 2);
5397 pos = bp->streamPos;
5399 GSF_LE_SET_GUINT32 (data, pos - ri_start [0]);
5400 for (i = 0 ; i < nrows; i++) {
5401 offset = rc_start [0]
5402 - (i > 0 ? rc_start [i - 1] : ri_start [1]);
5403 GSF_LE_SET_GUINT16 (data + 4 + i * 2, offset);
5406 ms_biff_put_commit (bp);
5408 g_array_append_val (dbcells, pos);
5412 * excel_sheet_write_block
5413 * @esheet sheet
5414 * @begin first row no
5415 * @nrows no. of rows in block.
5417 * Write a block of rows. Returns no. of last row written.
5419 * We do not have to write row records for empty rows which use the
5420 * default style. But we do not test for this yet.
5422 * See: 'Finding records in BIFF files'
5424 static guint32
5425 excel_sheet_write_block (ExcelWriteSheet *esheet, guint32 begin, int nrows,
5426 GArray *dbcells)
5428 ExcelWriteState *ewb = esheet->ewb;
5429 int col, row, max_row, max_col = esheet->max_col;
5430 unsigned ri_start [2]; /* Row info start */
5431 unsigned *rc_start; /* Row cells start */
5432 guint16 *xf_list;
5433 GnmRange r;
5434 Sheet *sheet = esheet->gnum_sheet;
5435 TwoWayTable *twt = esheet->ewb->base.xf.two_way_table;
5436 gboolean has_content = FALSE;
5437 ExcelStyleVariant esv_cache;
5438 int esv_cache_xf;
5440 xf_list = g_new (gint16, gnm_sheet_get_max_cols (esheet->gnum_sheet));
5441 if (nrows > esheet->max_row - (int) begin) /* Incomplete final block? */
5442 nrows = esheet->max_row - (int) begin;
5443 max_row = begin + nrows - 1;
5445 ri_start [0] = excel_write_ROWINFO (ewb->bp, esheet, begin, max_col);
5446 ri_start [1] = ewb->bp->streamPos;
5447 for (row = begin + 1; row <= max_row; row++)
5448 (void) excel_write_ROWINFO (ewb->bp, esheet, row, max_col);
5450 /* One-element cache for efficiency. Checked with eq only. #581378 */
5451 esv_cache_xf = -1;
5452 esv_cache.variant = 42;
5453 esv_cache.style = NULL;
5455 r.start.col = 0;
5456 r.end.col = max_col-1;
5458 rc_start = g_alloca (sizeof (unsigned) * nrows);
5459 for (row = begin; row <= max_row; row++) {
5460 guint32 run_size = 0;
5462 /* Save start pos of 1st cell in row */
5463 r.start.row = r.end.row = row;
5464 rc_start [row - begin] = ewb->bp->streamPos;
5465 if (sheet_row_get (sheet, row) == NULL &&
5466 sheet_style_is_default (sheet, &r, esheet->col_style))
5467 continue;
5468 has_content = TRUE;
5469 for (col = 0; col < max_col; col++) {
5470 int xf;
5471 GnmCell const *cell = sheet_cell_get (sheet, col, row);
5472 ExcelStyleVariant esv;
5474 esv.variant = GPOINTER_TO_INT
5475 (g_hash_table_lookup (ewb->base.xf.cell_style_variant, cell));
5477 /* check for a magic value_fmt override*/
5478 esv.style = g_hash_table_lookup
5479 (ewb->base.xf.value_fmt_styles, cell);
5480 if (esv.style == NULL)
5481 esv.style = sheet_style_get (sheet, col, row);
5483 if (esv.variant == esv_cache.variant &&
5484 esv.style == esv_cache.style)
5485 xf = esv_cache_xf;
5486 else {
5487 esv_cache = esv;
5488 esv_cache_xf = xf =
5489 two_way_table_key_to_idx (twt, &esv);
5492 if (xf < 0) {
5493 g_warning ("Can't find style %p for cell %s!%s",
5494 esv.style,
5495 sheet->name_unquoted,
5496 cell_coord_name (col, row));
5497 xf = 0;
5499 if (cell == NULL) {
5500 if (xf != esheet->col_xf [col])
5501 xf_list [run_size++] = xf;
5502 else if (run_size > 0) {
5503 write_mulblank (ewb->bp, esheet, col - 1, row,
5504 xf_list, run_size);
5505 run_size = 0;
5507 } else {
5508 if (run_size > 0) {
5509 write_mulblank (ewb->bp, esheet, col - 1, row,
5510 xf_list, run_size);
5511 run_size = 0;
5513 excel_write_cell (ewb, esheet, cell, xf);
5514 go_io_count_progress_update (esheet->ewb->io_context, 1);
5517 if (run_size > 0)
5518 write_mulblank (ewb->bp, esheet, col - 1, row,
5519 xf_list, run_size);
5522 excel_sheet_write_DBCELL (esheet, ri_start, rc_start,
5523 has_content ? nrows : 0, dbcells);
5525 g_free (xf_list);
5527 return row - 1;
5530 static void
5531 excel_write_CODENAME (ExcelWriteState *ewb, GObject *src)
5533 if (ewb->export_macros) {
5534 char const *codename = g_object_get_data (src, CODENAME_KEY);
5535 /* it does not appear to always exist */
5536 if (codename != NULL) {
5537 ms_biff_put_var_next (ewb->bp, BIFF_CODENAME);
5538 excel_write_string (ewb->bp, STR_TWO_BYTE_LENGTH, codename);
5539 ms_biff_put_commit (ewb->bp);
5544 static void
5545 cb_NOTE_v8 (SheetObject const *so, gconstpointer id, BiffPut *bp)
5547 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
5548 char const *author = cell_comment_author_get (GNM_CELL_COMMENT (so));
5549 guint8 buf [4 * 2];
5551 if (!author)
5552 author = "";
5554 ms_biff_put_var_next (bp, BIFF_NOTE);
5555 GSF_LE_SET_GUINT16 (buf + 0, anchor->cell_bound.start.row);
5556 GSF_LE_SET_GUINT16 (buf + 2, anchor->cell_bound.start.col);
5557 GSF_LE_SET_GUINT16 (buf + 4, 0);
5558 GSF_LE_SET_GUINT16 (buf + 6, GPOINTER_TO_INT (id));
5559 ms_biff_put_var_write (bp, buf, sizeof buf);
5560 excel_write_string (bp, STR_TWO_BYTE_LENGTH, author);
5561 GSF_LE_SET_GUINT8 (buf, 0);
5562 ms_biff_put_var_write (bp, buf, 1); /* XL adds a trailing 0 */
5563 ms_biff_put_commit (bp);
5566 static void
5567 excel_write_objs_v8 (ExcelWriteSheet *esheet)
5569 BiffPut *bp = esheet->ewb->bp;
5570 GSList *ptr;
5571 gsf_off_t fpos = 0;
5572 guint32 len = 0;
5574 if (esheet->num_objs == 0)
5575 return;
5576 /* The header */
5577 if (bp->version >= MS_BIFF_V8) {
5578 static guint8 const header_obj_v8[] = {
5579 /* DgContainers */ 0x0f, 0, 2, 0xf0, 0, 0, 0, 0, /* fill in length */
5580 /* Dg */ 0x10, 0, 8, 0xf0, 8, 0, 0, 0,
5581 0, 0, 0, 0, /* fill num objects in this group + 1 */
5582 0, 0, 0, 0, /* fill last spid in this group */
5583 /* SpgrContainer */0x0f, 0, 3, 0xf0, 0, 0, 0, 0, /* fill in length */
5584 /* SpContainer */ 0x0f, 0, 4, 0xf0, 0x28, 0, 0, 0,
5585 /* Spgr */ 1, 0, 9, 0xf0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5586 /* Sp */ 2, 0, 0xa, 0xf0, 8, 0, 0, 0, 0, 4, 0, 0, 5, 0, 0, 0
5588 guint8 buf [sizeof header_obj_v8];
5589 unsigned last_id, num_filters = 0;
5591 if (esheet->gnum_sheet->filters != NULL) {
5592 GnmFilter const *f = esheet->gnum_sheet->filters->data;
5593 num_filters = range_width (&f->r);
5595 if (esheet->gnum_sheet->filters->next != NULL) {
5596 g_warning ("MS Excel does not support multiple autofilters in one sheet (%s), only the first will be saved", esheet->gnum_sheet->name_unquoted);
5600 esheet->ewb->cur_obj_group++;
5601 last_id = 0x400*esheet->ewb->cur_obj_group + esheet->num_objs;
5603 ms_biff_put_var_next (bp, BIFF_MS_O_DRAWING);
5604 fpos = bp->streamPos + 4;
5605 memcpy (buf, header_obj_v8, sizeof header_obj_v8);
5606 // GSF_LE_SET_GUINT32 (buf + 4, 72 + len);
5607 GSF_LE_SET_GUINT32 (buf + 16, esheet->num_objs + 1);
5608 GSF_LE_SET_GUINT32 (buf + 20, last_id); /* last spid in this group */
5609 // GSF_LE_SET_GUINT32 (buf + 28, 48 + len);
5610 ms_biff_put_var_write (bp, buf, sizeof header_obj_v8);
5611 /* First object commits. */
5614 for (ptr = esheet->objects; ptr != NULL ; ptr = ptr->next)
5615 len += excel_write_obj_v8 (esheet, ptr->data);
5617 len += excel_write_autofilter_objs (esheet);
5619 g_hash_table_foreach (esheet->commentshash,
5620 (GHFunc) cb_NOTE_v8, bp);
5622 if (bp->version >= MS_BIFF_V8) {
5623 char tmp[4];
5624 guint32 spgrcontlen = len + 48;
5625 guint32 dgcontlen = spgrcontlen + 24;
5627 GSF_LE_SET_GUINT32 (tmp, dgcontlen);
5628 ms_biff_put_abs_write (bp, fpos + 4, tmp, 4);
5630 GSF_LE_SET_GUINT32 (tmp, spgrcontlen);
5631 ms_biff_put_abs_write (bp, fpos + 28, tmp, 4);
5635 static void
5636 excel_write_sheet (ExcelWriteState *ewb, ExcelWriteSheet *esheet)
5638 GArray *dbcells;
5639 guint32 block_end;
5640 gint32 y;
5641 int rows_in_block = ROW_BLOCK_MAX_LEN;
5642 gsf_off_t index_off;
5643 MsBiffFileType type;
5645 /* No. of blocks of rows. Only correct as long as all rows
5646 * _including empties_ have row info records
5648 guint32 nblocks = (esheet->max_row - 1) / rows_in_block + 1;
5650 switch (esheet->gnum_sheet->sheet_type) {
5651 default :
5652 g_warning ("unknown sheet type %d (assuming WorkSheet)",
5653 esheet->gnum_sheet->sheet_type);
5654 case GNM_SHEET_DATA : type = MS_BIFF_TYPE_Worksheet; break;
5655 case GNM_SHEET_OBJECT : type = MS_BIFF_TYPE_Chart; break;
5656 case GNM_SHEET_XLM : type = MS_BIFF_TYPE_Macrosheet; break;
5658 esheet->streamPos = excel_write_BOF (ewb->bp, type);
5659 if (esheet->gnum_sheet->sheet_type == GNM_SHEET_OBJECT) {
5660 g_return_if_fail (esheet->graphs != NULL);
5661 ms_excel_chart_write (ewb, esheet->graphs->data);
5662 return;
5665 if (ewb->bp->version >= MS_BIFF_V8) {
5666 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_INDEX_v2,
5667 nblocks * 4 + 16);
5668 GSF_LE_SET_GUINT32 (data, 0);
5669 GSF_LE_SET_GUINT32 (data + 4, 0);
5670 GSF_LE_SET_GUINT32 (data + 8, esheet->max_row);
5671 GSF_LE_SET_GUINT32 (data + 12, 0);
5673 index_off = ewb->bp->streamPos + 4 + 16;
5674 memset (data + 16, 0, nblocks * 4);
5675 } else {
5676 guint8 *data = ms_biff_put_len_next (ewb->bp, BIFF_INDEX_v2,
5677 nblocks * 4 + 12);
5678 GSF_LE_SET_GUINT32 (data, 0);
5679 GSF_LE_SET_GUINT16 (data + 4, 0);
5680 GSF_LE_SET_GUINT16 (data + 6, esheet->max_row);
5681 GSF_LE_SET_GUINT32 (data + 8, 0);
5683 index_off = ewb->bp->streamPos + 4 + 12;
5684 memset (data + 12, 0, nblocks * 4);
5686 ms_biff_put_commit (ewb->bp);
5688 write_sheet_head (ewb->bp, esheet);
5690 d (1, g_printerr ("Saving esheet '%s' geom (%d, %d)\n",
5691 esheet->gnum_sheet->name_unquoted,
5692 esheet->max_col, esheet->max_row););
5693 dbcells = g_array_new (FALSE, FALSE, sizeof (unsigned));
5694 for (y = 0; y < esheet->max_row; y = block_end + 1)
5695 block_end = excel_sheet_write_block (esheet, y, rows_in_block,
5696 dbcells);
5698 if (ewb->bp->version < MS_BIFF_V8)
5699 excel_write_comments_biff7 (ewb->bp, esheet);
5700 excel_sheet_write_INDEX (esheet, index_off, dbcells);
5702 if (ewb->num_obj_groups > 0)
5703 excel_write_objs_v8 (esheet);
5705 SHEET_FOREACH_VIEW (esheet->gnum_sheet, view, {
5706 excel_write_WINDOW2 (ewb->bp, esheet, view);
5707 excel_write_SCL (ewb->bp, /* zoom will move to view eentually */
5708 esheet->gnum_sheet->last_zoom_factor_used, FALSE);
5709 excel_write_selections (ewb->bp, esheet, view);
5712 /* These are actually specific to >= biff8
5713 * but it can't hurt to have them here
5714 * things will just ignore them */
5715 excel_write_MERGECELLs (ewb->bp, esheet);
5716 excel_write_conditions (ewb->bp, esheet);
5717 if (ewb->bp->version >= MS_BIFF_V8) {
5718 // We won't write these right pre-v8
5719 excel_write_HLINKs (ewb->bp, esheet);
5721 excel_write_DVALs (ewb->bp, esheet);
5722 excel_write_SHEETPROTECTION (ewb->bp, esheet->gnum_sheet);
5724 /* See: Global Column Widths... not crucial.
5725 data = ms_biff_put_len_next (ewb->bp, BIFF_GCW, 34);
5727 int i;
5728 for (i = 0; i < 34; i++)
5729 GSF_LE_SET_GUINT8 (data+i, 0xff);
5730 GSF_LE_SET_GUINT32 (data, 0xfffd0020);
5732 ms_biff_put_commit (ewb->bp);
5735 excel_write_CODENAME (ewb, G_OBJECT (esheet->gnum_sheet));
5737 ms_biff_put_empty (ewb->bp, BIFF_EOF);
5738 g_array_free (dbcells, TRUE);
5741 static GnmNamedExpr *
5742 create_macroname (SheetObject *so)
5744 Sheet *sheet = sheet_object_get_sheet (so);
5745 GnmNamedExpr *nexpr;
5746 char *basename, *objname, *name;
5747 unsigned ui;
5748 GnmParsePos pp;
5750 parse_pos_init_sheet (&pp, sheet);
5752 g_object_get (so, "name", &objname, NULL);
5753 if (objname) {
5754 basename = g_strconcat (objname, "_Click", NULL);
5755 g_free (objname);
5756 } else
5757 basename = g_strdup ("Click");
5759 name = g_strdup (basename);
5760 for (ui = 1; expr_name_lookup (&pp, name) != NULL; ui++) {
5761 g_free (name);
5762 name = g_strdup_printf ("%s_%d", basename, ui);
5765 pp.sheet = NULL;
5767 nexpr = expr_name_add (&pp, name, NULL, NULL, TRUE, NULL);
5768 expr_name_set_expr (nexpr, NULL);
5770 g_free (name);
5771 g_free (basename);
5773 return nexpr;
5776 static ExcelWriteSheet *
5777 excel_sheet_new (ExcelWriteState *ewb, Sheet *sheet,
5778 gboolean biff7, gboolean biff8)
5780 int const maxrows = MIN (biff7 ? XLS_MaxRow_V7 : XLS_MaxRow_V8,
5781 gnm_sheet_get_max_rows (sheet));
5782 int const maxcols = MIN (XLS_MaxCol,
5783 gnm_sheet_get_max_cols (sheet));
5784 ExcelWriteSheet *esheet = g_new0 (ExcelWriteSheet, 1);
5785 GnmRange extent;
5786 GSList *objs, *l;
5788 g_return_val_if_fail (sheet, NULL);
5789 g_return_val_if_fail (ewb, NULL);
5791 esheet->col_xf = g_new (guint16, gnm_sheet_get_max_cols (sheet));
5792 esheet->col_style = sheet_style_most_common (sheet, TRUE);
5793 excel_sheet_extent (sheet, &extent, esheet->col_style, maxcols, maxrows, ewb->io_context);
5795 esheet->gnum_sheet = sheet;
5796 esheet->streamPos = 0x0deadbee;
5797 esheet->ewb = ewb;
5799 /* makes it easier to refer to 1 past the end */
5800 esheet->max_col = extent.end.col + 1;
5801 esheet->max_row = extent.end.row + 1;
5803 /* It is ok to have formatting out of range, we can disregard that. */
5804 if (esheet->max_col > XLS_MaxCol)
5805 esheet->max_col = XLS_MaxCol;
5806 if (esheet->max_row > maxrows)
5807 esheet->max_row = maxrows;
5810 * Supposedly biff8 only, but just let readers ignore what they
5811 * cannot handle.
5813 esheet->hlinks = sheet_style_collect_hlinks (sheet, NULL);
5814 esheet->conditions = sheet_style_collect_conditions (sheet, NULL);
5815 esheet->validations = sheet_style_collect_validations (sheet, NULL);
5817 esheet->cur_obj = esheet->num_objs = 0;
5819 esheet->widget_macroname =
5820 g_hash_table_new_full (g_direct_hash, g_direct_equal,
5821 NULL, (GDestroyNotify)expr_name_remove);
5823 objs = sheet_objects_get (sheet, NULL, G_TYPE_NONE);
5824 for (l = objs; l; l = l->next) {
5825 SheetObject *so = GNM_SO (l->data);
5826 gboolean handled = FALSE;
5828 if (GNM_IS_SO_GRAPH (so)) {
5829 /* No derivation for now. */
5830 esheet->graphs = g_slist_prepend (esheet->graphs, so);
5831 esheet->objects =
5832 g_slist_prepend (esheet->objects, so);
5833 handled = TRUE;
5834 } else if (GNM_IS_SO_IMAGE (so)) {
5835 SheetObjectImage *soi = GNM_SO_IMAGE (l->data);
5836 BlipInf *bi = blipinf_new (soi);
5838 /* Images we can't export have a NULL BlipInf */
5839 if (!bi)
5840 goto unhandled;
5842 esheet->blips = g_slist_prepend (esheet->blips, bi);
5843 esheet->objects =
5844 g_slist_prepend (esheet->objects, so);
5845 handled = TRUE;
5846 } else if (GNM_IS_CELL_COMMENT (so)) {
5847 esheet->comments = g_slist_prepend (esheet->comments,
5848 so);
5849 /* Also an object. Is that right for v7? */
5850 esheet->objects =
5851 g_slist_prepend (esheet->objects, so);
5852 handled = TRUE;
5853 } else if (GNM_IS_SO_FILLED (so)) {
5854 esheet->objects =
5855 g_slist_prepend (esheet->objects, so);
5856 handled = TRUE;
5857 } else if (GNM_IS_SOW_CHECKBOX (so) ||
5858 GNM_IS_SOW_RADIO_BUTTON (so) ||
5859 GNM_IS_SOW_SPINBUTTON (so) ||
5860 GNM_IS_SOW_SCROLLBAR (so) ||
5861 GNM_IS_SOW_SLIDER (so) ||
5862 GNM_IS_SOW_LIST (so) ||
5863 GNM_IS_SOW_BUTTON (so) ||
5864 GNM_IS_SOW_COMBO (so)) {
5865 esheet->objects =
5866 g_slist_prepend (esheet->objects, so);
5867 g_hash_table_insert (esheet->widget_macroname,
5869 create_macroname (so));
5870 handled = TRUE;
5871 } else if (GNM_IS_SO_LINE (so)) {
5872 esheet->objects =
5873 g_slist_prepend (esheet->objects, so);
5874 handled = TRUE;
5875 } else if (GNM_IS_FILTER_COMBO (so)) {
5876 /* Handled outside loop. */
5877 continue;
5880 if (handled) {
5881 esheet->num_objs++;
5882 } else {
5883 char *name;
5884 unhandled:
5885 g_object_get (so, "name", &name, NULL);
5886 g_warning ("Not exporting object %s of type %s",
5887 (name ? name : "?"),
5888 g_type_name (G_OBJECT_TYPE (so)));
5889 g_free (name);
5892 g_slist_free (objs);
5894 /* We used to reserve the lists here, but we actually need
5895 to write the objects in reverse order for xls. */
5897 /* ---------------------------------------- */
5899 /* And the autofilters (only 1 per sheet in XL) */
5900 if (sheet->filters != NULL) {
5901 GnmFilter const *filter = sheet->filters->data;
5902 esheet->num_objs += filter->fields->len;
5905 /* ---------------------------------------- */
5907 /* Gets populated with obj_ids later */
5908 esheet->commentshash = g_hash_table_new (g_direct_hash, g_direct_equal);
5910 return esheet;
5913 static void
5914 excel_sheet_free (ExcelWriteSheet *esheet)
5916 g_slist_free (esheet->objects);
5917 g_slist_free (esheet->comments);
5918 g_slist_free (esheet->graphs);
5919 g_hash_table_destroy (esheet->commentshash);
5920 g_hash_table_destroy (esheet->widget_macroname);
5921 g_slist_free_full (esheet->blips, (GDestroyNotify)blipinf_free);
5922 style_list_free (esheet->conditions);
5923 style_list_free (esheet->hlinks);
5924 style_list_free (esheet->validations);
5925 g_free (esheet->col_style);
5926 g_free (esheet->col_xf);
5927 g_free (esheet);
5930 /* Scans all the workbook items. Adds all styles, fonts, formats and colors to
5931 * tables. Resolves any referencing problems before they occur, hence the
5932 * records can be written in a linear order. */
5933 static void
5934 pre_pass (ExcelWriteState *ewb)
5936 TwoWayTable *twt;
5937 XLExportBase *xle = &ewb->base;
5939 gather_styles (ewb); /* (and cache cells) */
5941 /* Gather Info from styles */
5942 twt = xle->xf.two_way_table;
5943 g_hash_table_foreach (twt->unique_keys, (GHFunc)put_style_font, xle);
5944 twt = xle->xf.two_way_table;
5945 g_hash_table_foreach (twt->unique_keys, (GHFunc)put_format, xle);
5946 gather_palette (xle);
5949 typedef struct {
5950 guint32 streampos;
5951 guint16 record_pos;
5952 } SSTInf;
5954 static void
5955 excel_write_SST (ExcelWriteState *ewb)
5957 /* According to MSDN max SST sisze is 8224 */
5958 GPtrArray const *strings = ewb->sst.indicies;
5959 BiffPut *bp = ewb->bp;
5960 SSTInf *extsst = NULL;
5961 char *ptr, data [8224];
5962 char const * const last = data + sizeof (data);
5963 size_t out_bytes, char_len, byte_len;
5964 unsigned i, tmp, blocks, scale;
5965 GOString const *string;
5966 char *str;
5968 if (strings->len > 0) {
5969 blocks = 1 + ((strings->len - 1) / 8);
5970 extsst = g_alloca (sizeof (SSTInf) * blocks);
5971 } else
5972 blocks = 0;
5974 ms_biff_put_var_next (bp, BIFF_SST);
5975 GSF_LE_SET_GUINT32 (data + 0, strings->len);
5976 GSF_LE_SET_GUINT32 (data + 4, strings->len);
5978 ptr = data + 8;
5979 for (i = 0; i < strings->len ; i++) {
5980 string = g_ptr_array_index (strings, i);
5981 str = (char *)string->str;
5983 if (0 == (i % 8)) {
5984 tmp = ptr - data + /* biff header */ 4;
5985 extsst[i/8].record_pos = tmp;
5986 extsst[i/8].streampos = bp->streamPos + tmp;
5989 char_len = excel_strlen (str, &byte_len);
5991 /* get the size, the marker, and at least 1 character out */
5992 if ((ptr + 5) >= last) {
5993 ms_biff_put_var_write (bp, data, ptr-data);
5994 ms_biff_put_commit (bp);
5995 ms_biff_put_var_next (bp, BIFF_CONTINUE);
5996 ptr = data;
5998 GSF_LE_SET_GUINT16 (ptr, char_len);
5999 ptr += 2;
6001 if (char_len == byte_len) {
6002 while ((ptr + 1 + char_len) > last) {
6003 *ptr++ = 0; /* unicode header == 0 */
6004 strncpy (ptr, str, last - ptr);
6005 str += (last - ptr);
6006 char_len -= (last - ptr);
6007 ptr = data;
6009 ms_biff_put_var_write (bp, data, sizeof (data));
6010 ms_biff_put_commit (bp);
6011 ms_biff_put_var_next (bp, BIFF_CONTINUE);
6014 *ptr = 0; /* unicode header == 0 */
6015 strncpy (ptr + 1, str, char_len);
6016 ptr += char_len + 1;
6017 } else {
6018 size_t old_out_bytes, count = 0;
6019 unsigned old_byte_len = INT_MAX;
6020 guint8 *len = ptr - 2; /* stash just in case of problem */
6022 unicode_loop :
6023 *ptr++ = 1; /* unicode header == 1 */
6024 old_out_bytes = out_bytes = last - ptr;
6025 g_iconv (bp->convert, &str, &byte_len, (char **)&ptr, &out_bytes);
6026 count += old_out_bytes - out_bytes;
6028 if (byte_len > 0) {
6029 if (old_byte_len == byte_len) {
6030 g_warning ("hmm we could not represent character 0x%x, skipping it.",
6031 g_utf8_get_char (str));
6032 str = g_utf8_next_char (str);
6033 } else {
6034 old_byte_len = byte_len;
6035 ms_biff_put_var_write (bp, data, ptr - data);
6036 ms_biff_put_commit (bp);
6037 ms_biff_put_var_next (bp, BIFF_CONTINUE);
6038 ptr = data;
6039 len = NULL;
6041 goto unicode_loop;
6044 if (count != (char_len*2)) {
6045 if (len != NULL) {
6046 if (count % 1) {
6047 g_warning ("W.T.F ??? utf-16 should be 2 byte aligned");
6048 *ptr++ = 0;
6049 count++;
6050 } else {
6051 g_warning ("We exported a string containg unicode characters > 0xffff (%s).\n"
6052 "Expect some funky characters to show up.", str);
6054 GSF_LE_SET_GUINT16 (len, (count/2));
6055 } else {
6056 g_warning ("We're toast a string containg unicode characters > 0xffff crossed a record boundary.");
6062 ms_biff_put_var_write (bp, data, ptr-data);
6063 ms_biff_put_commit (bp);
6065 /* EXSST must fit in 1 record, no CONTINUEs */
6066 scale = 1;
6067 while (((blocks / scale) * 8) >= (ms_biff_max_record_len (bp) - 2))
6068 scale *= 2;
6069 ms_biff_put_var_next (bp, BIFF_EXTSST);
6070 GSF_LE_SET_GUINT16 (data + 0, 8*scale);
6071 ms_biff_put_var_write (bp, data, 2);
6073 GSF_LE_SET_GUINT16 (data + 6, 0); /* constant ignored */
6074 for (i = 0; i < blocks; i += scale) {
6075 GSF_LE_SET_GUINT32 (data + 0, extsst[i].streampos);
6076 GSF_LE_SET_GUINT16 (data + 4, extsst[i].record_pos);
6077 ms_biff_put_var_write (bp, data, 8);
6079 ms_biff_put_commit (bp);
6082 static void
6083 excel_write_WRITEACCESS (BiffPut *bp)
6085 guint8 pad [112];
6086 unsigned len;
6087 char const *utf8_name = go_get_real_name ();
6089 if (utf8_name == NULL)
6090 utf8_name = "";
6092 ms_biff_put_var_next (bp, BIFF_WRITEACCESS);
6093 if (bp->version >= MS_BIFF_V8) {
6094 len = excel_write_string (bp, STR_TWO_BYTE_LENGTH, utf8_name);
6095 memset (pad, ' ', sizeof pad);
6096 ms_biff_put_var_write (bp, pad, sizeof pad - len);
6097 ms_biff_put_commit (bp);
6098 } else {
6099 len = excel_write_string (bp, STR_ONE_BYTE_LENGTH, utf8_name);
6100 memset (pad, ' ', 32);
6101 ms_biff_put_var_write (bp, pad, 32 - len - 1);
6102 ms_biff_put_commit (bp);
6106 static void
6107 excel_foreach_name (ExcelWriteState *ewb, GHFunc func)
6109 workbook_foreach_name (ewb->base.wb, FALSE, func, ewb);
6112 static void
6113 cb_enumerate_names (gpointer key, GnmNamedExpr *nexpr, ExcelWriteState *ewb)
6115 ewb->tmp_counter++; /* pre increment to avoid 0 */
6116 g_hash_table_insert (ewb->names, (gpointer)nexpr,
6117 GUINT_TO_POINTER (ewb->tmp_counter));
6120 static void
6121 cb_enumerate_macros (gpointer key, ExcelFunc *efunc, ExcelWriteState *ewb)
6123 if (efunc->macro_name != NULL)
6124 efunc->idx = ++ewb->tmp_counter;
6127 static void
6128 cb_write_macro_NAME (gpointer key, ExcelFunc *efunc, ExcelWriteState *ewb)
6130 if (efunc->macro_name != NULL) {
6131 guint8 data[14] = {
6132 0xE, 0x0, /* flag vba macro */
6133 0x0, /* key */
6134 0x0, /* namelen <FILLIN> */
6135 0x0, 0x0, /* no expr */
6136 0x0, 0x0, /* not sheet local */
6137 0x0, 0x0, /* not sheet local */
6138 0x0, /* menu */
6139 0x0, /* description */
6140 0x0, /* help */
6141 0x0 /* status */
6143 unsigned len = excel_strlen (efunc->macro_name, NULL);
6145 if (len > 255)
6146 len = 255;
6147 ms_biff_put_var_next (ewb->bp, BIFF_NAME_v0); /* yes v0 */
6148 GSF_LE_SET_GUINT8 (data+3, len);
6149 ms_biff_put_var_write (ewb->bp, data, sizeof (data));
6150 excel_write_string (ewb->bp, STR_NO_LENGTH, efunc->macro_name);
6151 ms_biff_put_commit (ewb->bp);
6155 static void
6156 excel_write_names (ExcelWriteState *ewb)
6158 excel_foreach_name (ewb, (GHFunc)&cb_enumerate_names);
6159 g_hash_table_foreach (ewb->function_map,
6160 (GHFunc)cb_enumerate_macros, ewb);
6162 excel_foreach_name (ewb, (GHFunc)&excel_write_NAME);
6163 g_hash_table_foreach (ewb->function_map,
6164 (GHFunc)cb_write_macro_NAME, ewb);
6165 excel_write_autofilter_names (ewb);
6168 static void
6169 excel_write_image_bytes (BiffPut *bp, GByteArray *bytes)
6172 ms_biff_put_var_write (bp, bytes->data, bytes->len);
6176 * FIXME: Excel doesn't read images written by this (but will open the files).
6177 * OpenOffice.org can read the images.
6179 static void
6180 excel_write_vector_blip (ExcelWriteState *ewb, BlipInf *blip, const BlipType *bt)
6182 BiffPut *bp = ewb->bp;
6184 if (bp->version >= MS_BIFF_V8) {
6185 guint8 buf [VECTOR_BLIP_HDR_LEN];
6186 double coords [4];
6187 double width, height;
6189 sheet_object_position_pts_get (blip->so, coords);
6190 width = fabs (coords[2] - coords[0]);
6191 height = fabs (coords[3] - coords[1]);
6193 d(2,
6195 g_message ("emu_width=%d (0x%x)",
6196 (guint32) GO_PT_TO_EMU(width),
6197 (guint32) GO_PT_TO_EMU (width));
6198 g_message ("emu_height=%d (0x%x)",
6199 (guint32) GO_PT_TO_EMU(height),
6200 (guint32) GO_PT_TO_EMU (height));
6201 g_message ("cm_width=%d (0x%x)",
6202 (guint32) GO_PT_TO_CM(width*1000),
6203 (guint32) GO_PT_TO_CM (width*1000));
6204 g_message ("cm_height=%d (0x%x)",
6205 (guint32) GO_PT_TO_CM(height*1000),
6206 (guint32) GO_PT_TO_CM (height*1000));
6208 memset (buf, 0, sizeof buf);
6209 memcpy (buf, bt->blip_tag, sizeof bt->blip_tag);
6210 GSF_LE_SET_GUINT16 (buf + 2, 0xf018 + bt->type);
6211 GSF_LE_SET_GUINT32 (buf + 4,
6212 blip->bytes.len + VECTOR_BLIP_HDR_LEN - 8);
6213 memcpy(buf + 8, blip->id, sizeof blip->id);
6214 /* buf + 24: uncompressed length */
6215 GSF_LE_SET_GUINT32 (buf + 24, blip->uncomp_len);
6216 /* buf + 28: metafile bounds (rectangle) */
6217 /* unit for rectangle is 1/1000 cm */
6218 GSF_LE_SET_GUINT32 (buf + 36,
6219 (guint32) GO_PT_TO_CM(width*1000));
6220 GSF_LE_SET_GUINT32 (buf + 40,
6221 (guint32) GO_PT_TO_CM(height*1000));
6222 /* buf + 44: size of metafile (point) */
6223 /* unit for points is EMU = 1/360000 cm = 1/12700 pt
6224 - 360 times finer than for rect. */
6225 GSF_LE_SET_GUINT32 (buf + 44,
6226 (guint32) GO_PT_TO_EMU (width));
6227 GSF_LE_SET_GUINT32 (buf + 48,
6228 (guint32) GO_PT_TO_EMU (height));
6229 /* buf + 52: compressed length */
6230 GSF_LE_SET_GUINT32 (buf + 52, blip->bytes.len);
6231 /* buf + 56: = 0 if compressed, 0xfe if not */
6232 /* buf + 57: = 0xfe - filter none */
6233 GSF_LE_SET_GUINT8 (buf + 57, 0xfe);
6234 ms_biff_put_var_write (bp, buf, sizeof buf);
6236 /* Write image data */
6237 excel_write_image_bytes (bp, &blip->bytes);
6241 static void
6242 excel_write_raster_blip (ExcelWriteState *ewb, BlipInf *blip, const BlipType *bt)
6244 BiffPut *bp = ewb->bp;
6246 if (bp->version >= MS_BIFF_V8) {
6247 guint8 buf [RASTER_BLIP_HDR_LEN];
6249 memset (buf, 0, sizeof buf);
6250 memcpy (buf, bt->blip_tag, sizeof bt->blip_tag);
6251 GSF_LE_SET_GUINT16 (buf + 2, 0xf018 + bt->type);
6252 GSF_LE_SET_GUINT32 (buf + 4,
6253 blip->bytes.len + BLIP_ID_LEN + 1);
6254 memcpy(buf + 8, blip->id, sizeof blip->id);
6255 GSF_LE_SET_GUINT8 (buf + 24, 0xff);
6256 ms_biff_put_var_write (bp, buf, sizeof buf);
6258 /* Write image data */
6259 excel_write_image_bytes (bp, &blip->bytes);
6263 static const BlipType bliptypes[] =
6265 {"emf", 2, {0x40, 0x3d}, excel_write_vector_blip},
6266 {"wmf", 3, {0x60, 0x21}, excel_write_vector_blip},
6267 {"pict", 4, {0x20, 0x54}, excel_write_vector_blip},
6268 {"jpeg", 5, {0xa0, 0x46}, excel_write_raster_blip},
6269 {"png", 6, {0, 0x6e}, excel_write_raster_blip},
6270 {"dib", 7, {0x80, 0x7a}, excel_write_raster_blip}
6273 static const BlipType *
6274 get_bliptype (char const *type)
6276 int n = G_N_ELEMENTS (bliptypes);
6277 int i;
6279 for (i = 0; i < n; i++)
6280 if (strcmp (type, bliptypes[i].type_name) == 0)
6281 return &bliptypes[i];
6282 return NULL;
6284 static void
6285 excel_write_blip (ExcelWriteState *ewb, BlipInf *blip)
6287 BiffPut *bp = ewb->bp;
6288 const BlipType *bt;
6290 if (bp->version >= MS_BIFF_V8) {
6291 static guint8 const header_obj_v8[] = {
6292 /* BSE header */ 0x2, 0, 7, 0xf0, 0, 0, 0, 0 /* fill in bliptype, length */
6294 guint8 buf [44];
6295 guint8 win_type, mac_type;
6297 memset (buf, 0, sizeof buf);
6298 memcpy (buf, header_obj_v8, sizeof header_obj_v8);
6299 GSF_LE_SET_GUINT32 (buf + 4,
6300 blip->bytes.len + blip->header_len - 8);
6301 bt = get_bliptype (blip->type);
6302 if (!bt)
6303 return;
6305 win_type = mac_type = bt->type;
6306 if (bt->type == 4)
6307 win_type = 2;
6308 else if (bt->type == 2 || bt->type == 3)
6309 mac_type = 4;
6310 GSF_LE_SET_GUINT8 (buf, (bt->type << 4) + 2);
6311 GSF_LE_SET_GUINT8 (buf + 8, win_type);
6312 GSF_LE_SET_GUINT8 (buf + 9, mac_type);
6314 /* id (checksum) */
6315 mdfour(blip->id, blip->bytes.data, blip->bytes.len);
6316 memcpy(buf + 10, blip->id, sizeof blip->id);
6317 /* size */
6318 GSF_LE_SET_GUINT32 (buf + 28,
6319 blip->bytes.len + blip->header_len - 44);
6320 /* refcount */
6321 GSF_LE_SET_GUINT32 (buf + 32, 1);
6322 ms_biff_put_var_write (bp, buf, sizeof buf);
6324 bt->handler (ewb, blip, bt);
6328 /* FIXME: Store repeats only once. */
6329 static void
6330 excel_write_blips (ExcelWriteState *ewb, guint32 bliplen)
6332 BiffPut *bp = ewb->bp;
6334 if (bp->version >= MS_BIFF_V8) {
6335 static guint8 const header_obj_v8[] = {
6336 /* BStore header */ 0x1f, 0, 1, 0xf0, 0, 0, 0, 0 /* fill in #blips, length */
6338 guint8 buf [sizeof header_obj_v8];
6339 GSList *b;
6340 ExcelWriteSheet const *s;
6341 guint i, nblips;
6343 for (i = 0, nblips = 0; i < ewb->esheets->len; i++) {
6344 s = g_ptr_array_index (ewb->esheets, i);
6345 nblips += g_slist_length (s->blips);
6348 memcpy (buf, header_obj_v8, sizeof header_obj_v8);
6349 GSF_LE_SET_GUINT8 (buf, (nblips << 4 | 0xf));
6350 GSF_LE_SET_GUINT32 (buf + 4, bliplen);
6351 ms_biff_put_var_write (bp, buf, sizeof header_obj_v8);
6353 for (i = 0; i < ewb->esheets->len; i++) {
6354 s = g_ptr_array_index (ewb->esheets, i);
6355 for (b = s->blips; b != NULL; b = b->next)
6356 excel_write_blip (ewb, b->data);
6361 static void
6362 excel_write_workbook (ExcelWriteState *ewb)
6364 BiffPut *bp = ewb->bp;
6365 ExcelWriteSheet *s = NULL;
6366 guint8 *data;
6367 unsigned i, n;
6369 ewb->streamPos = excel_write_BOF (ewb->bp, MS_BIFF_TYPE_Workbook);
6371 if (bp->version >= MS_BIFF_V8)
6372 ms_biff_put_2byte (ewb->bp, BIFF_INTERFACEHDR, bp->codepage);
6373 else
6374 ms_biff_put_empty (ewb->bp, BIFF_INTERFACEHDR);
6376 data = ms_biff_put_len_next (bp, BIFF_MMS, 2);
6377 GSF_LE_SET_GUINT16(data, 0);
6378 ms_biff_put_commit (bp);
6380 if (bp->version < MS_BIFF_V8) {
6381 ms_biff_put_empty (ewb->bp, BIFF_TOOLBARHDR);
6382 ms_biff_put_empty (ewb->bp, BIFF_TOOLBAREND);
6385 ms_biff_put_empty (ewb->bp, BIFF_INTERFACEEND);
6387 excel_write_WRITEACCESS (ewb->bp);
6389 ms_biff_put_2byte (ewb->bp, BIFF_CODEPAGE, bp->codepage);
6390 if (bp->version >= MS_BIFF_V8) {
6391 ms_biff_put_2byte (ewb->bp, BIFF_DSF, ewb->double_stream_file ? 1 : 0);
6392 ms_biff_put_empty (ewb->bp, BIFF_XL9FILE);
6394 n = ewb->esheets->len;
6395 data = ms_biff_put_len_next (bp, BIFF_TABID, n * 2);
6396 for (i = 0; i < n; i++)
6397 GSF_LE_SET_GUINT16 (data + i*2, i + 1);
6398 ms_biff_put_commit (bp);
6400 if (ewb->export_macros) {
6401 ms_biff_put_empty (ewb->bp, BIFF_OBPROJ);
6402 excel_write_CODENAME (ewb, G_OBJECT (ewb->base.wb));
6406 ms_biff_put_2byte (ewb->bp, BIFF_FNGROUPCOUNT, 0x0e);
6408 if (bp->version < MS_BIFF_V8) {
6409 /* write externsheets for every sheet in the workbook
6410 * to make our lives easier */
6411 excel_write_externsheets_v7 (ewb);
6413 /* assign indicies to the names before we export */
6414 ewb->tmp_counter = 0;
6415 excel_write_names (ewb);
6418 ms_biff_put_2byte (ewb->bp, BIFF_WINDOWPROTECT, 0);
6419 ms_biff_put_2byte (ewb->bp, BIFF_PROTECT, ewb->base.wb_view->is_protected ? 1 : 0);
6420 ms_biff_put_2byte (ewb->bp, BIFF_PASSWORD, 0);
6422 if (bp->version >= MS_BIFF_V8) {
6423 ms_biff_put_2byte (ewb->bp, BIFF_PROT4REV, 0);
6424 ms_biff_put_2byte (ewb->bp, BIFF_PROT4REVPASS, 0);
6427 WORKBOOK_FOREACH_VIEW (ewb->base.wb, view,
6428 excel_write_WINDOW1 (bp, view););
6430 ms_biff_put_2byte (ewb->bp, BIFF_BACKUP, 0);
6431 ms_biff_put_2byte (ewb->bp, BIFF_HIDEOBJ, 0);
6432 ms_biff_put_2byte (ewb->bp, BIFF_1904,
6433 workbook_date_conv (ewb->base.wb)->use_1904 ? 1 : 0);
6434 ms_biff_put_2byte (ewb->bp, BIFF_PRECISION, 0x0001);
6435 ms_biff_put_2byte (ewb->bp, BIFF_REFRESHALL, 0);
6436 ms_biff_put_2byte (ewb->bp, BIFF_BOOKBOOL, 0);
6438 excel_write_FONTs (bp, ewb);
6439 excel_write_FORMATs (ewb);
6440 excel_write_XFs (ewb);
6442 if (bp->version >= MS_BIFF_V8)
6443 ms_biff_put_2byte (ewb->bp, BIFF_USESELFS, 0x01);
6444 write_palette (bp, ewb);
6446 for (i = 0; i < ewb->esheets->len; i++) {
6447 s = g_ptr_array_index (ewb->esheets, i);
6448 s->boundsheetPos = excel_write_BOUNDSHEET (bp, s->gnum_sheet);
6451 if (bp->version >= MS_BIFF_V8) {
6452 unsigned max_obj_id, num_objs;
6453 guint32 bliplen = 0;
6455 excel_write_COUNTRY (bp);
6457 excel_write_externsheets_v8 (ewb);
6459 ewb->tmp_counter = 0;
6460 excel_write_names (ewb);
6462 /* If there are any objects in the workbook add a header */
6463 num_objs = max_obj_id = 0;
6464 for (i = 0; i < ewb->esheets->len; i++) {
6465 GSList *b;
6467 s = g_ptr_array_index (ewb->esheets, i);
6468 if (s->num_objs > 0) {
6469 ewb->num_obj_groups++;
6470 max_obj_id = 0x400 * (ewb->num_obj_groups) | s->num_objs;
6471 num_objs += s->num_objs + 1;
6473 for (b = s->blips; b; b = b->next) {
6474 BlipInf *bi = b->data;
6475 bliplen += (bi->header_len + bi->bytes.len);
6479 if (ewb->num_obj_groups > 0) {
6480 static guint8 const header[] = {
6481 /* DggContainer */ 0xf, 0, 0, 0xf0, 0, 0, 0, 0, /* fill in length */
6482 /* Dgg */ 0, 0, 6, 0xf0, 0, 0, 0, 0, /* fill in length */
6484 static guint8 const footer[] = {
6485 /* OPT */ 0x33, 0, 0xb, 0xf0, 0x12, 0, 0, 0,
6486 0xbf, 0, 8, 0, 8, 0, /* bool fFitTextToShape 191 = 0x00080008; */
6487 0x81, 1, 0x41, 0, 0, 8, /* colour fillColor 385 = 0x08000041; */
6488 0xc0, 1, 0x40, 0, 0, 8, /* colour lineColor 448 = 0x08000040; */
6489 /* SplitMenuColors */ 0x40, 0, 0x1e, 0xf1, 0x10, 0, 0, 0,
6490 0x0d, 0, 0, 8, 0x0c, 0, 0, 0x08,
6491 0x17, 0, 0, 8, 0xf7, 0, 0, 0x10
6494 guint8 buf[16];
6496 ms_biff_put_var_next (bp, BIFF_MS_O_DRAWING_GROUP);
6497 memcpy (buf, header, sizeof header);
6499 GSF_LE_SET_GUINT32 (buf+ 4,
6500 (0x4a + ewb->num_obj_groups * 8 +
6501 ((bliplen > 0) ? (bliplen + 8) : 0)));
6502 GSF_LE_SET_GUINT32 (buf+12,
6503 (0x10 + ewb->num_obj_groups * 8));
6504 ms_biff_put_var_write (bp, buf, sizeof header);
6506 GSF_LE_SET_GUINT32 (buf+ 0, (max_obj_id+1)); /* max_spid */
6507 GSF_LE_SET_GUINT32 (buf+ 4, (ewb->num_obj_groups+1)); /* num_id_clust */
6508 GSF_LE_SET_GUINT32 (buf+ 8, num_objs); /* (c) */ /* num_shapes_saved */
6509 GSF_LE_SET_GUINT32 (buf+ 12, ewb->num_obj_groups); /* num_drawings_saved */
6510 ms_biff_put_var_write (bp, buf, 4*4);
6512 ewb->cur_obj_group = 0;
6513 for (i = 0; i < ewb->esheets->len; i++) {
6514 s = g_ptr_array_index (ewb->esheets, i);
6515 if (s->num_objs > 0) {
6516 ewb->cur_obj_group++;
6517 GSF_LE_SET_GUINT32 (buf+0, ewb->cur_obj_group); /* dgid - DG owning the SPIDs in this cluster */
6518 GSF_LE_SET_GUINT32 (buf+4, s->num_objs+1); /* cspidCur - number of SPIDs used so far */
6519 ms_biff_put_var_write (bp, buf, 8);
6522 ewb->cur_obj_group = 0;
6524 if (bliplen > 0)
6525 excel_write_blips (ewb, bliplen);
6527 ms_biff_put_var_write (bp, footer, sizeof footer);
6528 ms_biff_put_commit (bp);
6531 excel_write_SST (ewb);
6534 ms_biff_put_empty (ewb->bp, BIFF_EOF);
6536 n = 0;
6537 for (i = workbook_sheet_count (ewb->base.wb) ; i-- > 0 ;)
6538 n += sheet_cells_count (workbook_sheet_by_index (ewb->base.wb, i));
6539 go_io_count_progress_set (ewb->io_context,
6540 n, N_CELLS_BETWEEN_UPDATES);
6541 for (i = 0; i < ewb->esheets->len; i++)
6542 excel_write_sheet (ewb, g_ptr_array_index (ewb->esheets, i));
6543 go_io_progress_unset (ewb->io_context);
6545 /* Finalise Workbook stuff */
6546 for (i = 0; i < ewb->esheets->len; i++) {
6547 ExcelWriteSheet *s = g_ptr_array_index (ewb->esheets, i);
6548 excel_fix_BOUNDSHEET (bp->output, s->boundsheetPos,
6549 s->streamPos);
6553 /****************************************************************************/
6555 void
6556 excel_write_v7 (ExcelWriteState *ewb, GsfOutfile *outfile)
6558 GsfOutput *content;
6559 int codepage = -1;
6560 gpointer tmp;
6562 g_return_if_fail (outfile != NULL);
6563 g_return_if_fail (ewb != NULL);
6564 g_return_if_fail (ewb->bp == NULL);
6566 content = gsf_outfile_new_child (outfile, "Book", FALSE);
6567 if (content != NULL) {
6568 tmp = g_object_get_data (G_OBJECT (ewb->base.wb), "excel-codepage");
6569 if (tmp != NULL)
6570 codepage = GPOINTER_TO_INT (tmp);
6572 ewb->bp = ms_biff_put_new (content, MS_BIFF_V7, codepage);
6573 excel_write_workbook (ewb);
6574 ms_biff_put_destroy (ewb->bp);
6575 ewb->bp = NULL;
6577 xls_write_pivot_caches (ewb, outfile, MS_BIFF_V7, codepage);
6578 } else
6579 go_cmd_context_error_export (GO_CMD_CONTEXT (ewb->io_context),
6580 _("Couldn't open stream 'Book' for writing\n"));
6583 void
6584 excel_write_v8 (ExcelWriteState *ewb, GsfOutfile *outfile)
6586 GsfOutput *content;
6588 g_return_if_fail (outfile != NULL);
6589 g_return_if_fail (ewb != NULL);
6590 g_return_if_fail (ewb->bp == NULL);
6592 content = gsf_outfile_new_child (outfile, "Workbook", FALSE);
6593 if (content != NULL) {
6594 ewb->bp = ms_biff_put_new (content, MS_BIFF_V8, -1);
6595 excel_write_workbook (ewb);
6596 ms_biff_put_destroy (ewb->bp);
6597 ewb->bp = NULL;
6599 xls_write_pivot_caches (ewb, outfile, MS_BIFF_V8, -1);
6600 } else
6601 go_cmd_context_error_export (GO_CMD_CONTEXT (ewb->io_context),
6602 _("Couldn't open stream 'Workbook' for writing\n"));
6605 /****************************************************************************/
6607 static void
6608 cb_check_names (G_GNUC_UNUSED gconstpointer key,
6609 GnmNamedExpr *nexpr, ExcelWriteState *ewb)
6611 if (expr_name_is_active (nexpr))
6612 excel_write_prep_expr (ewb, nexpr->texpr);
6615 static void
6616 extract_gog_object_style (XLExportBase *ewb, GogObject *obj)
6618 GSList *ptr = obj->children;
6620 if (GOG_IS_STYLED_OBJECT (obj)) {
6621 GOStyle const *style = GOG_STYLED_OBJECT (obj)->style;
6622 if (style->interesting_fields & (GO_STYLE_OUTLINE | GO_STYLE_LINE))
6623 put_color_go_color (ewb, style->line.color);
6624 if (style->interesting_fields & GO_STYLE_FILL)
6625 switch (style->fill.type) {
6626 default :
6627 case GO_STYLE_FILL_NONE :
6628 case GO_STYLE_FILL_IMAGE :
6629 break;
6630 case GO_STYLE_FILL_PATTERN :
6631 put_color_go_color (ewb, style->fill.pattern.fore);
6632 put_color_go_color (ewb, style->fill.pattern.back);
6633 break;
6634 case GO_STYLE_FILL_GRADIENT :
6635 put_color_go_color (ewb, style->fill.pattern.fore);
6637 if (style->interesting_fields & GO_STYLE_MARKER) {
6638 put_color_go_color (ewb, go_marker_get_outline_color (style->marker.mark));
6639 put_color_go_color (ewb, go_marker_get_fill_color (style->marker.mark));
6642 if (style->interesting_fields & GO_STYLE_FONT)
6643 excel_font_from_go_font (ewb, style->font.font);
6645 if (GOG_IS_AXIS (obj)) {
6646 char *fmt_str;
6647 g_object_get (G_OBJECT (obj), "assigned-format-string-XL", &fmt_str, NULL);
6648 if (fmt_str != NULL) {
6649 GOFormat *fmt = go_format_new_from_XL (fmt_str);
6650 if (!go_format_is_general (fmt))
6651 two_way_table_put (ewb->formats.two_way_table,
6652 (gpointer)fmt, TRUE,
6653 (AfterPutFunc) after_put_format,
6654 "Found unique format %d - 0x%x\n");
6655 else
6656 go_format_unref (fmt);
6658 g_free (fmt_str);
6661 for ( ; ptr != NULL ; ptr = ptr->next)
6662 extract_gog_object_style (ewb, ptr->data);
6665 /* extract markup for text objects. Has to happen early, so that the font
6666 * gets saved */
6667 static void
6668 extract_txomarkup (ExcelWriteState *ewb, SheetObject *so)
6670 PangoAttrList *markup;
6671 GArray *txo;
6672 char *text;
6674 g_object_get (G_OBJECT (so), "markup", &markup, NULL);
6675 if (!markup)
6676 return;
6678 g_object_get (G_OBJECT (so), "text", &text, NULL);
6679 txo = txomarkup_new (ewb, text ? text : "", markup, ewb->base.xf.default_style);
6680 g_free (text);
6682 /* It isn't a cell, but that doesn't matter here */
6683 g_hash_table_insert (ewb->cell_markup, (gpointer)so, txo);
6685 pango_attr_list_unref (markup);
6688 static void
6689 cb_g_array_free (GArray *array)
6691 g_array_free (array, TRUE);
6694 static void
6695 free_excel_func (ExcelFunc *efunc)
6697 g_free (efunc->macro_name);
6698 g_free (efunc);
6701 ExcelWriteState *
6702 excel_write_state_new (GOIOContext *context, WorkbookView const *wb_view,
6703 gboolean biff7, gboolean biff8)
6705 ExcelWriteState *ewb = g_new (ExcelWriteState, 1);
6706 ExcelWriteSheet *esheet;
6707 Sheet *sheet;
6708 int i;
6710 g_return_val_if_fail (ewb != NULL, NULL);
6712 ewb->base.wb = wb_view_get_workbook (wb_view);
6713 ewb->base.wb_view = wb_view;
6715 ewb->bp = NULL;
6716 ewb->io_context = context;
6717 ewb->esheets = g_ptr_array_new ();
6718 ewb->names = g_hash_table_new (g_direct_hash, g_direct_equal);
6719 ewb->externnames = g_ptr_array_new ();
6720 ewb->function_map = g_hash_table_new_full
6721 (g_direct_hash, g_direct_equal,
6722 NULL,
6723 (GDestroyNotify)free_excel_func);
6724 ewb->sheet_pairs = NULL;
6725 ewb->cell_markup = g_hash_table_new_full (g_direct_hash, g_direct_equal,
6726 NULL, (GDestroyNotify) cb_g_array_free);
6727 ewb->double_stream_file = biff7 && biff8;
6728 ewb->num_obj_groups = ewb->cur_obj_group = ewb->cur_blip = 0;
6730 ewb->base.fonts.two_way_table = two_way_table_new (
6731 excel_font_hash, excel_font_equal, 0,
6732 (GDestroyNotify) excel_font_free);
6733 ewb->unique_name_id = 0;
6735 formats_init (&ewb->base);
6736 palette_init (&ewb->base);
6737 xf_init (&ewb->base);
6739 /* look for externsheet references in */
6740 excel_write_prep_expressions (ewb); /* dependents */
6741 WORKBOOK_FOREACH_DEPENDENT (ewb->base.wb, dep,
6742 excel_write_prep_expr (ewb, dep->texpr););
6743 excel_foreach_name (ewb, (GHFunc) cb_check_names); /* names */
6745 for (i = 0 ; i < workbook_sheet_count (ewb->base.wb) ; i++) {
6746 GSList *ptr;
6748 sheet = workbook_sheet_by_index (ewb->base.wb, i);
6749 esheet = excel_sheet_new (ewb, sheet, biff7, biff8);
6750 if (esheet == NULL)
6751 continue;
6753 g_ptr_array_add (ewb->esheets, esheet);
6755 if (sheet->sheet_type != GNM_SHEET_DATA)
6756 continue;
6758 excel_write_prep_conditions (esheet);
6759 excel_write_prep_validations (esheet);
6760 if (sheet->filters != NULL)
6761 excel_write_prep_sheet (ewb, sheet);
6762 for (ptr = esheet->graphs ; ptr != NULL ; ptr = ptr->next)
6763 extract_gog_object_style (&ewb->base,
6764 (GogObject *)sheet_object_graph_get_gog (ptr->data));
6765 for (ptr = esheet->objects ; ptr != NULL ; ptr = ptr->next) {
6766 SheetObject *so = ptr->data;
6767 GParamSpec *pspec = g_object_class_find_property
6768 (G_OBJECT_GET_CLASS (so), "text");
6769 if (pspec)
6770 extract_txomarkup (ewb, so);
6774 if (biff8) {
6775 ewb->sst.strings = g_hash_table_new (g_direct_hash, g_direct_equal);
6776 ewb->sst.indicies = g_ptr_array_new ();
6777 } else {
6778 ewb->sst.strings = NULL;
6779 ewb->sst.indicies = NULL;
6782 if (ewb->esheets->len != 0)
6783 pre_pass (ewb);
6784 ewb->base.pivot_caches = excel_collect_pivot_caches (ewb->base.wb);
6786 return ewb;
6789 void
6790 excel_write_state_free (ExcelWriteState *ewb)
6792 unsigned i;
6794 if (ewb->base.fonts.two_way_table != NULL) {
6795 two_way_table_free (ewb->base.fonts.two_way_table);
6796 ewb->base.fonts.two_way_table = NULL;
6798 formats_free (&ewb->base);
6799 palette_free (&ewb->base);
6800 xf_free (&ewb->base);
6802 for (i = 0; i < ewb->esheets->len; i++)
6803 excel_sheet_free (g_ptr_array_index (ewb->esheets, i));
6804 g_ptr_array_free (ewb->esheets, TRUE);
6806 g_hash_table_destroy (ewb->names);
6807 g_ptr_array_foreach (ewb->externnames, (GFunc)g_free, NULL);
6808 g_ptr_array_free (ewb->externnames, TRUE);
6809 g_hash_table_destroy (ewb->function_map);
6810 g_hash_table_destroy (ewb->sheet_pairs);
6811 g_hash_table_destroy (ewb->cell_markup);
6813 if (ewb->sst.strings != NULL) {
6814 g_hash_table_destroy (ewb->sst.strings);
6815 g_ptr_array_free (ewb->sst.indicies, TRUE);
6818 g_free (ewb);
6821 /*****************************************************************************/
6823 GHashTable *
6824 excel_collect_pivot_caches (Workbook const *wb)
6826 GSList *slicers;
6827 GHashTable *caches = NULL;
6828 unsigned int i;
6830 for (i = workbook_sheet_count (wb) ; i-- > 0 ;) {
6831 Sheet const *sheet = workbook_sheet_by_index (wb, i);
6832 for (slicers = sheet->slicers; NULL != slicers ; slicers = slicers->next) {
6833 GODataCache *cache = go_data_slicer_get_cache (slicers->data);
6834 if (NULL == caches)
6835 caches = g_hash_table_new (g_direct_hash, g_direct_equal);
6836 else if (NULL != g_hash_table_lookup (caches, cache))
6837 continue;
6838 g_hash_table_insert (caches, cache, GUINT_TO_POINTER (g_hash_table_size (caches)+1));
6842 return caches;