1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * xlsx-write.c : export MS Office Open xlsx files.
5 * Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 /*****************************************************************************/
25 #include <gnumeric-config.h>
26 #include <goffice/goffice.h>
28 #include "ms-excel-write.h"
29 #include "xlsx-utils.h"
31 #include "parse-util.h"
33 #include "workbook-priv.h"
34 #include "workbook-view.h"
36 #include "sheet-style.h"
37 #include "sheet-view.h"
38 #include "sheet-filter.h"
39 #include "sheet-filter-combo.h"
45 #include "style-color.h"
46 #include "validation.h"
48 #include "input-msg.h"
49 #include "print-info.h"
51 #include "sheet-object.h"
52 #include "sheet-object-cell-comment.h"
53 #include "sheet-object-graph.h"
54 #include "sheet-object-widget.h"
55 #include "sheet-object-image.h"
56 #include "gnm-so-line.h"
57 #include "gnm-so-filled.h"
59 #include "style-border.h"
60 #include "style-conditions.h"
62 #include "expr-name.h"
66 #include <gsf/gsf-output.h>
67 #include <gsf/gsf-outfile.h>
68 #include <gsf/gsf-outfile-zip.h>
69 #include <gsf/gsf-open-pkg-utils.h>
70 #include <gsf/gsf-utils.h>
71 #include <gsf/gsf-libxml.h>
72 #include <glib/gi18n-lib.h>
73 #include <gsf/gsf-meta-names.h>
74 #include <gsf/gsf-doc-meta-data.h>
75 #include <gsf/gsf-docprop-vector.h>
76 #include <gsf/gsf-timestamp.h>
80 #define NUM_FORMAT_BASE 100
87 static char const *ns_ss
= "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
88 static char const *ns_ss_drawing
= "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
89 static char const *ns_docprops_core_cp
= "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
90 static char const *ns_docprops_core_dc
= "http://purl.org/dc/elements/1.1/";
91 static char const *ns_docprops_core_dcmitype
= "http://purl.org/dc/dcmitype/";
92 static char const *ns_docprops_core_dcterms
= "http://purl.org/dc/terms/";
93 static char const *ns_docprops_core_xsi
= "http://www.w3.org/2001/XMLSchema-instance";
94 static char const *ns_docprops_extended
= "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
95 static char const *ns_docprops_extended_vt
= "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";
96 static char const *ns_docprops_custom
= "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
97 static char const *ns_drawing
= "http://schemas.openxmlformats.org/drawingml/2006/main";
98 static char const *ns_chart
= "http://schemas.openxmlformats.org/drawingml/2006/chart";
99 static char const *ns_vml
= "urn:schemas-microsoft-com:vml";
100 static char const *ns_leg_office
= "urn:schemas-microsoft-com:office:office";
101 static char const *ns_leg_excel
= "urn:schemas-microsoft-com:office:excel";
103 static char const *ns_gnm_ext
= "http://www.gnumeric.org/ext/spreadsheetml";
105 static char const *ns_rel
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
106 static char const *ns_rel_hlink
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
107 static char const *ns_rel_draw
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
108 static char const *ns_rel_leg_draw
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
109 static char const *ns_rel_chart
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
110 static char const *ns_rel_com
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
111 static char const *ns_rel_image
= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
122 xlsx_dir_init (XLSXDir
*dir
, GsfOutfile
*parent
, const char *name
)
125 dir
->parent
= parent
;
131 xlsx_dir_close (XLSXDir
*dir
)
134 gsf_output_close (GSF_OUTPUT (dir
->dir
));
135 g_object_unref (dir
->dir
);
141 xlsx_dir_get (XLSXDir
*dir
)
144 char *debug_name
= g_strdup_printf ("xlsx directory %s", dir
->name
);
145 dir
->dir
= (GsfOutfile
*)gsf_outfile_new_child (dir
->parent
, dir
->name
, TRUE
);
146 go_debug_check_finalized (dir
->dir
, debug_name
);
158 gboolean with_extension
;
161 GHashTable
*shared_string_hash
;
162 GPtrArray
*shared_string_array
;
163 GHashTable
*styles_hash
;
164 GPtrArray
*styles_array
;
165 GHashTable
*dxfs_hash
;
166 GPtrArray
*dxfs_array
;
167 GnmConventions
*convs
;
168 GOIOContext
*io_context
;
173 XLSXDir chart_dir
, drawing_dir
, legacy_drawing_dir
;
174 XLSXDir media_dir
, pivotCache_dir
, pivotTable_dir
;
176 unsigned drawing_elem_id
;
183 XLSXWriteState
*state
;
196 xlsx_add_bool (GsfXMLOut
*xml
, char const *id
, gboolean val
)
198 gsf_xml_out_add_cstr_unchecked (xml
, id
, val
? "1" : "0");
201 xlsx_add_rgb (GsfXMLOut
*xml
, char const *id
, GOColor c
)
203 char buf
[3 * 4 * sizeof (unsigned int) + 1];
204 sprintf (buf
, "%02X%02X%02X%02X",
205 GO_COLOR_UINT_A (c
), GO_COLOR_UINT_R (c
),
206 GO_COLOR_UINT_G (c
), GO_COLOR_UINT_B (c
));
207 gsf_xml_out_add_cstr_unchecked (xml
, id
, buf
);
210 xlsx_add_pos (GsfXMLOut
*xml
, char const *id
, GnmCellPos
const *pos
)
212 gsf_xml_out_add_cstr_unchecked (xml
, id
,
213 cellpos_as_string (pos
));
216 xlsx_add_range (GsfXMLOut
*xml
, char const *id
, GnmRange
const *range
)
218 gsf_xml_out_add_cstr_unchecked (xml
, id
,
219 range_as_string (range
));
222 xlsx_add_range_list (GsfXMLOut
*xml
, char const *id
, GSList
const *ranges
)
224 GString
*accum
= g_string_new (NULL
);
226 for (; NULL
!= ranges
; ranges
= ranges
->next
) {
227 g_string_append (accum
, range_as_string (ranges
->data
));
228 if (NULL
!= ranges
->next
)
229 g_string_append_c (accum
, ' ');
232 gsf_xml_out_add_cstr_unchecked (xml
, id
, accum
->str
);
233 g_string_free (accum
, TRUE
);
236 xlsx_add_pt (GsfXMLOut
*xml
, char const *id
, double l
)
238 GString
*str
= g_string_new (NULL
);
240 g_string_append_printf (str
, "%.2fpt", l
);
241 gsf_xml_out_add_cstr_unchecked (xml
, id
, str
->str
);
242 g_string_free (str
, TRUE
);
245 /****************************************************************************/
247 #define N_PREDEFINED_FILLS (G_N_ELEMENTS (pre_def_fills) - 1)
249 static char const * const pats
[] = {
252 "mediumGray", /* 3 */
256 "darkHorizontal", /* 7 */
257 "darkVertical", /* 8 */
261 "darkTrellis", /* 12 */
262 "lightHorizontal", /* 13 */
263 "lightVertical", /* 14 */
264 "lightDown", /* 15 */
266 "lightGrid", /* 17 */
267 "lightTrellis", /* 18 */
269 "lightVertical", /* 19 */
270 "darkHorizontal", /* 20 */
271 "lightGray", /* 21 */
272 "lightGray", /* 22 */
277 static char const * const pre_def_fills
[] = {
284 xlsx_write_predefined_fills (GsfXMLOut
*xml
)
286 char const * const *f
= pre_def_fills
;
289 gsf_xml_out_start_element (xml
, "fill");
290 gsf_xml_out_start_element (xml
, "patternFill");
291 gsf_xml_out_add_cstr_unchecked (xml
, "patternType", *f
++);
292 gsf_xml_out_end_element (xml
);
293 gsf_xml_out_end_element (xml
);
298 xlsx_find_predefined_fill (GnmStyle
const *style
)
300 gboolean pattern_is_std
=
301 (!gnm_style_is_element_set (style
, MSTYLE_COLOR_PATTERN
) ||
302 gnm_style_get_pattern_color (style
)->go_color
== GO_COLOR_BLACK
);
303 gboolean back_is_std
=
304 (!gnm_style_is_element_set (style
, MSTYLE_COLOR_BACK
) ||
305 gnm_style_get_back_color (style
)->go_color
== GO_COLOR_WHITE
);
307 if (gnm_style_is_element_set (style
, MSTYLE_PATTERN
) &&
308 gnm_style_get_pattern (style
) == 0 &&
312 if (gnm_style_is_element_set (style
, MSTYLE_PATTERN
) &&
313 gnm_style_get_pattern (style
) == 5 &&
322 /****************************************************************************/
325 xlsx_write_rich_text (GsfXMLOut
*xml
, char const *text
, PangoAttrList
*attrs
,
326 gboolean allow_xml_space
)
328 PangoAttrIterator
*iter
;
332 gsf_xml_out_start_element (xml
, "t");
333 gsf_xml_out_add_cstr (xml
, NULL
, text
);
334 gsf_xml_out_end_element (xml
); /* </t> */
339 iter
= pango_attr_list_get_iterator (attrs
);
341 PangoAttribute
*attr
;
342 GOFontScript fs
= GO_FONT_SCRIPT_STANDARD
;
344 gsf_xml_out_start_element (xml
, "r");
345 gsf_xml_out_start_element (xml
, "rPr");
347 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_FAMILY
);
349 gsf_xml_out_start_element (xml
, "rFont");
350 gsf_xml_out_add_cstr_unchecked (xml
, "val", ((PangoAttrString
*) attr
)->value
);
351 gsf_xml_out_end_element (xml
); /* </rFont> */
354 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_WEIGHT
);
356 gsf_xml_out_start_element (xml
, "b");
357 gsf_xml_out_add_cstr_unchecked (xml
, "val", ((PangoAttrInt
*) attr
)->value
> PANGO_WEIGHT_NORMAL
? "true": "false");
358 gsf_xml_out_end_element (xml
); /* </b> */
361 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_STYLE
);
363 gsf_xml_out_start_element (xml
, "i");
364 gsf_xml_out_add_cstr_unchecked (xml
, "val", ((PangoAttrInt
*) attr
)->value
!= PANGO_STYLE_NORMAL
? "true": "false");
365 gsf_xml_out_end_element (xml
); /* </i> */
368 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_STRIKETHROUGH
);
370 gsf_xml_out_start_element (xml
, "strike");
371 gsf_xml_out_add_cstr_unchecked (xml
, "val", ((PangoAttrInt
*) attr
)->value
? "true": "false");
372 gsf_xml_out_end_element (xml
); /* </strike> */
375 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_FOREGROUND
);
377 PangoColor
*color
= &((PangoAttrColor
*) attr
)->color
;
378 char *buf
= g_strdup_printf("ff%02x%02x%02x", color
->red
>> 8, color
->green
>> 8, color
->blue
>> 8);
379 gsf_xml_out_start_element (xml
, "color");
380 gsf_xml_out_add_cstr_unchecked (xml
, "rgb", buf
);
381 gsf_xml_out_end_element (xml
); /* </color> */
385 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_SIZE
);
387 gsf_xml_out_start_element (xml
, "sz");
388 gsf_xml_out_add_uint (xml
, "val", ((PangoAttrInt
*) attr
)->value
/ PANGO_SCALE
);
389 gsf_xml_out_end_element (xml
); /* </sz> */
392 attr
= pango_attr_iterator_get (iter
, PANGO_ATTR_UNDERLINE
);
394 PangoUnderline u
= ((PangoAttrInt
*) attr
)->value
;
395 gsf_xml_out_start_element (xml
, "u");
397 case PANGO_UNDERLINE_NONE
:
399 gsf_xml_out_add_cstr_unchecked (xml
, "val", "none");
401 case PANGO_UNDERLINE_ERROR
:
402 /* not supported by OpenXML */
404 case PANGO_UNDERLINE_SINGLE
:
405 gsf_xml_out_add_cstr_unchecked (xml
, "val", "single");
407 case PANGO_UNDERLINE_DOUBLE
:
408 gsf_xml_out_add_cstr_unchecked (xml
, "val", "double");
410 case PANGO_UNDERLINE_LOW
:
411 gsf_xml_out_add_cstr_unchecked (xml
, "val", "singleAccounting");
414 gsf_xml_out_end_element (xml
); /* </u> */
417 attr
= pango_attr_iterator_get (iter
, go_pango_attr_superscript_get_attr_type ());
418 if (attr
&& ((PangoAttrInt
*) attr
)->value
)
419 fs
= GO_FONT_SCRIPT_SUPER
;
420 attr
= pango_attr_iterator_get (iter
, go_pango_attr_subscript_get_attr_type ());
421 if (attr
&& ((PangoAttrInt
*) attr
)->value
)
422 fs
= GO_FONT_SCRIPT_SUB
;
423 if (fs
!= GO_FONT_SCRIPT_STANDARD
) {
424 const char *va
= (fs
== GO_FONT_SCRIPT_SUB
)
427 gsf_xml_out_start_element (xml
, "vertAlign");
428 gsf_xml_out_add_cstr_unchecked (xml
, "val", va
);
429 gsf_xml_out_end_element (xml
); /* </vertAlign> */
432 gsf_xml_out_end_element (xml
); /* </rPr> */
433 gsf_xml_out_start_element (xml
, "t");
434 pango_attr_iterator_range (iter
, &start
, &end
);
438 char *buf
= g_strndup (text
+ start
, end
- start
);
439 if (allow_xml_space
) {
441 gboolean has_space
= FALSE
;
442 for (p
= buf
; *p
; p
= g_utf8_next_char (p
)) {
443 if (g_unichar_isspace (g_utf8_get_char (p
))) {
449 gsf_xml_out_add_cstr_unchecked
450 (xml
, "xml:space", "preserve");
452 gsf_xml_out_add_cstr (xml
, NULL
, buf
);
455 gsf_xml_out_end_element (xml
); /* </t> */
456 gsf_xml_out_end_element (xml
); /* </r> */
457 } while (pango_attr_iterator_next (iter
));
458 pango_attr_iterator_destroy (iter
);
462 xlsx_shared_string (XLSXWriteState
*state
, GnmValue
const *v
)
467 g_return_val_if_fail (VALUE_IS_STRING (v
), -1);
469 if (g_hash_table_lookup_extended (state
->shared_string_hash
,
471 i
= GPOINTER_TO_INT (tmp
);
473 GnmValue
*v2
= value_dup (v
);
475 if (VALUE_FMT (v2
) && !go_format_is_markup (VALUE_FMT (v2
))) {
476 value_set_fmt (v2
, NULL
);
477 i
= xlsx_shared_string (state
, v2
);
480 i
= state
->shared_string_array
->len
;
481 g_ptr_array_add (state
->shared_string_array
, v2
);
482 g_hash_table_insert (state
->shared_string_hash
,
483 v2
, GINT_TO_POINTER (i
));
491 xlsx_write_shared_strings (XLSXWriteState
*state
, GsfOutfile
*wb_part
)
493 const unsigned N
= state
->shared_string_array
->len
;
501 part
= gsf_outfile_open_pkg_add_rel
502 (state
->xl_dir
, "sharedStrings.xml",
503 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
505 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings");
506 xml
= gsf_xml_out_new (part
);
508 gsf_xml_out_start_element (xml
, "sst");
509 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns", ns_ss
);
510 gsf_xml_out_add_int (xml
, "uniqueCount", N
);
511 gsf_xml_out_add_int (xml
, "count", N
);
513 for (i
= 0; i
< N
; i
++) {
514 GnmValue
const *val
=
515 g_ptr_array_index (state
->shared_string_array
, i
);
516 PangoAttrList
*attrs
= VALUE_FMT (val
)
517 ? (PangoAttrList
*)go_format_get_markup (VALUE_FMT (val
))
519 gsf_xml_out_start_element (xml
, "si");
520 xlsx_write_rich_text (xml
,
521 value_peek_string (val
),
524 gsf_xml_out_end_element (xml
); /* </si> */
527 gsf_xml_out_end_element (xml
); /* </sst> */
529 g_object_unref (xml
);
530 gsf_output_close (part
);
531 g_object_unref (part
);
535 xlsx_load_buildin_num_formats (GHashTable
*hash
)
537 static char const * const std_builtins
[] = {
551 /* 13 */ "# ?""?/?""?", /* silly trick to avoid using a trigraph */
556 /* 18 */ "h:mm AM/PM",
557 /* 19 */ "h:mm:ss AM/PM",
560 /* 22 */ "m/d/yy h:mm",
575 /* 37 */ "#,##0 ;(#,##0)",
576 /* 38 */ "#,##0 ;[Red](#,##0)",
577 /* 39 */ "#,##0.00;(#,##0.00)",
578 /* 40 */ "#,##0.00;[Red](#,##0.00)",
584 /* 46 */ "[h]:mm:ss",
591 g_return_if_fail (NUM_FORMAT_BASE
> (int) G_N_ELEMENTS (std_builtins
));
593 for (i
= 1; i
< G_N_ELEMENTS (std_builtins
); i
++)
594 if (std_builtins
[i
] != NULL
)
595 g_hash_table_insert (hash
, g_strdup (std_builtins
[i
]), GUINT_TO_POINTER (i
));
599 xlsx_write_num_format (XLSXWriteState
*state
, GsfXMLOut
*xml
,
600 GOFormat
const *format
, int id
)
602 gsf_xml_out_start_element (xml
, "numFmt");
603 gsf_xml_out_add_cstr (xml
, "formatCode", go_format_as_XL (format
));
604 gsf_xml_out_add_int (xml
, "numFmtId", id
);
605 gsf_xml_out_end_element (xml
);
609 xlsx_write_num_formats (XLSXWriteState
*state
, GsfXMLOut
*xml
)
611 GHashTable
*hash
= g_hash_table_new_full
612 (g_str_hash
, g_str_equal
, g_free
, NULL
);
613 GHashTable
*num_format_hash
= g_hash_table_new
614 (g_direct_hash
, g_direct_equal
);
615 unsigned int i
, count
;
616 GPtrArray
*num_format_array
= g_ptr_array_new ();
618 xlsx_load_buildin_num_formats (hash
);
620 for (i
= 0 ; i
< state
->styles_array
->len
; i
++) {
621 GnmStyle
const *style
= g_ptr_array_index (state
->styles_array
, i
);
622 GOFormat
const *format
;
626 if (!gnm_style_is_element_set (style
, MSTYLE_FORMAT
))
628 format
= gnm_style_get_format (style
);
629 if (go_format_is_general (format
))
631 xl
= go_format_as_XL (format
);
632 tmp
= g_hash_table_lookup (hash
, xl
);
634 tmp
= GUINT_TO_POINTER (num_format_array
->len
+ NUM_FORMAT_BASE
);
635 g_hash_table_insert (hash
, g_strdup (xl
), tmp
);
636 g_ptr_array_add (num_format_array
, (gpointer
)format
);
638 g_hash_table_insert (num_format_hash
, (gpointer
) style
, tmp
);
641 count
= num_format_array
->len
;
643 gsf_xml_out_start_element (xml
, "numFmts");
644 gsf_xml_out_add_int (xml
, "count", count
);
645 for (i
= 0; i
< count
; i
++) {
646 GOFormat
const *format
=
647 g_ptr_array_index (num_format_array
, i
);
648 xlsx_write_num_format (state
, xml
, format
,
649 i
+ NUM_FORMAT_BASE
);
651 gsf_xml_out_end_element (xml
);
653 g_ptr_array_free (num_format_array
, TRUE
);
654 g_hash_table_destroy (hash
);
655 return num_format_hash
;
659 xlsx_write_color_element (GsfXMLOut
*xml
, char const *id
, GOColor color
)
661 gsf_xml_out_start_element (xml
, id
);
662 xlsx_add_rgb (xml
, "rgb", color
);
663 gsf_xml_out_end_element (xml
);
667 xlsx_find_font (GnmStyle
const *style
, GPtrArray
*styles
)
670 for (i
= 0 ; i
< styles
->len
; i
++) {
671 GnmStyle
const *old_style
= g_ptr_array_index (styles
, i
);
672 if (style
== old_style
||
673 (gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_BOLD
) &&
674 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_ITALIC
) &&
675 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_UNDERLINE
) &&
676 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_COLOR
) &&
677 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_SIZE
) &&
678 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_SCRIPT
) &&
679 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_STRIKETHROUGH
) &&
680 gnm_style_equal_elem (style
, old_style
, MSTYLE_FONT_NAME
)))
687 xlsx_has_font_style (GnmStyle
const *style
)
689 return (gnm_style_is_element_set (style
, MSTYLE_FONT_BOLD
) ||
690 gnm_style_is_element_set (style
, MSTYLE_FONT_ITALIC
) ||
691 gnm_style_is_element_set (style
, MSTYLE_FONT_UNDERLINE
) ||
692 gnm_style_is_element_set (style
, MSTYLE_FONT_COLOR
) ||
693 gnm_style_is_element_set (style
, MSTYLE_FONT_NAME
) ||
694 gnm_style_is_element_set (style
, MSTYLE_FONT_SCRIPT
) ||
695 gnm_style_is_element_set (style
, MSTYLE_FONT_SIZE
) ||
696 gnm_style_is_element_set (style
, MSTYLE_FONT_STRIKETHROUGH
));
700 xlsx_write_font (XLSXWriteState
*state
, GsfXMLOut
*xml
, GnmStyle
const *style
)
702 gsf_xml_out_start_element (xml
, "font");
703 if (gnm_style_is_element_set (style
, MSTYLE_FONT_BOLD
)) {
704 gsf_xml_out_start_element (xml
, "b");
705 xlsx_add_bool (xml
, "val", gnm_style_get_font_bold (style
));
706 gsf_xml_out_end_element (xml
);
708 if (gnm_style_is_element_set (style
, MSTYLE_FONT_ITALIC
)) {
709 gsf_xml_out_start_element (xml
, "i");
710 xlsx_add_bool (xml
, "val", gnm_style_get_font_italic (style
));
711 gsf_xml_out_end_element (xml
);
713 if (gnm_style_is_element_set (style
, MSTYLE_FONT_UNDERLINE
)) {
714 static const char * const types
[] = {
721 unsigned uline
= gnm_style_get_font_uline (style
);
723 if (uline
< G_N_ELEMENTS (types
)) {
724 gsf_xml_out_start_element (xml
, "u");
726 (xml
, "val", types
[gnm_style_get_font_uline (style
)]);
727 gsf_xml_out_end_element (xml
);
730 if (gnm_style_is_element_set (style
, MSTYLE_FONT_COLOR
))
731 xlsx_write_color_element
733 gnm_style_get_font_color (style
)->go_color
);
734 if (gnm_style_is_element_set (style
, MSTYLE_FONT_NAME
)) {
735 gsf_xml_out_start_element (xml
, "name");
737 (xml
, "val", gnm_style_get_font_name (style
));
738 gsf_xml_out_end_element (xml
);
740 if (gnm_style_is_element_set (style
, MSTYLE_FONT_SCRIPT
)) {
741 GOFontScript scr
= gnm_style_get_font_script (style
);
745 case GO_FONT_SCRIPT_SUB
: val
= "subscript"; break;
746 case GO_FONT_SCRIPT_SUPER
: val
= "superscript"; break;
748 case GO_FONT_SCRIPT_STANDARD
: val
= "baseline"; break;
750 gsf_xml_out_start_element (xml
, "vertAlign");
751 gsf_xml_out_add_cstr (xml
, "val", val
);
752 gsf_xml_out_end_element (xml
);
754 if (gnm_style_is_element_set (style
, MSTYLE_FONT_SIZE
)) {
755 gsf_xml_out_start_element (xml
, "sz");
756 go_xml_out_add_double
757 (xml
, "val", gnm_style_get_font_size (style
));
758 gsf_xml_out_end_element (xml
);
761 if (gnm_style_is_element_set (style
, MSTYLE_FONT_STRIKETHROUGH
)) {
762 gsf_xml_out_start_element (xml
, "strike");
763 xlsx_add_bool (xml
, "val", gnm_style_get_font_strike (style
));
764 gsf_xml_out_end_element (xml
);
766 gsf_xml_out_end_element (xml
); /* font */
770 xlsx_write_fonts (XLSXWriteState
*state
, GsfXMLOut
*xml
)
772 GHashTable
*fonts_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
773 GPtrArray
*styles_w_fonts
= g_ptr_array_new ();
776 for (i
= 0 ; i
< state
->styles_array
->len
; i
++) {
777 GnmStyle
const *style
= g_ptr_array_index (state
->styles_array
, i
);
778 if (xlsx_has_font_style (style
)) {
779 gint font_n
= xlsx_find_font (style
, styles_w_fonts
);
781 g_ptr_array_add (styles_w_fonts
, (gpointer
)style
);
782 g_hash_table_insert (fonts_hash
, (gpointer
)style
,
783 GINT_TO_POINTER (styles_w_fonts
->len
));
785 g_hash_table_insert (fonts_hash
, (gpointer
)style
,
786 GINT_TO_POINTER (font_n
+ 1));
790 if (styles_w_fonts
->len
> 0) {
791 gsf_xml_out_start_element (xml
, "fonts");
792 gsf_xml_out_add_int (xml
, "count", styles_w_fonts
->len
);
793 for (i
= 0 ; i
< styles_w_fonts
->len
; i
++) {
794 GnmStyle
const *style
= g_ptr_array_index (styles_w_fonts
, i
);
795 xlsx_write_font (state
, xml
, style
);
797 gsf_xml_out_end_element (xml
);
800 g_ptr_array_free (styles_w_fonts
, TRUE
);
805 xlsx_find_fill (GnmStyle
const *style
, GPtrArray
*styles
)
810 j
= xlsx_find_predefined_fill (style
);
813 for (i
= 0 ; i
< styles
->len
; i
++) {
814 GnmStyle
const *old_style
= g_ptr_array_index (styles
, i
);
815 if (style
== old_style
||
816 (gnm_style_equal_elem (style
, old_style
, MSTYLE_COLOR_BACK
) &&
817 gnm_style_equal_elem (style
, old_style
, MSTYLE_COLOR_PATTERN
) &&
818 gnm_style_equal_elem (style
, old_style
, MSTYLE_PATTERN
)))
819 return (gint
) i
+ N_PREDEFINED_FILLS
;
825 xlsx_has_background_style (GnmStyle
const *style
)
827 return (gnm_style_is_element_set (style
, MSTYLE_COLOR_BACK
) ||
828 gnm_style_is_element_set (style
, MSTYLE_COLOR_PATTERN
) ||
829 gnm_style_is_element_set (style
, MSTYLE_PATTERN
));
833 xlsx_write_background (XLSXWriteState
*state
, GsfXMLOut
*xml
,
834 GnmStyle
const *style
, gboolean invert_solid
)
838 * Looks like pattern background and forground colours are inverted
839 * for dxfs with solid fills for no apparent reason.
841 gboolean invert
= FALSE
;
845 gsf_xml_out_start_element (xml
, "fill");
846 gsf_xml_out_start_element (xml
, "patternFill");
847 if (gnm_style_is_element_set (style
, MSTYLE_PATTERN
)) {
848 gint pattern
= gnm_style_get_pattern (style
);
850 if (pattern
<= 0 || pattern
> (gint
)G_N_ELEMENTS (pats
)) {
853 invert
= (pattern
== 1 && invert_solid
);
854 type
= pats
[pattern
- 1];
856 gsf_xml_out_add_cstr_unchecked (xml
, "patternType", type
);
859 fg
= gnm_style_is_element_set (style
, MSTYLE_COLOR_BACK
)
860 ? gnm_style_get_back_color (style
)
862 bg
= gnm_style_is_element_set (style
, MSTYLE_COLOR_PATTERN
)
863 ? gnm_style_get_pattern_color (style
)
872 xlsx_write_color_element (xml
, "fgColor", fg
->go_color
);
874 xlsx_write_color_element (xml
, "bgColor", bg
->go_color
);
876 gsf_xml_out_end_element (xml
);
877 gsf_xml_out_end_element (xml
);
881 xlsx_write_fills (XLSXWriteState
*state
, GsfXMLOut
*xml
)
883 GHashTable
*fills_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
884 GPtrArray
*styles_w_fills
= g_ptr_array_new ();
887 for (i
= 0 ; i
< state
->styles_array
->len
; i
++) {
888 GnmStyle
const *style
= g_ptr_array_index (state
->styles_array
, i
);
889 if (xlsx_has_background_style (style
)) {
890 gint fill_n
= xlsx_find_fill (style
, styles_w_fills
);
892 g_ptr_array_add (styles_w_fills
, (gpointer
)style
);
894 (fills_hash
, (gpointer
)style
,
895 GINT_TO_POINTER (styles_w_fills
->len
896 + N_PREDEFINED_FILLS
));
899 (fills_hash
, (gpointer
)style
,
905 gsf_xml_out_start_element (xml
, "fills");
906 gsf_xml_out_add_int (xml
, "count", styles_w_fills
->len
907 + N_PREDEFINED_FILLS
);
908 /* Excel considers the first few fills special (not according to */
910 xlsx_write_predefined_fills (xml
);
911 for (i
= 0 ; i
< styles_w_fills
->len
; i
++) {
912 GnmStyle
const *style
= g_ptr_array_index (styles_w_fills
, i
);
913 xlsx_write_background (state
, xml
, style
, FALSE
);
915 gsf_xml_out_end_element (xml
);
917 g_ptr_array_free (styles_w_fills
, TRUE
);
922 xlsx_find_border (GnmStyle
const *style
, GPtrArray
*styles
)
925 for (i
= 0 ; i
< styles
->len
; i
++) {
926 GnmStyle
const *old_style
= g_ptr_array_index (styles
, i
);
927 if (style
== old_style
||
928 (gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_TOP
) &&
929 gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_BOTTOM
) &&
930 gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_LEFT
) &&
931 gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_RIGHT
) &&
932 gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_DIAGONAL
) &&
933 gnm_style_equal_elem (style
, old_style
, MSTYLE_BORDER_REV_DIAGONAL
)))
940 xlsx_write_border (XLSXWriteState
*state
, GsfXMLOut
*xml
, GnmBorder
*border
, GnmStyleElement elem
)
945 case MSTYLE_BORDER_TOP
:
946 gsf_xml_out_start_element (xml
, "top");
948 case MSTYLE_BORDER_BOTTOM
:
949 gsf_xml_out_start_element (xml
, "bottom");
951 case MSTYLE_BORDER_LEFT
:
952 gsf_xml_out_start_element
953 (xml
, (state
->version
== ECMA_376_2006
) ? "left" : "start");
955 case MSTYLE_BORDER_DIAGONAL
:
956 case MSTYLE_BORDER_REV_DIAGONAL
:
957 gsf_xml_out_start_element (xml
, "diagonal");
960 case MSTYLE_BORDER_RIGHT
:
961 gsf_xml_out_start_element
962 (xml
, (state
->version
== ECMA_376_2006
) ? "right" : "end");
965 switch (border
->line_type
) {
966 case GNM_STYLE_BORDER_THIN
:
967 gsf_xml_out_add_cstr_unchecked (xml
, "style", "thin");
969 case GNM_STYLE_BORDER_MEDIUM
:
970 gsf_xml_out_add_cstr_unchecked (xml
, "style", "medium");
972 case GNM_STYLE_BORDER_DASHED
:
973 gsf_xml_out_add_cstr_unchecked (xml
, "style", "dashed");
975 case GNM_STYLE_BORDER_DOTTED
:
976 gsf_xml_out_add_cstr_unchecked (xml
, "style", "dotted");
978 case GNM_STYLE_BORDER_THICK
:
979 gsf_xml_out_add_cstr_unchecked (xml
, "style", "thick");
981 case GNM_STYLE_BORDER_DOUBLE
:
982 gsf_xml_out_add_cstr_unchecked (xml
, "style", "double");
984 case GNM_STYLE_BORDER_HAIR
:
985 gsf_xml_out_add_cstr_unchecked (xml
, "style", "hair");
987 case GNM_STYLE_BORDER_MEDIUM_DASH
:
988 gsf_xml_out_add_cstr_unchecked (xml
, "style", "mediumDashed");
990 case GNM_STYLE_BORDER_DASH_DOT
:
991 gsf_xml_out_add_cstr_unchecked (xml
, "style", "dashDot");
993 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT
:
994 gsf_xml_out_add_cstr_unchecked (xml
, "style", "mediumDashDot");
996 case GNM_STYLE_BORDER_DASH_DOT_DOT
:
997 gsf_xml_out_add_cstr_unchecked (xml
, "style", "dashDotDot");
999 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT
:
1000 gsf_xml_out_add_cstr_unchecked (xml
, "style", "mediumDashDotDot");
1002 case GNM_STYLE_BORDER_SLANTED_DASH_DOT
:
1003 gsf_xml_out_add_cstr_unchecked (xml
, "style", "slantDashDot");
1006 case GNM_STYLE_BORDER_NONE
:
1007 gsf_xml_out_add_cstr_unchecked (xml
, "style", "none");
1011 if (border
->color
!= NULL
)
1012 xlsx_write_color_element (xml
, "color", border
->color
->go_color
);
1014 gsf_xml_out_end_element (xml
);
1018 xlsx_has_border_style (GnmStyle
const *style
)
1020 return (gnm_style_is_element_set (style
, MSTYLE_BORDER_TOP
) ||
1021 gnm_style_is_element_set (style
, MSTYLE_BORDER_BOTTOM
) ||
1022 gnm_style_is_element_set (style
, MSTYLE_BORDER_LEFT
) ||
1023 gnm_style_is_element_set (style
, MSTYLE_BORDER_RIGHT
) ||
1024 gnm_style_is_element_set (style
, MSTYLE_BORDER_REV_DIAGONAL
) ||
1025 gnm_style_is_element_set (style
, MSTYLE_BORDER_DIAGONAL
));
1029 xlsx_write_full_border (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1030 GnmStyle
const *style
)
1032 gboolean diagonal_border_written
= FALSE
;
1033 gsf_xml_out_start_element (xml
, "border");
1034 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_DIAGONAL
)) {
1035 GnmBorder
*border
= gnm_style_get_border
1036 (style
, MSTYLE_BORDER_DIAGONAL
);
1037 gsf_xml_out_add_bool
1039 (border
&& border
->line_type
!= GNM_STYLE_BORDER_NONE
));
1041 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_REV_DIAGONAL
)) {
1042 GnmBorder
*border
= gnm_style_get_border
1043 (style
, MSTYLE_BORDER_REV_DIAGONAL
);
1044 gsf_xml_out_add_bool
1045 (xml
, "diagonalDown",
1046 (border
&& border
->line_type
!= GNM_STYLE_BORDER_NONE
));
1048 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_LEFT
))
1049 xlsx_write_border (state
, xml
,
1050 gnm_style_get_border
1051 (style
, MSTYLE_BORDER_LEFT
),
1052 MSTYLE_BORDER_LEFT
);
1053 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_RIGHT
))
1054 xlsx_write_border (state
, xml
,
1055 gnm_style_get_border
1056 (style
, MSTYLE_BORDER_RIGHT
),
1057 MSTYLE_BORDER_RIGHT
);
1058 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_TOP
))
1059 xlsx_write_border (state
, xml
,
1060 gnm_style_get_border
1061 (style
, MSTYLE_BORDER_TOP
),
1063 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_BOTTOM
))
1064 xlsx_write_border (state
, xml
,
1065 gnm_style_get_border
1066 (style
, MSTYLE_BORDER_BOTTOM
),
1067 MSTYLE_BORDER_BOTTOM
);
1068 if (gnm_style_is_element_set (style
, MSTYLE_BORDER_DIAGONAL
)) {
1069 GnmBorder
*border
= gnm_style_get_border
1070 (style
, MSTYLE_BORDER_DIAGONAL
);
1071 if (border
&& border
->line_type
!= GNM_STYLE_BORDER_NONE
) {
1072 diagonal_border_written
= TRUE
;
1073 xlsx_write_border (state
, xml
,
1075 MSTYLE_BORDER_DIAGONAL
);
1078 if (!diagonal_border_written
&&
1079 gnm_style_is_element_set (style
, MSTYLE_BORDER_REV_DIAGONAL
)) {
1080 GnmBorder
*border
= gnm_style_get_border
1081 (style
, MSTYLE_BORDER_REV_DIAGONAL
);
1082 if (border
&& border
->line_type
!= GNM_STYLE_BORDER_NONE
) {
1083 xlsx_write_border (state
, xml
,
1085 MSTYLE_BORDER_REV_DIAGONAL
);
1088 gsf_xml_out_end_element (xml
); /* border */
1093 xlsx_write_borders (XLSXWriteState
*state
, GsfXMLOut
*xml
)
1095 GHashTable
*border_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1096 GPtrArray
*styles_w_border
= g_ptr_array_new ();
1099 for (i
= 0 ; i
< state
->styles_array
->len
; i
++) {
1100 GnmStyle
const *style
= g_ptr_array_index (state
->styles_array
, i
);
1101 if (xlsx_has_border_style (style
)) {
1102 gint border_n
= xlsx_find_border (style
, styles_w_border
);
1104 g_ptr_array_add (styles_w_border
, (gpointer
)style
);
1105 g_hash_table_insert (border_hash
, (gpointer
)style
,
1106 GINT_TO_POINTER (styles_w_border
->len
));
1108 g_hash_table_insert (border_hash
, (gpointer
)style
,
1109 GINT_TO_POINTER (border_n
+ 1));
1113 if (styles_w_border
->len
> 0) {
1114 gsf_xml_out_start_element (xml
, "borders");
1115 gsf_xml_out_add_int (xml
, "count", styles_w_border
->len
);
1116 for (i
= 0; i
< styles_w_border
->len
; i
++) {
1117 GnmStyle
const *style
= g_ptr_array_index (styles_w_border
, i
);
1118 xlsx_write_full_border (state
, xml
, style
);
1120 gsf_xml_out_end_element (xml
);
1123 g_ptr_array_free (styles_w_border
, TRUE
);
1128 xlsx_has_alignment_style (GnmStyle
const *style
)
1130 return gnm_style_is_element_set (style
, MSTYLE_ALIGN_H
)
1131 || gnm_style_is_element_set (style
, MSTYLE_ALIGN_V
)
1132 || gnm_style_is_element_set (style
, MSTYLE_WRAP_TEXT
)
1133 || gnm_style_is_element_set (style
, MSTYLE_SHRINK_TO_FIT
)
1134 || gnm_style_is_element_set (style
, MSTYLE_ROTATION
)
1135 || gnm_style_is_element_set (style
, MSTYLE_INDENT
);
1139 xlsx_write_style_write_alignment (G_GNUC_UNUSED XLSXWriteState
*state
, GsfXMLOut
*xml
,
1140 GnmStyle
const *style
)
1142 gsf_xml_out_start_element (xml
, "alignment");
1143 if (gnm_style_is_element_set (style
, MSTYLE_ALIGN_H
)) {
1144 switch (gnm_style_get_align_h (style
)) {
1145 case GNM_HALIGN_LEFT
:
1146 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1149 case GNM_HALIGN_RIGHT
:
1150 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1153 case GNM_HALIGN_CENTER
:
1154 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1157 case GNM_HALIGN_FILL
:
1158 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1161 case GNM_HALIGN_JUSTIFY
:
1162 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1165 case GNM_HALIGN_CENTER_ACROSS_SELECTION
:
1166 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1167 "centerContinuous");
1169 case GNM_HALIGN_DISTRIBUTED
:
1170 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1173 case GNM_HALIGN_GENERAL
:
1175 gsf_xml_out_add_cstr_unchecked (xml
, "horizontal",
1180 if (gnm_style_is_element_set (style
, MSTYLE_ALIGN_V
)) {
1181 switch (gnm_style_get_align_v (style
)) {
1182 case GNM_VALIGN_TOP
:
1183 gsf_xml_out_add_cstr_unchecked (xml
, "vertical",
1186 case GNM_VALIGN_BOTTOM
:
1187 gsf_xml_out_add_cstr_unchecked (xml
, "vertical",
1190 case GNM_VALIGN_CENTER
:
1191 gsf_xml_out_add_cstr_unchecked (xml
, "vertical",
1194 case GNM_VALIGN_JUSTIFY
:
1195 gsf_xml_out_add_cstr_unchecked (xml
, "vertical",
1199 case GNM_VALIGN_DISTRIBUTED
:
1200 gsf_xml_out_add_cstr_unchecked (xml
, "vertical",
1205 if (gnm_style_is_element_set (style
, MSTYLE_WRAP_TEXT
)) {
1206 gsf_xml_out_add_bool (xml
, "wrapText", gnm_style_get_wrap_text (style
));
1208 if (gnm_style_is_element_set (style
, MSTYLE_SHRINK_TO_FIT
)) {
1209 gsf_xml_out_add_bool (xml
, "shrinkToFit", gnm_style_get_shrink_to_fit (style
));
1211 if (gnm_style_is_element_set (style
, MSTYLE_ROTATION
)) {
1212 int r
= gnm_style_get_rotation (style
);
1217 gsf_xml_out_add_int (xml
, "textRotation", r
);
1219 if (gnm_style_is_element_set (style
, MSTYLE_INDENT
)) {
1220 gsf_xml_out_add_int (xml
, "indent", gnm_style_get_indent (style
));
1222 gsf_xml_out_end_element (xml
);
1226 xlsx_write_style (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1227 GnmStyle
const *style
, GHashTable
*fills_hash
,
1228 GHashTable
*num_format_hash
, GHashTable
*fonts_hash
,
1229 GHashTable
*border_hash
, gint id
)
1231 gboolean alignment
= xlsx_has_alignment_style (style
);
1232 gpointer tmp_fill
, tmp_font
, tmp_border
;
1233 gboolean fill
= (NULL
!= (tmp_fill
= g_hash_table_lookup (fills_hash
, style
)));
1234 gboolean font
= (NULL
!= (tmp_font
= g_hash_table_lookup (fonts_hash
, style
)));
1235 gboolean border
= (NULL
!= (tmp_border
= g_hash_table_lookup (border_hash
, style
)));
1236 gboolean num_fmt
= gnm_style_is_element_set (style
, MSTYLE_FORMAT
);
1239 xlsx_add_bool (xml
, "applyAlignment", alignment
);
1240 xlsx_add_bool (xml
, "applyBorder", border
);
1241 xlsx_add_bool (xml
, "applyFont", font
);
1242 xlsx_add_bool (xml
, "applyFill", fill
);
1243 xlsx_add_bool (xml
, "applyNumberFormat", num_fmt
);
1246 gsf_xml_out_add_int (xml
, "fontId", GPOINTER_TO_INT (tmp_font
) - 1);
1248 gsf_xml_out_add_int (xml
, "fillId", GPOINTER_TO_INT (tmp_fill
) - 1);
1250 gsf_xml_out_add_int (xml
, "borderId", GPOINTER_TO_INT (tmp_border
) - 1);
1254 GPOINTER_TO_INT (g_hash_table_lookup (num_format_hash
, style
)));
1256 gsf_xml_out_add_int (xml
, "xfId", id
);
1259 xlsx_write_style_write_alignment (state
, xml
, style
);
1263 xlsx_write_cellStyleXfs (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1264 GHashTable
*fills_hash
, GHashTable
*num_format_hash
,
1265 GHashTable
*fonts_hash
, GHashTable
*border_hash
)
1267 gsf_xml_out_start_element (xml
, "cellStyleXfs");
1268 gsf_xml_out_add_int (xml
, "count", 1);
1269 gsf_xml_out_start_element (xml
, "xf");
1272 g_ptr_array_index (state
->styles_array
, 0),
1273 fills_hash
, num_format_hash
, fonts_hash
,
1275 gsf_xml_out_end_element (xml
);
1276 gsf_xml_out_end_element (xml
);
1280 xlsx_write_cellXfs (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1281 GHashTable
*fills_hash
, GHashTable
*num_format_hash
,
1282 GHashTable
*fonts_hash
, GHashTable
*border_hash
)
1284 unsigned i
, N
= state
->styles_array
->len
;
1289 gsf_xml_out_start_element (xml
, "cellXfs");
1290 gsf_xml_out_add_int (xml
, "count", N
);
1291 for (i
= 0; i
< N
; i
++) {
1292 GnmStyle
const *style
=
1293 g_ptr_array_index (state
->styles_array
, i
);
1294 gsf_xml_out_start_element (xml
, "xf");
1297 fills_hash
, num_format_hash
, fonts_hash
,
1299 gsf_xml_out_end_element (xml
);
1301 gsf_xml_out_end_element (xml
);
1305 xlsx_write_dxfs (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1306 GHashTable
*num_format_hash
)
1308 unsigned i
, N
= state
->dxfs_array
->len
;
1309 int id
= NUM_FORMAT_BASE
+ g_hash_table_size (num_format_hash
);
1314 gsf_xml_out_start_element (xml
, "dxfs");
1315 gsf_xml_out_add_int (xml
, "count", N
);
1316 for (i
= 0; i
< N
; i
++) {
1318 g_ptr_array_index (state
->dxfs_array
, i
);
1319 gsf_xml_out_start_element (xml
, "dxf");
1320 if (xlsx_has_font_style (style
))
1321 xlsx_write_font (state
, xml
, style
);
1322 if (xlsx_has_alignment_style (style
))
1323 xlsx_write_style_write_alignment (state
, xml
, style
);
1324 if (xlsx_has_background_style (style
))
1325 xlsx_write_background (state
, xml
, style
, TRUE
);
1326 if (gnm_style_is_element_set (style
, MSTYLE_FORMAT
)) {
1327 GOFormat
const *format
= gnm_style_get_format (style
);
1328 xlsx_write_num_format (state
, xml
, format
, id
++);
1330 if (xlsx_has_border_style (style
))
1331 xlsx_write_full_border (state
, xml
, style
);
1333 gsf_xml_out_end_element (xml
);
1335 gsf_xml_out_end_element (xml
);
1339 xlsx_write_styles (XLSXWriteState
*state
, GsfOutfile
*wb_part
)
1341 GsfOutput
*part
= gsf_outfile_open_pkg_add_rel (state
->xl_dir
, "styles.xml",
1342 "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
1344 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles");
1345 GsfXMLOut
*xml
= gsf_xml_out_new (part
);
1346 GHashTable
*fills_hash
, *num_format_hash
, *fonts_hash
, *border_hash
;
1348 gsf_xml_out_start_element (xml
, "styleSheet");
1349 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns", ns_ss
);
1350 /* Note the schema does not allow the attribute xml:space */
1352 /* The order of elements is fixed in the schema (xsd:sequence) */
1353 num_format_hash
= xlsx_write_num_formats (state
, xml
);
1354 fonts_hash
= xlsx_write_fonts (state
, xml
);
1355 fills_hash
= xlsx_write_fills (state
, xml
);
1356 border_hash
= xlsx_write_borders (state
, xml
);
1357 xlsx_write_cellStyleXfs (state
, xml
, fills_hash
, num_format_hash
, fonts_hash
, border_hash
);
1358 xlsx_write_cellXfs (state
, xml
, fills_hash
, num_format_hash
, fonts_hash
, border_hash
);
1359 /* <xsd:element name="cellStyles" type="CT_CellStyles" minOccurs="0" maxOccurs="1"/> */
1360 xlsx_write_dxfs (state
, xml
, num_format_hash
);
1361 /* <xsd:element name="tableStyles" type="CT_TableStyles" minOccurs="0" maxOccurs="1"/> */
1362 /* <xsd:element name="colors" type="CT_Colors" minOccurs="0" maxOccurs="1"/> */
1363 /* <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/> */
1365 g_hash_table_destroy (fills_hash
);
1366 g_hash_table_destroy (fonts_hash
);
1367 g_hash_table_destroy (num_format_hash
);
1368 g_hash_table_destroy (border_hash
);
1369 gsf_xml_out_end_element (xml
); /* </styleSheet> */
1370 g_object_unref (xml
);
1371 gsf_output_close (part
);
1372 g_object_unref (part
);
1375 #define XLSX_MAX_COLS gnm_sheet_get_max_cols (state->sheet) /* default is (2^14) */
1376 #define XLSX_MAX_ROWS gnm_sheet_get_max_rows (state->sheet) /* default is (2^20) */
1379 xlsx_write_sheet_view (GsfXMLOut
*xml
, SheetView
const *sv
)
1381 Sheet
const *sheet
= sv_sheet (sv
);
1382 GnmColor
*sheet_auto
= sheet_style_get_auto_pattern_color (sheet
);
1383 GnmColor
*default_auto
= style_color_auto_pattern ();
1384 GnmCellPos topLeft
, frozen_topLeft
;
1385 char const *activePane
= NULL
;
1386 int const frozen_height
= sv
->unfrozen_top_left
.row
-
1387 sv
->frozen_top_left
.row
;
1388 int const frozen_width
= sv
->unfrozen_top_left
.col
-
1389 sv
->frozen_top_left
.col
;
1392 if (frozen_width
> 0) {
1393 topLeft
.col
= sv
->frozen_top_left
.col
;
1394 frozen_topLeft
.col
= sv
->initial_top_left
.col
;
1396 topLeft
.col
= sv
->initial_top_left
.col
;
1397 frozen_topLeft
.col
= sv
->frozen_top_left
.col
;
1399 if (frozen_height
> 0) {
1400 topLeft
.row
= sv
->frozen_top_left
.row
;
1401 frozen_topLeft
.row
= sv
->initial_top_left
.row
;
1403 topLeft
.row
= sv
->initial_top_left
.row
;
1404 frozen_topLeft
.row
= sv
->frozen_top_left
.row
;
1407 gsf_xml_out_start_element (xml
, "sheetView");
1408 if (topLeft
.col
> 0 || topLeft
.row
> 0) /* A1 is the default */
1409 xlsx_add_pos (xml
, "topLeftCell", &topLeft
);
1410 gsf_xml_out_add_int (xml
, "workbookViewId",
1411 wb_view_get_index_in_wb (sv_wbv (sv
)));
1413 tmp
= (int) (100.* sheet
->last_zoom_factor_used
+ .5);
1415 gsf_xml_out_add_int (xml
, "zoomScale", tmp
);
1417 switch (sv
->view_mode
) {
1418 case GNM_SHEET_VIEW_NORMAL_MODE
: break;
1419 case GNM_SHEET_VIEW_PAGE_BREAK_MODE
:
1420 gsf_xml_out_add_cstr_unchecked (xml
, "view", "pageBreakPreview"); break;
1421 case GNM_SHEET_VIEW_LAYOUT_MODE
:
1422 gsf_xml_out_add_cstr_unchecked (xml
, "view", "pageLayout"); break;
1425 if (sheet
->hide_grid
)
1426 gsf_xml_out_add_cstr_unchecked (xml
, "showGridLines", "0");
1427 if (sheet
->display_formulas
)
1428 gsf_xml_out_add_cstr_unchecked (xml
, "showFormulas", "1");
1429 if (sheet
->hide_col_header
|| sheet
->hide_row_header
)
1430 gsf_xml_out_add_cstr_unchecked (xml
, "showRowColHeaders", "0");
1431 if (sheet
->hide_zero
)
1432 gsf_xml_out_add_cstr_unchecked (xml
, "showZeros", "0");
1433 if (!sheet
->display_outlines
)
1434 gsf_xml_out_add_cstr_unchecked (xml
, "showOutlineSymbols", "0");
1435 if (sheet
->text_is_rtl
)
1436 gsf_xml_out_add_cstr_unchecked (xml
, "rightToLeft", "1");
1437 if (sheet
== wb_view_cur_sheet (sv_wbv (sv
)))
1438 gsf_xml_out_add_cstr_unchecked (xml
, "tabSelected", "1");
1440 if (!style_color_equal (sheet_auto
, default_auto
)) {
1441 gsf_xml_out_add_cstr_unchecked (xml
, "defaultGridColor", "1");
1443 gsf_xml_out_add_int (xml
, "colorId", grid_color_index
);
1446 style_color_unref (sheet_auto
);
1447 style_color_unref (default_auto
);
1449 if (sv_is_frozen (sv
)) {
1450 activePane
= "bottomRight"; /* h&v freeze */
1452 gsf_xml_out_start_element (xml
, "pane");
1453 if (frozen_width
> 0)
1454 gsf_xml_out_add_int (xml
, "xSplit", frozen_width
);
1456 activePane
= "bottomLeft"; /* v freeze */
1457 if (frozen_height
> 0)
1458 gsf_xml_out_add_int (xml
, "ySplit", frozen_height
);
1460 activePane
= "topRight"; /* h freeze */
1461 xlsx_add_pos (xml
, "topLeftCell", &frozen_topLeft
);
1462 gsf_xml_out_add_cstr_unchecked (xml
, "activePane", activePane
);
1463 gsf_xml_out_add_cstr_unchecked (xml
, "state", "frozen");
1464 gsf_xml_out_end_element (xml
); /* </pane> */
1467 gsf_xml_out_start_element (xml
, "selection");
1468 if (NULL
!= activePane
)
1469 gsf_xml_out_add_cstr_unchecked (xml
, "pane", activePane
);
1470 /* activeCellId is always 0 for gnumeric */
1471 xlsx_add_pos (xml
, "activeCell", &sv
->edit_pos
);
1472 xlsx_add_range_list (xml
, "sqref", sv
->selections
);
1473 gsf_xml_out_end_element (xml
); /* </selection> */
1475 gsf_xml_out_end_element (xml
); /* </sheetView> */
1479 xlsx_write_init_row (gboolean
*needs_row
, GsfXMLOut
*xml
, int r
, char const *span
)
1482 gsf_xml_out_start_element (xml
, "row");
1483 gsf_xml_out_add_int (xml
, "r", r
+1);
1484 gsf_xml_out_add_cstr_unchecked (xml
, "spans", span
);
1490 xlsx_get_style_id (XLSXWriteState
*state
, GnmStyle
const *style
)
1494 g_return_val_if_fail (style
!= NULL
, 0);
1496 if (NULL
== (tmp
= g_hash_table_lookup (state
->styles_hash
, style
))) {
1497 g_ptr_array_add (state
->styles_array
, (gpointer
) style
);
1498 tmp
= GINT_TO_POINTER (state
->styles_array
->len
);
1499 gnm_style_ref (style
);
1500 g_hash_table_insert (state
->styles_hash
, (gpointer
) style
, tmp
);
1502 return GPOINTER_TO_INT (tmp
) - 1;
1506 xlsx_get_cond_style_id (XLSXWriteState
*state
, GnmStyle
const *style
)
1510 g_return_val_if_fail (style
!= NULL
, 0);
1512 if (NULL
== (tmp
= g_hash_table_lookup (state
->dxfs_hash
, style
))) {
1513 g_ptr_array_add (state
->dxfs_array
, (gpointer
)style
);
1514 tmp
= GINT_TO_POINTER (state
->dxfs_array
->len
);
1515 g_hash_table_insert (state
->dxfs_hash
, (gpointer
)style
, tmp
);
1517 return GPOINTER_TO_INT (tmp
) - 1;
1521 row_boring (Sheet
*sheet
, int r
)
1523 ColRowInfo
const *ri
= sheet_row_get (sheet
, r
);
1527 return (!ri
->hard_size
&&
1528 fabs (ri
->size_pts
- sheet
->rows
.default_style
.size_pts
) < 1e-6 &&
1529 !ri
->is_collapsed
&&
1531 ri
->outline_level
== 0);
1535 xlsx_write_cells (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1536 GnmRange
const *extent
, GnmStyle
**col_styles
)
1541 GnmExprTop
const *texpr
;
1542 char *cheesy_span
= g_strdup_printf ("%d:%d", extent
->start
.col
+1, extent
->end
.col
+1);
1543 Sheet
*sheet
= (Sheet
*)state
->sheet
;
1544 GPtrArray
*all_cells
= sheet_cells (sheet
, extent
);
1547 guint8
*non_defaults_rows
= sheet_style_get_nondefault_rows (sheet
, col_styles
);
1549 boring_count
= g_new0 (int, extent
->end
.row
+ 1);
1550 r
= extent
->end
.row
;
1551 boring_count
[r
] = row_boring (sheet
, r
);
1552 while (r
-- > extent
->start
.row
)
1553 boring_count
[r
] = row_boring (sheet
, r
)
1554 ? 1 + boring_count
[r
+ 1]
1557 /* Add a NULL to simplify code. */
1558 g_ptr_array_add (all_cells
, NULL
);
1560 gsf_xml_out_start_element (xml
, "sheetData");
1561 for (r
= extent
->start
.row
; r
<= extent
->end
.row
; r
++) {
1562 gboolean needs_row
= TRUE
;
1564 if (boring_count
[r
] == 0) {
1565 ColRowInfo
const *ri
= sheet_row_get (sheet
, r
);
1567 /* The code here needs to match row_boring. */
1569 if (ri
->hard_size
) {
1570 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1571 gsf_xml_out_add_cstr_unchecked (xml
, "customHeight", "1");
1573 if (ri
->hard_size
|| fabs (ri
->size_pts
- sheet
->cols
.default_style
.size_pts
) > 1e-6) {
1574 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1575 go_xml_out_add_double (xml
, "ht", ri
->size_pts
);
1577 if (ri
->is_collapsed
) {
1578 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1579 gsf_xml_out_add_cstr_unchecked (xml
, "collapsed", "1");
1582 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1583 gsf_xml_out_add_cstr_unchecked (xml
, "hidden", "1");
1585 if (ri
->outline_level
> 0) {
1586 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1587 gsf_xml_out_add_int (xml
, "outlineLevel", ri
->outline_level
);
1592 * If we didn't have to write anything yet and if the whole
1593 * row -- and possibly the ones after it -- are all
1594 * using default style, skip them.
1597 GnmCell
*cell
= g_ptr_array_index (all_cells
, cno
);
1598 int dr
, rows
= (cell
? cell
->pos
.row
: extent
->end
.row
+ 1) - r
;
1599 rows
= MIN (rows
, boring_count
[r
]);
1600 for (dr
= 0; dr
< rows
; dr
++)
1601 if (non_defaults_rows
[r
+ dr
])
1603 rows
= MIN (rows
, dr
);
1610 for (c
= extent
->start
.col
; c
<= extent
->end
.col
; c
++) {
1612 GnmValue
const *val
;
1613 GnmStyle
const *style
;
1614 GnmStyle
*style1
= NULL
;
1616 GOFormat
const *fmt1
, *fmt2
;
1618 cell
= g_ptr_array_index (all_cells
, cno
);
1619 if (cell
&& cell
->pos
.row
== r
&& cell
->pos
.col
== c
) {
1627 /* FIXME: Use sheet_style_get_row once per row */
1628 style
= sheet_style_get (sheet
, c
, r
);
1629 fmt1
= gnm_style_get_format (style
);
1630 fmt2
= cell
? gnm_cell_get_format_given_style (cell
, style
) : fmt1
;
1631 if (fmt1
!= fmt2
&& !go_format_is_markup (fmt2
)) {
1632 style
= style1
= gnm_style_dup (style
);
1633 gnm_style_set_format (style1
, fmt2
);
1635 style_id
= style
&& style
!= col_styles
[c
]
1636 ? xlsx_get_style_id (state
, style
)
1639 gnm_style_unref (style1
);
1643 gboolean inlineStr
= FALSE
;
1646 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1647 gsf_xml_out_start_element (xml
, "c");
1648 gsf_xml_out_add_cstr_unchecked (xml
, "r",
1649 cell_coord_name (c
, r
));
1651 gsf_xml_out_add_int (xml
, "s", style_id
);
1653 switch (val
->v_any
.type
) {
1656 type
= NULL
; /* FIXME : what to do ? */
1662 type
= ""; /* "n" is the default */
1668 /* A reasonable approximation of * 'is_shared'. It can get spoofed by
1669 * rich text references to a base * string */
1670 if (go_string_get_ref_count (val
->v_str
.val
) > 1) {
1671 str_id
= xlsx_shared_string (state
, val
);
1673 } else if (gnm_cell_has_expr (cell
))
1680 case VALUE_CELLRANGE
:
1682 type
= NULL
; /* FIXME */
1686 if (NULL
!= type
&& *type
)
1687 gsf_xml_out_add_cstr_unchecked (xml
, "t", type
);
1689 if (gnm_cell_has_expr (cell
)) {
1690 texpr
= cell
->base
.texpr
;
1691 if (!gnm_expr_top_is_array_elem (texpr
, NULL
, NULL
)) {
1692 gsf_xml_out_start_element (xml
, "f");
1694 if (gnm_expr_top_is_array_corner (texpr
)) {
1698 gnm_expr_top_get_array_size (texpr
, &cols
, &rows
);
1699 range_init_cellpos_size (&r
, &cell
->pos
,
1701 gsf_xml_out_add_cstr_unchecked (xml
, "t", "array");
1702 xlsx_add_range (xml
, "ref", &r
);
1704 content
= gnm_expr_top_as_string (cell
->base
.texpr
,
1705 parse_pos_init_cell (&pp
, cell
), state
->convs
);
1706 gsf_xml_out_add_cstr (xml
, NULL
, content
);
1709 gsf_xml_out_end_element (xml
); /* </f> */
1714 GOFormat
const *fmt
= VALUE_FMT (val
);
1715 PangoAttrList
*attrs
=
1716 fmt
&& go_format_is_markup (fmt
)
1717 ? (PangoAttrList
*)go_format_get_markup (VALUE_FMT (val
))
1720 gsf_xml_out_start_element (xml
, "is");
1721 xlsx_write_rich_text (xml
,
1722 value_peek_string (val
),
1725 gsf_xml_out_end_element (xml
); /* </is> */
1727 gsf_xml_out_start_element (xml
, "v");
1729 gsf_xml_out_add_int (xml
, NULL
, str_id
);
1730 else if (VALUE_IS_BOOLEAN (val
))
1731 xlsx_add_bool (xml
, NULL
, value_get_as_int (val
));
1733 GString
*str
= g_string_new (NULL
);
1734 value_get_as_gstring (cell
->value
, str
, state
->convs
);
1735 gsf_xml_out_add_cstr (xml
, NULL
, str
->str
);
1736 g_string_free (str
, TRUE
);
1738 gsf_xml_out_end_element (xml
); /* </v> */
1741 gsf_xml_out_end_element (xml
); /* </c> */
1742 } else if (style_id
> -1) {
1743 xlsx_write_init_row (&needs_row
, xml
, r
, cheesy_span
);
1744 gsf_xml_out_start_element (xml
, "c");
1745 gsf_xml_out_add_cstr_unchecked (xml
, "r",
1746 cell_coord_name (c
, r
));
1747 gsf_xml_out_add_int (xml
, "s", style_id
);
1748 gsf_xml_out_end_element (xml
); /* </c> */
1752 gsf_xml_out_end_element (xml
); /* </row> */
1754 gsf_xml_out_end_element (xml
); /* </sheetData> */
1755 g_free (non_defaults_rows
);
1756 g_free (boring_count
);
1757 g_ptr_array_free (all_cells
, TRUE
);
1758 g_free (cheesy_span
);
1762 xlsx_write_merges (XLSXWriteState
*state
, GsfXMLOut
*xml
)
1766 if (NULL
!= (ptr
= state
->sheet
->list_merged
)) {
1767 gsf_xml_out_start_element (xml
, "mergeCells");
1768 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
1769 gsf_xml_out_start_element (xml
, "mergeCell");
1770 xlsx_add_range (xml
, "ref", ptr
->data
);
1771 gsf_xml_out_end_element (xml
); /* </mergeCell> */
1773 gsf_xml_out_end_element (xml
); /* </mergeCells> */
1778 xlsx_write_cond_rule (XLSXWriteState
*state
, GsfXMLOut
*xml
,
1779 GnmStyleCond
*cond
, GnmParsePos
const *pp
)
1782 const char *type
= NULL
;
1783 const char *operator = NULL
;
1784 GnmExprTop
const *alt_texpr
;
1785 gboolean needs_alt_texpr
= FALSE
;
1787 gsf_xml_out_start_element (xml
, "cfRule");
1789 case GNM_STYLE_COND_BETWEEN
:
1790 n
= 2; operator = "between";
1792 case GNM_STYLE_COND_NOT_BETWEEN
:
1793 n
= 2; operator = "notBetween";
1795 case GNM_STYLE_COND_EQUAL
:
1796 n
= 1; operator = "equal";
1798 case GNM_STYLE_COND_NOT_EQUAL
:
1799 n
= 1; operator = "notEqual";
1801 case GNM_STYLE_COND_GT
:
1802 n
= 1; operator = "greaterThan";
1804 case GNM_STYLE_COND_LT
:
1805 n
= 1; operator = "lessThan";
1807 case GNM_STYLE_COND_GTE
:
1808 n
= 1; operator = "greaterThanOrEqual";
1810 case GNM_STYLE_COND_LTE
:
1811 n
= 1; operator = "lessThanOrEqual";
1813 case GNM_STYLE_COND_CONTAINS_BLANKS
:
1814 needs_alt_texpr
= TRUE
;
1815 n
= 1; type
= "containsBlanks";
1817 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
1818 needs_alt_texpr
= TRUE
;
1819 n
= 1; type
= "notContainsBlanks";
1821 case GNM_STYLE_COND_CONTAINS_ERR
:
1822 needs_alt_texpr
= TRUE
;
1823 n
= 1; type
= "containsErrors";
1825 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
1826 needs_alt_texpr
= TRUE
;
1827 n
= 1; type
= "notContainsErrors";
1829 case GNM_STYLE_COND_CUSTOM
:
1830 n
= 1; type
= "expression";
1832 case GNM_STYLE_COND_CONTAINS_STR
:
1833 needs_alt_texpr
= TRUE
;
1834 n
= 1; type
= "containsText";
1836 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
1837 needs_alt_texpr
= TRUE
;
1838 n
= 1; type
= "notContainsText";
1840 case GNM_STYLE_COND_BEGINS_WITH_STR
:
1841 needs_alt_texpr
= TRUE
;
1842 n
= 1; type
= "beginsWith";
1844 case GNM_STYLE_COND_ENDS_WITH_STR
:
1845 needs_alt_texpr
= TRUE
;
1846 n
= 1; type
= "endsWith";
1849 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
1850 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
1851 needs_alt_texpr
= TRUE
;
1852 n
= 1; type
= "expression";
1856 g_assert_not_reached ();
1859 alt_texpr
= needs_alt_texpr
1860 ? gnm_style_cond_get_alternate_expr (cond
)
1863 gsf_xml_out_add_cstr_unchecked (xml
, "type", type
? type
: "cellIs");
1864 gsf_xml_out_add_int (xml
, "dxfId",
1865 xlsx_get_cond_style_id (state
, cond
->overlay
));
1866 gsf_xml_out_add_int (xml
, "priority", 1) ;
1867 gsf_xml_out_add_int (xml
, "stopIfTrue", 1) ;
1869 gsf_xml_out_add_cstr_unchecked (xml
, "operator", operator);
1870 for (i
= 0; i
< n
; i
++) {
1871 GnmExprTop
const *texpr
= alt_texpr
1873 : gnm_style_cond_get_expr (cond
, i
);
1874 char *str
= gnm_expr_top_as_string (texpr
, pp
, state
->convs
);
1875 gsf_xml_out_simple_element (xml
, "formula", str
);
1879 gnm_expr_top_unref (alt_texpr
);
1880 gsf_xml_out_end_element (xml
); /* </cfRule> */
1885 xlsx_write_conditional_formatting (XLSXWriteState
*state
, GsfXMLOut
*xml
)
1887 GnmStyleList
*cond_styles
=
1888 sheet_style_collect_conditions (state
->sheet
, NULL
);
1893 for (l
= cond_styles
; l
; l
= l
->next
) {
1894 GnmStyleRegion
const *sr
= l
->data
;
1895 GnmStyleConditions
*sc
= gnm_style_get_conditions (sr
->style
);
1896 GPtrArray
const *conds
= gnm_style_conditions_details (sc
);
1903 parse_pos_init (&pp
, NULL
, state
->sheet
,
1904 sr
->range
.start
.col
, sr
->range
.start
.row
);
1905 gsf_xml_out_start_element (xml
, "conditionalFormatting");
1906 xlsx_add_range (xml
, "sqref", &sr
->range
);
1907 for (ui
= 0; ui
< conds
->len
; ui
++) {
1908 GnmStyleCond
*cond
= g_ptr_array_index (conds
, ui
);
1909 xlsx_write_cond_rule (state
, xml
, cond
, &pp
);
1911 gsf_xml_out_end_element (xml
); /* </conditionalFormatting> */
1914 style_list_free (cond_styles
);
1919 xlsx_write_validation_expr (XLSXClosure
*info
, GnmCellPos
const *pos
,
1920 char const *elem
, GnmExprTop
const *texpr
)
1922 if (NULL
!= texpr
) {
1924 char *str
= gnm_expr_top_as_string (texpr
,
1925 parse_pos_init (&pp
, NULL
, (Sheet
*)info
->state
->sheet
,
1926 pos
->col
, pos
->row
),
1927 info
->state
->convs
);
1928 gsf_xml_out_simple_element (info
->xml
, elem
, str
);
1934 xlsx_write_validation (XLValInputPair
const *vip
, G_GNUC_UNUSED gpointer dummy
, XLSXClosure
*info
)
1937 /* Get docs on this */
1938 "imeMode" default="noControl"
1953 gsf_xml_out_start_element (info
->xml
, "dataValidation");
1955 if (NULL
!= vip
->v
) {
1957 switch (vip
->v
->type
) {
1958 default : /* fall back to the default */
1959 case GNM_VALIDATION_TYPE_ANY
: /* the default "none" */ break;
1960 case GNM_VALIDATION_TYPE_AS_INT
: tmp
= "whole"; break;
1961 case GNM_VALIDATION_TYPE_AS_NUMBER
: tmp
= "decimal"; break;
1962 case GNM_VALIDATION_TYPE_IN_LIST
: tmp
= "list"; break;
1963 case GNM_VALIDATION_TYPE_AS_DATE
: tmp
= "date"; break;
1964 case GNM_VALIDATION_TYPE_AS_TIME
: tmp
= "time"; break;
1965 case GNM_VALIDATION_TYPE_TEXT_LENGTH
: tmp
= "textLength"; break;
1966 case GNM_VALIDATION_TYPE_CUSTOM
: tmp
= "custom"; break;
1969 gsf_xml_out_add_cstr_unchecked (info
->xml
, "type", tmp
);
1972 switch (vip
->v
->op
) {
1973 default : /* fall back to the default */
1974 case GNM_VALIDATION_OP_BETWEEN
: /* the default "between" */ break;
1975 case GNM_VALIDATION_OP_NOT_BETWEEN
: tmp
= "notBetween"; break;
1976 case GNM_VALIDATION_OP_EQUAL
: tmp
= "equal"; break;
1977 case GNM_VALIDATION_OP_NOT_EQUAL
: tmp
= "notEqual"; break;
1978 case GNM_VALIDATION_OP_LT
: tmp
= "lessThan"; break;
1979 case GNM_VALIDATION_OP_GT
: tmp
= "greaterThan"; break;
1980 case GNM_VALIDATION_OP_LTE
: tmp
= "lessThanOrEqual"; break;
1981 case GNM_VALIDATION_OP_GTE
: tmp
= "greaterThanOrEqual"; break;
1984 gsf_xml_out_add_cstr_unchecked (info
->xml
, "operator", tmp
);
1987 switch (vip
->v
->style
) {
1988 default : /* fall back to the default */
1989 case GNM_VALIDATION_STYLE_STOP
: /* "stop" the default */ break;
1990 case GNM_VALIDATION_STYLE_WARNING
: tmp
= "warning"; break;
1991 case GNM_VALIDATION_STYLE_INFO
: tmp
= "information"; break;
1994 gsf_xml_out_add_cstr_unchecked (info
->xml
, "errorStyle", tmp
);
1996 if (vip
->v
->allow_blank
)
1997 xlsx_add_bool (info
->xml
, "allowBlank", TRUE
);
1998 if (vip
->v
->use_dropdown
)
1999 xlsx_add_bool (info
->xml
, "showDropDown", TRUE
);
2001 if (NULL
!= vip
->v
->title
)
2002 gsf_xml_out_add_cstr (info
->xml
, "errorTitle", vip
->v
->title
->str
);
2003 if (NULL
!= vip
->v
->msg
)
2004 gsf_xml_out_add_cstr (info
->xml
, "error", vip
->v
->msg
->str
);
2007 /* ?? Always TRUE but not the default ?? */
2008 xlsx_add_bool (info
->xml
, "showInputMessage", TRUE
);
2009 xlsx_add_bool (info
->xml
, "showErrorMessage", TRUE
);
2011 if (NULL
!= vip
->msg
) {
2013 if (NULL
!= (str
= gnm_input_msg_get_title (vip
->msg
)))
2014 gsf_xml_out_add_cstr (info
->xml
, "promptTitle", str
);
2015 if (NULL
!= (str
= gnm_input_msg_get_msg (vip
->msg
)))
2016 gsf_xml_out_add_cstr (info
->xml
, "prompt", str
);
2019 xlsx_add_range_list (info
->xml
, "sqref", vip
->ranges
);
2021 if (NULL
!= vip
->v
) {
2022 GnmRange
const *first
= vip
->ranges
->data
;
2023 xlsx_write_validation_expr (info
, &first
->start
,
2024 "formula1", vip
->v
->deps
[0].texpr
);
2025 xlsx_write_validation_expr (info
, &first
->start
,
2026 "formula2", vip
->v
->deps
[1].texpr
);
2029 gsf_xml_out_end_element (info
->xml
); /* </dataValidation> */
2033 by_first_range (gpointer vip_a
, G_GNUC_UNUSED gpointer val_a
,
2034 gpointer vip_b
, G_GNUC_UNUSED gpointer val_b
,
2035 G_GNUC_UNUSED gpointer user
)
2037 const XLValInputPair
*a
= vip_a
;
2038 const XLValInputPair
*b
= vip_b
;
2039 return gnm_range_compare (a
->ranges
->data
, b
->ranges
->data
);
2043 xlsx_write_validations (XLSXWriteState
*state
, GsfXMLOut
*xml
, G_GNUC_UNUSED GnmRange
const *extent
)
2045 GnmStyleList
*validations
= sheet_style_collect_validations (state
->sheet
, NULL
);
2047 if (NULL
!= validations
) {
2048 XLSXClosure info
= { state
, xml
};
2049 /* filter on logical max, not extent. XL allows validations
2050 * past the stated dimension */
2051 GHashTable
*group
= xls_collect_validations (validations
,
2052 XLSX_MAX_COLS
, XLSX_MAX_ROWS
);
2054 gsf_xml_out_start_element (xml
, "dataValidations");
2055 gsf_xml_out_add_int (xml
, "count", g_hash_table_size (group
)) ;
2056 gnm_hash_table_foreach_ordered
2057 (group
, (GHFunc
)xlsx_write_validation
,
2060 gsf_xml_out_end_element (xml
); /* </dataValidations> */
2062 g_hash_table_destroy (group
);
2063 style_list_free (validations
);
2068 xlsx_write_hlink (GnmHLink
const *lnk
, GSList
*ranges
, XLSXClosure
*info
)
2070 gchar
*target
= g_strdup (gnm_hlink_get_target (lnk
));
2071 gchar
const *rid
= NULL
;
2072 gchar
*location
= NULL
;
2073 gchar
const *tip
= gnm_hlink_get_tip (lnk
);
2074 GType
const t
= G_OBJECT_TYPE (lnk
);
2076 if (target
&& g_type_is_a (t
, gnm_hlink_url_get_type ())) {
2077 // URLs, including email.
2079 char *hash
= strchr (target
, '#');
2081 location
= g_strdup (hash
+ 1);
2085 rid
= gsf_outfile_open_pkg_add_extern_rel (
2086 GSF_OUTFILE_OPEN_PKG (gsf_xml_out_get_output (info
->xml
)),
2087 target
, ns_rel_hlink
);
2088 } else if (t
== gnm_hlink_cur_wb_get_type ()) {
2097 for (; ranges
!= NULL
; ranges
= ranges
->next
) {
2098 GnmRange
const *range
= ranges
->data
;
2100 gsf_xml_out_start_element (info
->xml
, "hyperlink");
2101 xlsx_add_range (info
->xml
, "ref", range
);
2104 gsf_xml_out_add_cstr (info
->xml
, "r:id", rid
);
2106 gsf_xml_out_add_cstr (info
->xml
, "location", location
);
2108 gsf_xml_out_add_cstr (info
->xml
, "tooltip", tip
);
2110 gsf_xml_out_end_element (info
->xml
); /* </hyperlink> */
2118 by_hlink_order (G_GNUC_UNUSED gpointer link_a
, gpointer val_a
,
2119 G_GNUC_UNUSED gpointer link_b
, gpointer val_b
,
2120 G_GNUC_UNUSED gpointer user
)
2122 GList
*ranges_a
= val_a
;
2123 GList
*ranges_b
= val_b
;
2125 return gnm_range_compare (ranges_a
->data
, ranges_b
->data
);
2129 xlsx_write_hlinks (XLSXWriteState
*state
, GsfXMLOut
*xml
, G_GNUC_UNUSED GnmRange
const *extent
)
2131 GnmStyleList
*hlinks
= sheet_style_collect_hlinks (state
->sheet
, NULL
);
2133 if (NULL
!= hlinks
) {
2134 XLSXClosure info
= { state
, xml
};
2135 /* filter on logical max, not extent. XL allows validations
2136 * past the stated dimension */
2137 GHashTable
*group
= xls_collect_hlinks (hlinks
,
2138 XLSX_MAX_COLS
, XLSX_MAX_ROWS
);
2140 gsf_xml_out_start_element (xml
, "hyperlinks");
2141 gnm_hash_table_foreach_ordered
2142 (group
, (GHFunc
) xlsx_write_hlink
,
2145 gsf_xml_out_end_element (xml
); /* </hyperlinks> */
2147 g_hash_table_destroy (group
);
2148 style_list_free (hlinks
);
2153 xlsx_write_col (XLSXWriteState
*state
, GsfXMLOut
*xml
,
2154 ColRowInfo
const *ci
, int first
, int last
,
2157 double const def_width
= state
->sheet
->cols
.default_style
.size_pts
;
2158 gint style_id
= xlsx_get_style_id (state
, style
);
2160 gsf_xml_out_start_element (xml
, "col");
2161 gsf_xml_out_add_int (xml
, "min", first
+ 1);
2162 gsf_xml_out_add_int (xml
, "max", last
+ 1);
2163 gsf_xml_out_add_int (xml
, "style", style_id
);
2165 go_xml_out_add_double (xml
, "width",
2166 (ci
? ci
->size_pts
: def_width
) /
2167 ((130. / 18.5703125) * (72./96.)));
2171 gsf_xml_out_add_cstr_unchecked (xml
, "hidden", "1");
2173 gsf_xml_out_add_cstr_unchecked (xml
, "customWidth", "1");
2174 else if (fabs (def_width
- ci
->size_pts
) > .1) {
2175 gsf_xml_out_add_cstr_unchecked (xml
, "bestFit", "1");
2176 gsf_xml_out_add_cstr_unchecked (xml
, "customWidth", "1");
2179 if (ci
->outline_level
> 0)
2180 gsf_xml_out_add_int (xml
, "outlineLevel",
2182 if (ci
->is_collapsed
)
2183 gsf_xml_out_add_cstr_unchecked (xml
, "collapsed", "1");
2186 gsf_xml_out_end_element (xml
); /* </col> */
2190 xlsx_write_cols (XLSXWriteState
*state
, GsfXMLOut
*xml
, GnmStyle
**styles
)
2192 int first_col
= 0, i
;
2193 int last_col
= gnm_sheet_get_last_col (state
->sheet
);
2194 ColRowInfo
const *info
= sheet_col_get (state
->sheet
, first_col
);
2196 gsf_xml_out_start_element (xml
, "cols");
2198 for (i
= first_col
+ 1; i
<= last_col
; i
++) {
2199 ColRowInfo
const *ci
= sheet_col_get_info (state
->sheet
, i
);
2200 if (!colrow_equal (info
, ci
) || styles
[i
] != styles
[i
- 1]) {
2201 xlsx_write_col (state
, xml
, info
,
2208 xlsx_write_col (state
, xml
, info
,
2212 gsf_xml_out_end_element (xml
); /* </cols> */
2216 xlsx_write_autofilters (XLSXWriteState
*state
, GsfXMLOut
*xml
)
2218 GnmFilter
const *filter
;
2221 if (NULL
== state
->sheet
->filters
)
2224 /* We write only the first filter per sheet. */
2225 filter
= state
->sheet
->filters
->data
;
2227 gsf_xml_out_start_element (xml
, "autoFilter");
2228 xlsx_add_range (xml
, "ref", &filter
->r
);
2230 for (i
= 0; i
< filter
->fields
->len
; i
++) {
2231 GnmFilterCondition
const *cond
=
2232 gnm_filter_get_condition (filter
, i
);
2234 /* filter unused or bucket filters in excel5 */
2235 if (!cond
|| cond
->op
[0] == GNM_FILTER_UNUSED
)
2238 gsf_xml_out_start_element (xml
, "filterColumn");
2239 gsf_xml_out_add_int (xml
, "colId", i
);
2241 switch (cond
->op
[0]) {
2242 case GNM_FILTER_OP_EQUAL
:
2243 case GNM_FILTER_OP_GT
:
2244 case GNM_FILTER_OP_LT
:
2245 case GNM_FILTER_OP_GTE
:
2246 case GNM_FILTER_OP_LTE
:
2247 case GNM_FILTER_OP_NOT_EQUAL
: {
2248 static const char *const opname
[6] = {
2249 "equal", "greaterThan", "lessThan",
2250 "greaterThanOrEqual", "lessThanOrEqual",
2255 for (oi
= N
= 1; oi
< G_N_ELEMENTS (cond
->op
); oi
++) {
2256 if (cond
->op
[oi
] == GNM_FILTER_UNUSED
)
2261 gsf_xml_out_start_element (xml
, "customFilters");
2263 gsf_xml_out_add_cstr_unchecked (xml
, "and", "true");
2264 for (oi
= 0; oi
< G_N_ELEMENTS (cond
->op
); oi
++) {
2265 GnmFilterOp op
= cond
->op
[oi
];
2268 if (op
== GNM_FILTER_UNUSED
)
2270 gsf_xml_out_start_element (xml
, "customFilter");
2271 if (op
>= GNM_FILTER_OP_EQUAL
&& op
<= GNM_FILTER_OP_NOT_EQUAL
)
2272 gsf_xml_out_add_cstr_unchecked (xml
, "operator", opname
[op
]);
2274 str
= g_string_new (NULL
);
2275 value_get_as_gstring (cond
->value
[oi
], str
, state
->convs
);
2276 gsf_xml_out_add_cstr (xml
, "val", str
->str
);
2277 g_string_free (str
, TRUE
);
2278 gsf_xml_out_end_element (xml
); /* </customFilter> */
2280 gsf_xml_out_end_element (xml
); /* </customFilters> */
2284 case GNM_FILTER_OP_BLANKS
:
2285 gsf_xml_out_start_element (xml
, "filters");
2286 xlsx_add_bool (xml
, "blank", TRUE
);
2287 gsf_xml_out_end_element (xml
); /* </filters> */
2289 case GNM_FILTER_OP_NON_BLANKS
:
2290 gsf_xml_out_start_element (xml
, "customFilters");
2291 gsf_xml_out_start_element (xml
, "customFilter");
2292 gsf_xml_out_add_cstr_unchecked (xml
, "operator", "notEqual");
2293 gsf_xml_out_add_cstr (xml
, "val", " ");
2294 gsf_xml_out_end_element (xml
); /* </customFilter> */
2295 gsf_xml_out_end_element (xml
); /* </customFilters> */
2298 case GNM_FILTER_OP_TOP_N
:
2299 case GNM_FILTER_OP_BOTTOM_N
:
2300 case GNM_FILTER_OP_TOP_N_PERCENT
:
2301 case GNM_FILTER_OP_BOTTOM_N_PERCENT
:
2302 gsf_xml_out_start_element (xml
, "top10");
2303 go_xml_out_add_double (xml
, "val", cond
->count
);
2304 if (cond
->op
[0] & GNM_FILTER_OP_BOTTOM_MASK
)
2305 gsf_xml_out_add_cstr_unchecked (xml
, "top", "0");
2306 if (cond
->op
[0] & GNM_FILTER_OP_PERCENT_MASK
)
2307 gsf_xml_out_add_cstr_unchecked (xml
, "percent", "1");
2308 gsf_xml_out_end_element (xml
); /* </top10> */
2315 gsf_xml_out_end_element (xml
); /* </filterColumn> */
2317 gsf_xml_out_end_element (xml
); /* </autoFilter> */
2321 xlsx_write_protection (XLSXWriteState
*state
, GsfXMLOut
*xml
)
2326 gboolean formatCells
;
2327 gboolean formatColumns
;
2328 gboolean formatRows
;
2329 gboolean insertColumns
;
2330 gboolean insertRows
;
2331 gboolean insertHyperlinks
;
2332 gboolean deleteColumns
;
2333 gboolean deleteRows
;
2334 gboolean selectLockedCells
;
2336 gboolean autoFilter
;
2337 gboolean pivotTables
;
2338 gboolean selectUnlockedCells
;
2340 g_object_get (G_OBJECT (state
->sheet
),
2341 "protected", &sheet
,
2342 "protected-allow-edit-objects", &objects
,
2343 "protected-allow-edit-scenarios", &scenarios
,
2344 "protected-allow-cell-formatting", &formatCells
,
2345 "protected-allow-column-formatting", &formatColumns
,
2346 "protected-allow-row-formatting", &formatRows
,
2347 "protected-allow-insert-columns", &insertColumns
,
2348 "protected-allow-insert-rows", &insertRows
,
2349 "protected-allow-insert-hyperlinks", &insertHyperlinks
,
2350 "protected-allow-delete-columns", &deleteColumns
,
2351 "protected-allow-delete-rows", &deleteRows
,
2352 "protected-allow-select-locked-cells", &selectLockedCells
,
2353 "protected-allow-sort-ranges", &sort
,
2354 "protected-allow-edit-auto-filters", &autoFilter
,
2355 "protected-allow-edit-pivottable", &pivotTables
,
2356 "protected-allow-select-unlocked-cells", &selectUnlockedCells
,
2359 gsf_xml_out_start_element (xml
, "sheetProtection");
2360 if ( sheet
) xlsx_add_bool (xml
, "sheet", TRUE
);
2361 if ( objects
) xlsx_add_bool (xml
, "objects", TRUE
);
2362 if ( scenarios
) xlsx_add_bool (xml
, "scenarios", TRUE
);
2363 if (!formatCells
) xlsx_add_bool (xml
, "formatCells", FALSE
);
2364 if (!formatColumns
) xlsx_add_bool (xml
, "formatColumns", FALSE
);
2365 if (!formatRows
) xlsx_add_bool (xml
, "formatRows", FALSE
);
2366 if (!insertColumns
) xlsx_add_bool (xml
, "insertColumns", FALSE
);
2367 if (!insertRows
) xlsx_add_bool (xml
, "insertRows", FALSE
);
2368 if (!insertHyperlinks
) xlsx_add_bool (xml
, "insertHyperlinks", FALSE
);
2369 if (!deleteColumns
) xlsx_add_bool (xml
, "deleteColumns", FALSE
);
2370 if (!deleteRows
) xlsx_add_bool (xml
, "deleteRows", FALSE
);
2371 if ( selectLockedCells
) xlsx_add_bool (xml
, "selectLockedCells", TRUE
);
2372 if (!sort
) xlsx_add_bool (xml
, "sort", FALSE
);
2373 if (!autoFilter
) xlsx_add_bool (xml
, "autoFilter", FALSE
);
2374 if (!pivotTables
) xlsx_add_bool (xml
, "pivotTables", FALSE
);
2375 if ( selectUnlockedCells
) xlsx_add_bool (xml
, "selectUnlockedCells", TRUE
);
2377 gsf_xml_out_end_element (xml
); /* sheetProtection */
2381 xlsx_write_breaks (G_GNUC_UNUSED XLSXWriteState
*state
, GsfXMLOut
*xml
, GnmPageBreaks
*breaks
)
2383 unsigned const maxima
= (breaks
->is_vert
? XLSX_MaxCol
: XLSX_MaxRow
) - 1;
2384 GArray
const *details
= breaks
->details
;
2385 GnmPageBreak
const *binfo
;
2388 gsf_xml_out_start_element (xml
,
2389 (breaks
->is_vert
) ? "rowBreaks" : "colBreaks");
2390 gsf_xml_out_add_int (xml
, "count", details
->len
);
2392 for (i
= 0 ; i
< details
->len
; i
++) {
2393 binfo
= &g_array_index (details
, GnmPageBreak
, i
);
2394 gsf_xml_out_start_element (xml
, "brk");
2395 gsf_xml_out_add_int (xml
, "id", binfo
->pos
);
2397 /* hard code min=0 max=dir */
2398 gsf_xml_out_add_int (xml
, "max", maxima
);
2400 switch (binfo
->type
) {
2401 case GNM_PAGE_BREAK_MANUAL
: gsf_xml_out_add_bool (xml
, "man", TRUE
); break;
2402 case GNM_PAGE_BREAK_AUTO
: break;
2403 case GNM_PAGE_BREAK_NONE
: break;
2404 case GNM_PAGE_BREAK_DATA_SLICE
:gsf_xml_out_add_bool (xml
, "pt", TRUE
); break;
2406 gsf_xml_out_end_element (xml
); /* </brk> */
2408 gsf_xml_out_end_element (xml
);
2412 xlsx_find_paper_code (GtkPaperSize
*psize
)
2414 XLSXPaperDefs
*paper_defs
;
2415 XLSXPaperDefs paper
[] =
2416 {{ 74 , 90 , 90 , 205 , GTK_UNIT_MM
},
2417 { 38 , 92 , 3.625 , 6.5 , GTK_UNIT_INCH
},
2418 { 94 , 97 , 97 , 151 , GTK_UNIT_MM
},
2419 /* { 95 , 97 , 97 , 151 , GTK_UNIT_MM }, */
2420 { 37 , 98 , 3.875 , 7.5 , GTK_UNIT_INCH
},
2421 { 19 , 98 , 3.875 , 8.875 , GTK_UNIT_INCH
},
2422 { 96 , 102 , 102 , 165 , GTK_UNIT_MM
},
2423 { 97 , 102 , 102 , 176 , GTK_UNIT_MM
},
2424 { 20 , 104 , 4.125 , 9.5 , GTK_UNIT_INCH
},
2425 { 70 , 105 , 105 , 148 , GTK_UNIT_MM
},
2426 { 92 , 105 , 105 , 235 , GTK_UNIT_MM
},
2427 { 99 , 110 , 110 , 208 , GTK_UNIT_MM
},
2428 { 27 , 110 , 110 , 220 , GTK_UNIT_MM
},
2429 /* { 100 , 110 , 110 , 220 , GTK_UNIT_MM }, */
2430 { 36 , 110 , 110 , 230 , GTK_UNIT_MM
},
2431 { 21 , 114 , 4.5 , 10.375 , GTK_UNIT_INCH
},
2432 { 31 , 114 , 114 , 162 , GTK_UNIT_MM
},
2433 { 32 , 114 , 114 , 229 , GTK_UNIT_MM
},
2434 { 22 , 120 , 4.75 , 11 , GTK_UNIT_INCH
},
2435 { 101 , 120 , 120 , 230 , GTK_UNIT_MM
},
2436 { 73 , 120 , 120 , 235 , GTK_UNIT_MM
},
2437 { 103 , 120 , 120 , 309 , GTK_UNIT_MM
},
2438 { 98 , 125 , 125 , 176 , GTK_UNIT_MM
},
2439 { 23 , 127 , 5 , 11.5 , GTK_UNIT_INCH
},
2440 { 88 , 128 , 128 , 182 , GTK_UNIT_MM
},
2441 { 6 , 139 , 5.5 , 8.5 , GTK_UNIT_INCH
},
2442 { 93 , 146 , 146 , 215 , GTK_UNIT_MM
},
2443 { 81 , 148 , 148 , 100 , GTK_UNIT_MM
},
2444 { 83 , 148 , 148 , 105 , GTK_UNIT_MM
},
2445 { 82 , 148 , 148 , 200 , GTK_UNIT_MM
},
2446 { 11 , 148 , 148 , 210 , GTK_UNIT_MM
},
2447 /* { 61 , 148 , 148 , 210 , GTK_UNIT_MM }, */
2448 { 107 , 151 , 151 , 97 , GTK_UNIT_MM
},
2449 /* { 108 , 151 , 151 , 97 , GTK_UNIT_MM }, */
2450 { 102 , 160 , 160 , 230 , GTK_UNIT_MM
},
2451 { 28 , 162 , 162 , 229 , GTK_UNIT_MM
},
2452 { 109 , 165 , 165 , 102 , GTK_UNIT_MM
},
2453 { 64 , 174 , 174 , 235 , GTK_UNIT_MM
},
2454 { 110 , 176 , 176 , 102 , GTK_UNIT_MM
},
2455 { 35 , 176 , 176 , 125 , GTK_UNIT_MM
},
2456 /* { 111 , 176 , 176 , 125 , GTK_UNIT_MM }, */
2457 { 13 , 176 , 176 , 250 , GTK_UNIT_MM
},
2458 /* { 34 , 176 , 176 , 250 , GTK_UNIT_MM }, */
2459 { 89 , 182 , 182 , 128 , GTK_UNIT_MM
},
2460 { 62 , 182 , 182 , 257 , GTK_UNIT_MM
},
2461 { 7 , 184 , 7.25 , 10.5 , GTK_UNIT_INCH
},
2462 { 43 , 200 , 200 , 148 , GTK_UNIT_MM
},
2463 /* { 69 , 200 , 200 , 148 , GTK_UNIT_MM }, */
2464 { 65 , 201 , 201 , 276 , GTK_UNIT_MM
},
2465 { 87 , 205 , 205 , 90 , GTK_UNIT_MM
},
2466 { 112 , 208 , 208 , 110 , GTK_UNIT_MM
},
2467 { 54 , 210 , 8.275 , 11 , GTK_UNIT_INCH
},
2468 { 78 , 210 , 210 , 148 , GTK_UNIT_MM
},
2469 { 9 , 210 , 210 , 297 , GTK_UNIT_MM
},
2470 /* { 10 , 210 , 210 , 297 , GTK_UNIT_MM }, */
2471 /* { 55 , 210 , 210 , 297 , GTK_UNIT_MM }, */
2472 { 60 , 210 , 210 , 330 , GTK_UNIT_MM
},
2473 { 1 , 215 , 8.5 , 11 , GTK_UNIT_INCH
},
2474 /* { 2 , 215 , 8.5 , 11 , GTK_UNIT_INCH }, */
2475 /* { 18 , 215 , 8.5 , 11 , GTK_UNIT_INCH }, */
2476 { 40 , 215 , 8.5 , 12 , GTK_UNIT_INCH
},
2477 { 59 , 215 , 8.5 , 12.69 , GTK_UNIT_INCH
},
2478 { 14 , 215 , 8.5 , 13 , GTK_UNIT_INCH
},
2479 /* { 41 , 215 , 8.5 , 13 , GTK_UNIT_INCH }, */
2480 { 5 , 215 , 8.5 , 14 , GTK_UNIT_INCH
},
2481 { 106 , 215 , 215 , 146 , GTK_UNIT_MM
},
2482 { 15 , 215 , 215 , 275 , GTK_UNIT_MM
},
2483 { 72 , 216 , 216 , 277 , GTK_UNIT_MM
},
2484 { 113 , 220 , 220 , 110 , GTK_UNIT_MM
},
2485 { 47 , 220 , 220 , 220 , GTK_UNIT_MM
},
2486 { 57 , 227 , 227 , 356 , GTK_UNIT_MM
},
2487 { 44 , 228 , 9 , 11 , GTK_UNIT_INCH
},
2488 { 30 , 229 , 229 , 324 , GTK_UNIT_MM
},
2489 /* { 104 , 229 , 229 , 324 , GTK_UNIT_MM }, */
2490 { 114 , 230 , 230 , 120 , GTK_UNIT_MM
},
2491 { 115 , 230 , 230 , 160 , GTK_UNIT_MM
},
2492 { 50 , 235 , 9.275 , 12 , GTK_UNIT_INCH
},
2493 /* { 56 , 235 , 9.275 , 12 , GTK_UNIT_INCH }, */
2494 { 51 , 235 , 9.275 , 15 , GTK_UNIT_INCH
},
2495 { 91 , 235 , 235 , 105 , GTK_UNIT_MM
},
2496 { 86 , 235 , 235 , 120 , GTK_UNIT_MM
},
2497 { 53 , 236 , 236 , 322 , GTK_UNIT_MM
},
2498 { 71 , 240 , 240 , 332 , GTK_UNIT_MM
},
2499 { 12 , 250 , 250 , 353 , GTK_UNIT_MM
},
2500 /* { 33 , 250 , 250 , 353 , GTK_UNIT_MM }, */
2501 { 42 , 250 , 250 , 353 , GTK_UNIT_MM
},
2502 { 45 , 254 , 10 , 11 , GTK_UNIT_INCH
},
2503 { 16 , 254 , 10 , 14 , GTK_UNIT_INCH
},
2504 { 80 , 257 , 257 , 182 , GTK_UNIT_MM
},
2505 { 85 , 277 , 277 , 216 , GTK_UNIT_MM
},
2506 { 75 , 279 , 11 , 8.5 , GTK_UNIT_INCH
},
2507 { 3 , 279 , 11 , 17 , GTK_UNIT_INCH
},
2508 /* { 17 , 279 , 11 , 17 , GTK_UNIT_INCH }, */
2509 { 52 , 296 , 11.69 , 18 , GTK_UNIT_INCH
},
2510 { 77 , 297 , 297 , 210 , GTK_UNIT_MM
},
2511 { 8 , 297 , 297 , 420 , GTK_UNIT_MM
},
2512 /* { 67 , 297 , 297 , 420 , GTK_UNIT_MM }, */
2513 { 90 , 304 , 12 , 11 , GTK_UNIT_INCH
},
2514 { 58 , 305 , 305 , 487 , GTK_UNIT_MM
},
2515 { 116 , 309 , 309 , 120 , GTK_UNIT_MM
},
2516 { 63 , 322 , 322 , 445 , GTK_UNIT_MM
},
2517 /* { 68 , 322 , 322 , 445 , GTK_UNIT_MM }, */
2518 { 117 , 324 , 324 , 229 , GTK_UNIT_MM
},
2519 { 29 , 324 , 324 , 458 , GTK_UNIT_MM
},
2520 /* { 105 , 324 , 324 , 458 , GTK_UNIT_MM }, */
2521 { 84 , 332 , 332 , 240 , GTK_UNIT_MM
},
2522 { 79 , 364 , 364 , 257 , GTK_UNIT_MM
},
2523 { 39 , 377 , 14.875 , 11 , GTK_UNIT_INCH
},
2524 { 46 , 381 , 15 , 11 , GTK_UNIT_INCH
},
2525 { 76 , 420 , 420 , 297 , GTK_UNIT_MM
},
2526 { 66 , 420 , 420 , 594 , GTK_UNIT_MM
},
2527 { 4 , 431 , 17 , 11 , GTK_UNIT_INCH
},
2528 { 24 , 431 , 17 , 22 , GTK_UNIT_INCH
},
2529 { 118 , 458 , 458 , 324 , GTK_UNIT_MM
},
2530 { 25 , 558 , 22 , 34 , GTK_UNIT_INCH
},
2531 { 26 , 863 , 34 , 44 , GTK_UNIT_INCH
},
2533 gchar
const *psize_name
;
2536 psize_name
= gtk_paper_size_get_name (psize
);
2537 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_LETTER
))
2539 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_A4
))
2541 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_A3
))
2543 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_A5
))
2545 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_B5
))
2547 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_EXECUTIVE
))
2549 if (0 == strcmp (psize_name
, GTK_PAPER_NAME_LEGAL
))
2552 width_mm
= (int) gtk_paper_size_get_width (psize
, GTK_UNIT_MM
);
2554 for (paper_defs
= paper
; paper_defs
->code
> 0; paper_defs
++) {
2555 if (width_mm
< paper_defs
->width_mm
)
2557 if (width_mm
== paper_defs
->width_mm
) {
2558 gdouble width
= gtk_paper_size_get_width (psize
, paper_defs
->unit
);
2559 gdouble height
= gtk_paper_size_get_height (psize
, paper_defs
->unit
);
2561 if (width
== paper_defs
->width
&& height
== paper_defs
->height
)
2562 return paper_defs
->code
;
2569 xlsx_write_print_info_hf (XLSXWriteState
*state
, GsfXMLOut
*xml
,
2570 const GnmPrintHF
*hf
, const char *hftext
)
2572 char *s
= xls_header_footer_export (hf
);
2574 gsf_xml_out_start_element (xml
, hftext
);
2575 gsf_xml_out_add_cstr (xml
, NULL
, s
);
2576 gsf_xml_out_end_element (xml
); /* hftext */
2582 xlsx_write_print_info (XLSXWriteState
*state
, GsfXMLOut
*xml
)
2584 GnmPrintInformation
*pi
= state
->sheet
->print_info
;
2585 double h_margin
, f_margin
;
2588 double t_margin
, b_margin
;
2590 g_return_if_fail (pi
!= NULL
);
2592 gsf_xml_out_start_element (xml
, "printOptions");
2593 gsf_xml_out_end_element (xml
); /* </printOptions> */
2595 gsf_xml_out_start_element (xml
, "pageMargins");
2596 print_info_get_margins (pi
, &h_margin
, &f_margin
, &left
, &right
,
2597 &t_margin
, &b_margin
);
2598 go_xml_out_add_double (xml
, "left", left
/ 72.);
2599 go_xml_out_add_double (xml
, "right", right
/ 72.);
2600 go_xml_out_add_double (xml
, "top", t_margin
/ 72.);
2601 go_xml_out_add_double (xml
, "bottom", b_margin
/ 72.);
2602 go_xml_out_add_double (xml
, "header", h_margin
/ 72.);
2603 go_xml_out_add_double (xml
, "footer", f_margin
/ 72.);
2604 gsf_xml_out_end_element (xml
); /* </pageMargins> */
2606 gsf_xml_out_start_element (xml
, "pageSetup");
2608 xlsx_add_bool (xml
, "blackAndWhite", pi
->print_black_and_white
);
2609 switch (pi
->comment_placement
) {
2610 case GNM_PRINT_COMMENTS_IN_PLACE
:
2611 gsf_xml_out_add_cstr_unchecked (xml
, "cellComments", "asDisplayed");
2613 case GNM_PRINT_COMMENTS_AT_END
:
2614 gsf_xml_out_add_cstr_unchecked (xml
, "cellComments", "atEnd");
2616 case GNM_PRINT_COMMENTS_NONE
:
2618 gsf_xml_out_add_cstr_unchecked (xml
, "cellComments", "none");
2621 if (pi
->n_copies
> 0)
2622 gsf_xml_out_add_int (xml
, "copies", pi
->n_copies
);
2623 xlsx_add_bool (xml
, "draft", pi
->print_as_draft
);
2624 switch (pi
->error_display
) {
2625 case GNM_PRINT_ERRORS_AS_BLANK
:
2626 gsf_xml_out_add_cstr_unchecked (xml
, "errors", "blank");
2628 case GNM_PRINT_ERRORS_AS_DASHES
:
2629 gsf_xml_out_add_cstr_unchecked (xml
, "errors", "dash");
2631 case GNM_PRINT_ERRORS_AS_NA
:
2632 gsf_xml_out_add_cstr_unchecked (xml
, "errors", "NA");
2634 case GNM_PRINT_ERRORS_AS_DISPLAYED
:
2636 gsf_xml_out_add_cstr_unchecked (xml
, "errors", "displayed");
2639 if (pi
->start_page
>= 0)
2640 gsf_xml_out_add_int (xml
, "firstPageNumber", pi
->start_page
);
2641 if (pi
->scaling
.dim
.rows
!= 1)
2642 gsf_xml_out_add_int (xml
, "fitToHeight", pi
->scaling
.dim
.rows
);
2643 if (pi
->scaling
.dim
.cols
!= 1)
2644 gsf_xml_out_add_int (xml
, "fitToWidth", pi
->scaling
.dim
.cols
);
2645 /* horizontalDpi skipped */
2648 if (pi
->page_setup
) {
2649 GtkPageOrientation orient
;
2651 orient
= gtk_page_setup_get_orientation (pi
->page_setup
);
2653 case GTK_PAGE_ORIENTATION_PORTRAIT
:
2654 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT
:
2655 gsf_xml_out_add_cstr_unchecked (xml
, "orientation", "portrait");
2657 case GTK_PAGE_ORIENTATION_LANDSCAPE
:
2658 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE
:
2659 gsf_xml_out_add_cstr_unchecked (xml
, "orientation", "landscape");
2662 gsf_xml_out_add_cstr_unchecked (xml
, "orientation", "default");
2666 gsf_xml_out_add_cstr_unchecked (xml
, "orientation", "default");
2668 gsf_xml_out_add_cstr_unchecked
2670 pi
->print_across_then_down
? "overThenDown" : "downThenOver");
2672 if (pi
->page_setup
) {
2673 GtkPaperSize
*psize
;
2676 psize
= gtk_page_setup_get_paper_size (pi
->page_setup
);
2677 paper_code
= xlsx_find_paper_code (psize
);
2680 gsf_xml_out_add_int (xml
, "paperSize", paper_code
);
2682 gdouble width
= gtk_paper_size_get_width (psize
, GTK_UNIT_POINTS
);
2683 gdouble height
= gtk_paper_size_get_height (psize
, GTK_UNIT_POINTS
);
2685 xlsx_add_pt (xml
, "paperHeight", height
);
2686 xlsx_add_pt (xml
, "paperWidth", width
);
2690 if (pi
->scaling
.percentage
.x
> 0)
2691 gsf_xml_out_add_int (xml
, "scale",
2692 (int)CLAMP (pi
->scaling
.percentage
.x
, 10, 400));
2693 xlsx_add_bool (xml
, "useFirstPageNumber", (pi
->start_page
>= 0));
2694 /* usePrinterDefaults skipped */
2695 /* verticalDpi skipped */
2697 gsf_xml_out_end_element (xml
); /* </pageSetup> */
2699 gsf_xml_out_start_element (xml
, "headerFooter");
2700 xlsx_write_print_info_hf (state
, xml
, pi
->header
, "oddHeader");
2701 xlsx_write_print_info_hf (state
, xml
, pi
->footer
, "oddFooter");
2702 gsf_xml_out_end_element (xml
); /* </headerFooter> */
2704 if (NULL
!= pi
->page_breaks
.v
)
2705 xlsx_write_breaks (state
, xml
, pi
->page_breaks
.v
);
2706 if (NULL
!= pi
->page_breaks
.h
)
2707 xlsx_write_breaks (state
, xml
, pi
->page_breaks
.h
);
2710 /**********************************************************************/
2713 by_val_int (G_GNUC_UNUSED gpointer key_a
, gpointer val_a
,
2714 G_GNUC_UNUSED gpointer key_b
, gpointer val_b
,
2715 G_GNUC_UNUSED gpointer user
)
2717 return GPOINTER_TO_INT (val_a
) - GPOINTER_TO_INT (val_b
);
2721 write_comment_author (gpointer key
, G_GNUC_UNUSED gpointer value
, GsfXMLOut
*xml
)
2723 gsf_xml_out_start_element (xml
, "author");
2724 gsf_xml_out_add_cstr_unchecked (xml
, NULL
, (char const *) key
);
2725 gsf_xml_out_end_element (xml
);
2729 xlsx_write_comments (XLSXWriteState
*state
, GsfOutput
*sheet_part
, GSList
*objects
)
2732 GHashTable
*authors
;
2733 unsigned author
= 0;
2734 char const *authorname
;
2736 SheetObjectAnchor
const *anchor
;
2737 PangoAttrList
*attrs
;
2738 char *name
= g_strdup_printf ("comments%u.xml", ++state
->comment
);
2739 GsfOutput
*comments_part
= gsf_outfile_new_child_full (state
->xl_dir
, name
, FALSE
,
2740 "content-type", "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
2743 gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (comments_part
),
2744 GSF_OUTFILE_OPEN_PKG (sheet_part
), ns_rel_com
);
2745 xml
= gsf_xml_out_new (comments_part
);
2746 gsf_xml_out_start_element (xml
, "comments");
2747 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns", ns_ss
);
2748 /* search for comments authors */
2749 authors
= g_hash_table_new (g_str_hash
, g_str_equal
);
2750 for (ptr
= objects
; ptr
; ptr
= ptr
->next
) {
2751 authorname
= cell_comment_author_get (GNM_CELL_COMMENT (ptr
->data
));
2752 if (authorname
!= NULL
&& !g_hash_table_lookup_extended (authors
, authorname
, NULL
, NULL
))
2753 g_hash_table_insert (authors
, (gpointer
) authorname
, GUINT_TO_POINTER (author
++));
2756 gsf_xml_out_start_element (xml
, "authors");
2757 gnm_hash_table_foreach_ordered (authors
, (GHFunc
) write_comment_author
,
2759 gsf_xml_out_end_element (xml
); /* </authors> */
2761 gsf_xml_out_start_element (xml
, "commentList");
2762 for (ptr
= objects
; ptr
; ptr
= ptr
->next
) {
2763 gsf_xml_out_start_element (xml
, "comment");
2764 anchor
= sheet_object_get_anchor (ptr
->data
);
2765 gsf_xml_out_add_cstr_unchecked (xml
, "ref", range_as_string (&anchor
->cell_bound
));
2766 authorname
= cell_comment_author_get (GNM_CELL_COMMENT (ptr
->data
));
2767 if (authorname
!= NULL
)
2768 gsf_xml_out_add_uint (xml
, "authorId",
2769 GPOINTER_TO_UINT (g_hash_table_lookup (authors
, authorname
)));
2770 gsf_xml_out_start_element (xml
, "text");
2771 /* Save text as rich text */
2772 g_object_get (ptr
->data
, "text", &name
, "markup", &attrs
, NULL
);
2774 xlsx_write_rich_text (xml
, name
, attrs
, TRUE
);
2776 pango_attr_list_unref (attrs
);
2777 gsf_xml_out_end_element (xml
); /* </text> */
2778 gsf_xml_out_end_element (xml
); /* </comment> */
2780 gsf_xml_out_end_element (xml
); /* </commentList> */
2781 g_hash_table_destroy (authors
);
2782 gsf_xml_out_end_element (xml
); /* </comments> */
2783 g_object_unref (xml
);
2784 gsf_output_close (comments_part
);
2785 g_object_unref (comments_part
);
2788 #include "xlsx-write-drawing.c"
2791 xlsx_write_sheet (XLSXWriteState
*state
, GsfOutfile
*wb_part
, Sheet
*sheet
)
2793 char *name
= g_strdup_printf ("sheet%u.xml", ++state
->sheet_dir
.count
);
2794 GsfOutput
*sheet_part
= gsf_outfile_new_child_full
2795 (xlsx_dir_get (&state
->sheet_dir
), name
, FALSE
,
2796 "content-type", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
2798 char const *rId
= gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (sheet_part
),
2799 GSF_OUTFILE_OPEN_PKG (wb_part
),
2800 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet");
2802 GnmRange extent
, cell_extent
;
2803 GSList
*drawing_objs
, *legacy_drawing_objs
, *comments
, *others
, *objects
, *p
;
2804 char const *chart_drawing_rel_id
= NULL
;
2805 char const *legacy_drawing_rel_id
= NULL
;
2806 GnmStyle
**col_styles
;
2807 GnmPrintInformation
*pi
= NULL
;
2810 gboolean ext_tab_textcolor
= FALSE
;
2812 state
->sheet
= sheet
;
2813 col_styles
= sheet_style_most_common (state
->sheet
, TRUE
);
2814 excel_sheet_extent (state
->sheet
, &extent
, col_styles
,
2815 XLSX_MAX_COLS
, XLSX_MAX_ROWS
, state
->io_context
);
2816 cell_extent
= sheet_get_cells_extent (state
->sheet
);
2817 extent
= range_union (&extent
, &cell_extent
);
2819 objects
= sheet_objects_get (state
->sheet
, NULL
, G_TYPE_NONE
);
2820 drawing_objs
= legacy_drawing_objs
= comments
= others
= NULL
;
2821 zorder
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
2822 for (p
= objects
, z
= 1; p
; p
= p
->next
, z
++) {
2823 SheetObject
*so
= p
->data
;
2825 g_hash_table_insert (zorder
, so
, GINT_TO_POINTER (z
));
2827 if (GNM_IS_CELL_COMMENT (so
)) {
2828 comments
= g_slist_prepend (comments
, so
);
2829 legacy_drawing_objs
= g_slist_prepend (legacy_drawing_objs
, so
);
2830 } else if (GNM_IS_SO_GRAPH (so
) ||
2831 GNM_IS_SO_LINE (so
) ||
2832 GNM_IS_SO_FILLED (so
) ||
2833 GNM_IS_SO_IMAGE (so
))
2834 drawing_objs
= g_slist_prepend (drawing_objs
, so
);
2835 else if (GNM_IS_SOW_SCROLLBAR (so
) || GNM_IS_SOW_SLIDER (so
) ||
2836 GNM_IS_SOW_SPINBUTTON (so
) ||
2837 GNM_IS_SOW_BUTTON (so
) ||
2838 GNM_IS_SOW_RADIO_BUTTON (so
) ||
2839 GNM_IS_SOW_CHECKBOX (so
) ||
2840 GNM_IS_SOW_COMBO (so
) ||
2841 GNM_IS_SOW_LIST (so
))
2842 legacy_drawing_objs
= g_slist_prepend (legacy_drawing_objs
, so
);
2843 else if (GNM_IS_FILTER_COMBO (so
))
2844 ; /* Nothing here */
2846 others
= g_slist_prepend (others
, so
);
2848 g_slist_free (objects
);
2851 comments
= g_slist_reverse (comments
);
2852 xlsx_write_comments (state
, sheet_part
, comments
);
2853 g_slist_free (comments
);
2857 drawing_objs
= g_slist_reverse (drawing_objs
);
2858 chart_drawing_rel_id
= xlsx_write_drawing_objects (state
, sheet_part
, drawing_objs
, zorder
);
2859 g_slist_free (drawing_objs
);
2862 if (legacy_drawing_objs
) {
2863 legacy_drawing_objs
= g_slist_reverse (legacy_drawing_objs
);
2864 legacy_drawing_rel_id
= xlsx_write_legacy_drawing_objects (state
, sheet_part
, legacy_drawing_objs
, zorder
);
2865 g_slist_free (legacy_drawing_objs
);
2868 for (p
= others
; p
; p
= p
->next
) {
2869 SheetObject
*so
= p
->data
;
2872 g_object_get (so
, "name", &name
, NULL
);
2873 g_warning ("Not exporting object %s of type %s",
2874 (name
? name
: "?"),
2875 g_type_name (G_OBJECT_TYPE (so
)));
2878 g_slist_free (others
);
2880 g_hash_table_destroy (zorder
);
2882 xml
= gsf_xml_out_new (sheet_part
);
2883 /* CT_Worksheet = */
2884 gsf_xml_out_start_element (xml
, "worksheet");
2885 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns", ns_ss
);
2886 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns:r", ns_rel
);
2887 if (state
->with_extension
)
2888 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns:gnmx", ns_gnm_ext
);
2890 /* element sheetPr { CT_SheetPr }?, */
2891 gsf_xml_out_start_element (xml
, "sheetPr");
2893 if (NULL
!= state
->sheet
->tab_color
) {
2894 gsf_xml_out_start_element (xml
, "tabColor");
2895 xlsx_add_rgb (xml
, "rgb", state
->sheet
->tab_color
->go_color
);
2896 gsf_xml_out_end_element (xml
); /* </tabColor> */
2898 if (NULL
!= state
->sheet
->tab_text_color
)
2899 ext_tab_textcolor
= TRUE
;
2901 pi
= state
->sheet
->print_info
;
2903 gsf_xml_out_start_element (xml
, "pageSetUpPr");
2904 xlsx_add_bool (xml
, "fitToPage", pi
->scaling
.type
== PRINT_SCALE_FIT_PAGES
);
2905 gsf_xml_out_end_element (xml
); /* </pageSetUpPr> */
2908 gsf_xml_out_end_element (xml
); /* </sheetPr> */
2910 /* element dimension { CT_SheetDimension }?, */
2911 gsf_xml_out_start_element (xml
, "dimension");
2912 xlsx_add_range (xml
, "ref", &extent
);
2913 gsf_xml_out_end_element (xml
); /* </dimension> */
2914 /* element sheetViews { CT_SheetViews }?, */
2915 gsf_xml_out_start_element (xml
, "sheetViews");
2916 SHEET_FOREACH_VIEW (state
->sheet
, sv
, xlsx_write_sheet_view (xml
, sv
););
2917 gsf_xml_out_end_element (xml
); /* </sheetViews> */
2918 /* element sheetFormatPr { CT_SheetFormatPr }?, */
2919 gsf_xml_out_start_element (xml
, "sheetFormatPr");
2920 go_xml_out_add_double (xml
, "defaultRowHeight",
2921 sheet_row_get_default_size_pts (state
->sheet
));
2922 if (state
->sheet
->rows
.max_outline_level
> 0)
2923 gsf_xml_out_add_int (xml
, "outlineLevelRow",
2924 state
->sheet
->rows
.max_outline_level
);
2925 if (state
->sheet
->cols
.max_outline_level
> 0)
2926 gsf_xml_out_add_int (xml
, "outlineLevelCol",
2927 state
->sheet
->cols
.max_outline_level
);
2928 gsf_xml_out_end_element (xml
); /* </sheetFormatPr> */
2929 /* element cols { CT_Cols }*, */
2930 xlsx_write_cols (state
, xml
, col_styles
);
2931 /* element sheetData { CT_SheetData }, */
2932 xlsx_write_cells (state
, xml
, &extent
, col_styles
);
2933 /* element sheetCalcPr { CT_SheetCalcPr }?, */
2934 /* element sheetProtection { CT_SheetProtection }?, */
2935 xlsx_write_protection (state
, xml
);
2936 /* element protectedRanges { CT_ProtectedRanges }?, */
2937 /* element scenarios { CT_Scenarios }?, */
2938 /* element autoFilter { CT_AutoFilter }?, */
2939 xlsx_write_autofilters (state
, xml
);
2940 /* element sortState { CT_SortState }?, */
2941 /* element dataConsolidate { CT_DataConsolidate }?, */
2942 /* element customSheetViews { CT_CustomSheetViews }?, */
2943 /* element mergeCells { CT_MergeCells }?, */
2944 xlsx_write_merges (state
, xml
);
2945 /* element phoneticPr { CT_PhoneticPr }?, */
2946 /* element conditionalFormatting { CT_ConditionalFormatting }*, */
2947 xlsx_write_conditional_formatting (state
, xml
);
2948 /* element dataValidations { CT_DataValidations }?, */
2949 xlsx_write_validations (state
, xml
, &extent
);
2950 /* element hyperlinks { CT_Hyperlinks }?, */
2951 xlsx_write_hlinks (state
, xml
, &extent
);
2952 /* element printOptions { CT_PrintOptions }?, included in xlsx_write_print_info */
2953 /* element pageMargins { CT_PageMargins }?, included in xlsx_write_print_info */
2954 /* element pageSetup { CT_PageSetup }?, included in xlsx_write_print_info */
2955 /* element headerFooter { CT_HeaderFooter }?, included in xlsx_write_print_info */
2956 xlsx_write_print_info (state
, xml
);
2957 /* element rowBreaks { CT_PageBreak }?, */
2958 /* element colBreaks { CT_PageBreak }?, */
2959 /* element customProperties { CT_CustomProperties }?, */
2960 /* element cellWatches { CT_CellWatches }?, */
2961 /* element ignoredErrors { CT_IgnoredErrors }?, */
2962 /* element smartTags { CT_SmartTags }?, */
2963 /* element drawing { CT_Drawing }?, */
2964 if (NULL
!= chart_drawing_rel_id
) {
2965 gsf_xml_out_start_element (xml
, "drawing");
2966 gsf_xml_out_add_cstr_unchecked (xml
, "r:id", chart_drawing_rel_id
);
2967 gsf_xml_out_end_element (xml
); /* </drawing> */
2969 /* element legacyDrawing { CT_LegacyDrawing }?, Deleted in edition 2 */
2970 if (NULL
!= legacy_drawing_rel_id
) {
2971 gsf_xml_out_start_element (xml
, "legacyDrawing");
2972 gsf_xml_out_add_cstr_unchecked (xml
, "r:id", legacy_drawing_rel_id
);
2973 gsf_xml_out_end_element (xml
); /* </legacyDrawing> */
2975 /* element legacyDrawingHF { CT_LegacyDrawing }?, Deleted in edition 2 */
2976 /* element picture { CT_SheetBackgroundPicture }?, */
2977 /* element oleObjects { CT_OleObjects }?, */
2978 /* element controls { CT_Controls }?, */
2979 /* element webPublishItems { CT_WebPublishItems }?, */
2980 /* element tableParts { CT_TableParts }?, */
2982 if (state
->with_extension
&& ext_tab_textcolor
) {
2983 gsf_xml_out_start_element (xml
, "extLst");
2984 gsf_xml_out_start_element (xml
, "ext");
2985 gsf_xml_out_add_cstr_unchecked (xml
, "uri", ns_gnm_ext
);
2987 gsf_xml_out_start_element (xml
, "gnmx:tabTextColor");
2988 xlsx_add_rgb (xml
, "rgb", state
->sheet
->tab_text_color
->go_color
);
2989 gsf_xml_out_end_element (xml
); /* </gnmx:tabTextColor> */
2991 gsf_xml_out_end_element (xml
); /* "ext" */
2992 gsf_xml_out_end_element (xml
); /* "extLst" */
2995 /* element extLst { CT_ExtensionList }? */
2996 gsf_xml_out_end_element (xml
); /* </worksheet> */
2998 g_object_unref (xml
);
2999 gsf_output_close (sheet_part
);
3000 g_object_unref (sheet_part
);
3002 g_free (col_styles
);
3004 state
->sheet
= NULL
;
3010 xlsx_write_named_expression (G_GNUC_UNUSED gpointer key
, GnmNamedExpr
*nexpr
, XLSXClosure
*closure
)
3014 g_return_if_fail (nexpr
!= NULL
);
3015 if (!expr_name_is_active (nexpr
))
3018 gsf_xml_out_start_element (closure
->xml
, "definedName");
3020 if (nexpr
->is_permanent
) {
3021 char const *expr_name
= expr_name_name (nexpr
);
3022 if (0 == strcmp (expr_name
, "Print_Area"))
3023 gsf_xml_out_add_cstr (closure
->xml
, "name", "_xlnm.Print_Area");
3024 else if (0 == strcmp (expr_name
, "Sheet_Title"))
3025 gsf_xml_out_add_cstr (closure
->xml
, "name", "_xlnm.Sheet_Title");
3027 gsf_xml_out_add_cstr (closure
->xml
, "name", expr_name
);
3029 gsf_xml_out_add_cstr (closure
->xml
, "name", expr_name_name (nexpr
));
3031 if (nexpr
->pos
.sheet
!= NULL
)
3032 gsf_xml_out_add_int (closure
->xml
, "localSheetId",
3033 nexpr
->pos
.sheet
->index_in_wb
);
3035 formula
= expr_name_as_string (nexpr
, NULL
, closure
->state
->convs
);
3036 gsf_xml_out_add_cstr (closure
->xml
, NULL
, formula
);
3039 gsf_xml_out_end_element (closure
->xml
);
3043 xlsx_write_definedNames (XLSXWriteState
*state
, GsfXMLOut
*xml
)
3045 XLSXClosure closure
= {state
, xml
};
3047 gsf_xml_out_start_element (xml
, "definedNames");
3048 workbook_foreach_name
3049 (state
->base
.wb
, FALSE
,
3050 (GHFunc
)&xlsx_write_named_expression
, &closure
);
3051 gsf_xml_out_end_element (xml
);
3055 xlsx_write_calcPR (XLSXWriteState
*state
, GsfXMLOut
*xml
)
3057 Workbook
const *wb
= state
->base
.wb
;
3059 #warning Filter by defaults
3060 gsf_xml_out_start_element (xml
, "calcPr");
3062 gsf_xml_out_add_cstr_unchecked (xml
, "calcMode",
3063 wb
->recalc_auto
? "auto" : "manual");
3065 xlsx_add_bool (xml
, "iterate", wb
->iteration
.enabled
);
3066 gsf_xml_out_add_int (xml
, "iterateCount",
3067 wb
->iteration
.max_number
);
3068 go_xml_out_add_double (xml
, "iterateDelta",
3069 wb
->iteration
.tolerance
);
3071 gsf_xml_out_end_element (xml
);
3074 #include "xlsx-write-pivot.c"
3076 #include "xlsx-write-docprops.c"
3079 rich_value_equal (GnmValue
const *a
, GnmValue
const *b
)
3081 return value_equal (a
, b
) &&
3082 go_format_eq (VALUE_FMT (a
), VALUE_FMT (b
));
3086 rich_value_hash (GnmValue
const *v
)
3088 return value_hash (v
) ^ GPOINTER_TO_UINT (VALUE_FMT (v
));
3092 xlsx_write_workbook (XLSXWriteState
*state
, GsfOutfile
*root_part
)
3097 GPtrArray
*sheetIds
= g_ptr_array_new ();
3098 GsfOutfile
*xl_dir
= (GsfOutfile
*)gsf_outfile_new_child (root_part
, "xl", TRUE
);
3099 GsfOutfile
*wb_part
= (GsfOutfile
*)gsf_outfile_open_pkg_add_rel (xl_dir
, "workbook.xml",
3100 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
3102 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument");
3103 GnmStyle
*style
= gnm_style_new_default ();
3105 state
->xl_dir
= xl_dir
;
3106 state
->shared_string_hash
= g_hash_table_new_full
3107 ((GHashFunc
)rich_value_hash
, (GEqualFunc
)rich_value_equal
,
3108 (GDestroyNotify
)value_release
, NULL
);
3109 state
->shared_string_array
= g_ptr_array_new ();
3110 state
->styles_hash
= g_hash_table_new_full
3111 (gnm_style_hash
, (GEqualFunc
)gnm_style_equal
,
3112 (GDestroyNotify
)gnm_style_unref
, NULL
);
3113 state
->styles_array
= g_ptr_array_new ();
3114 state
->dxfs_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
3115 state
->dxfs_array
= g_ptr_array_new ();
3116 state
->axids
= g_hash_table_new (NULL
, NULL
);
3118 xlsx_get_style_id (state
, style
);
3119 gnm_style_unref (style
);
3121 state
->convs
= xlsx_conventions_new (TRUE
);
3122 xlsx_dir_init (&state
->sheet_dir
, state
->xl_dir
, "worksheets");
3123 xlsx_dir_init (&state
->chart_dir
, state
->xl_dir
, "charts");
3124 xlsx_dir_init (&state
->drawing_dir
, state
->xl_dir
, "drawings");
3125 xlsx_dir_init (&state
->legacy_drawing_dir
, NULL
, NULL
);
3126 xlsx_dir_init (&state
->media_dir
, state
->xl_dir
, "media");
3127 xlsx_dir_init (&state
->pivotCache_dir
, state
->xl_dir
, "pivotCache");
3128 xlsx_dir_init (&state
->pivotTable_dir
, state
->xl_dir
, "pivotTable");
3130 g_ptr_array_set_size (sheetIds
, workbook_sheet_count (state
->base
.wb
));
3131 for (i
= 0 ; i
< workbook_sheet_count (state
->base
.wb
); i
++) {
3132 Sheet
*sheet
= workbook_sheet_by_index (state
->base
.wb
, i
);
3133 const char *rId
= xlsx_write_sheet (state
, wb_part
, sheet
);
3134 g_ptr_array_index (sheetIds
, i
) = (gpointer
)rId
;
3137 xlsx_write_shared_strings (state
, wb_part
);
3138 xlsx_write_styles (state
, wb_part
);
3139 xlsx_write_docprops (state
, root_part
);
3140 cacheRefs
= xlsx_write_pivots (state
, wb_part
);
3142 xml
= gsf_xml_out_new (GSF_OUTPUT (wb_part
));
3143 gsf_xml_out_start_element (xml
, "workbook");
3144 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns", ns_ss
);
3145 gsf_xml_out_add_cstr_unchecked (xml
, "xmlns:r", ns_rel
);
3146 /* Note the schema does not allow the attribute xml:space */
3148 gsf_xml_out_start_element (xml
, "fileVersion");
3149 gsf_xml_out_add_int (xml
, "lastEdited", 4);
3150 gsf_xml_out_add_int (xml
, "lowestEdited", 4);
3151 gsf_xml_out_add_int (xml
, "rupBuild", 3820);
3152 gsf_xml_out_end_element (xml
);
3154 gsf_xml_out_start_element (xml
, "workbookPr");
3155 gsf_xml_out_add_int (xml
, "date1904",
3156 workbook_date_conv (state
->base
.wb
)->use_1904
3158 gsf_xml_out_end_element (xml
);
3160 gsf_xml_out_start_element (xml
, "bookViews");
3161 WORKBOOK_FOREACH_VIEW (state
->base
.wb
, view
, {
3162 int scale
= 10; /* Guess */
3163 gsf_xml_out_start_element (xml
, "workbookView");
3164 gsf_xml_out_add_int (xml
, "activeTab",
3165 view
->current_sheet
->index_in_wb
);
3166 if (view
->preferred_width
> 0)
3167 gsf_xml_out_add_int (xml
, "windowWidth", view
->preferred_width
* scale
);
3168 if (view
->preferred_height
> 0)
3169 gsf_xml_out_add_int (xml
, "windowHeight", view
->preferred_height
* scale
);
3170 gsf_xml_out_end_element (xml
);
3172 gsf_xml_out_end_element (xml
);
3174 gsf_xml_out_start_element (xml
, "sheets");
3175 for (i
= 0 ; i
< workbook_sheet_count (state
->base
.wb
); i
++) {
3176 Sheet
const *sheet
= workbook_sheet_by_index (state
->base
.wb
, i
);
3177 gsf_xml_out_start_element (xml
, "sheet");
3178 gsf_xml_out_add_cstr (xml
, "name", sheet
->name_unquoted
);
3179 gsf_xml_out_add_int (xml
, "sheetId", i
+1); /* FIXME What is this ?? */
3180 gsf_xml_out_add_cstr_unchecked (xml
, "r:id",
3181 g_ptr_array_index (sheetIds
, i
));
3182 gsf_xml_out_end_element (xml
); /* </sheet> */
3184 gsf_xml_out_end_element (xml
); /* </sheets> */
3186 xlsx_write_definedNames (state
, xml
);
3188 xlsx_write_calcPR (state
, xml
);
3190 if (NULL
!= cacheRefs
) {
3193 gsf_xml_out_start_element (xml
, "pivotCaches");
3194 for (ptr
= cacheRefs
; ptr
!= NULL
; ptr
= ptr
->next
) {
3195 gsf_xml_out_start_element (xml
, "pivotCache");
3196 gsf_xml_out_add_int (xml
, "cacheId", i
++);
3197 gsf_xml_out_add_cstr_unchecked (xml
, "r:id", ptr
->data
);
3198 gsf_xml_out_end_element (xml
); /* </pivotCache> */
3200 gsf_xml_out_end_element (xml
); /* </pivotCaches> */
3201 g_slist_free (cacheRefs
);
3203 gsf_xml_out_start_element (xml
, "webPublishing");
3204 xlsx_add_bool (xml
, "allowPng", TRUE
);
3205 xlsx_add_bool (xml
, "css", FALSE
);
3206 if (state
->version
== ECMA_376_2006
)
3207 gsf_xml_out_add_int (xml
, "codePage", 1252); /* FIXME : Use utf-8 ? */
3209 gsf_xml_out_add_cstr_unchecked (xml
, "characterSet", "UTF-8");
3210 gsf_xml_out_end_element (xml
);
3212 gsf_xml_out_end_element (xml
); /* </workbook> */
3213 g_object_unref (xml
);
3215 xlsx_conventions_free (state
->convs
);
3216 g_hash_table_destroy (state
->shared_string_hash
);
3217 g_ptr_array_free (state
->shared_string_array
, TRUE
);
3218 g_hash_table_destroy (state
->styles_hash
);
3219 g_ptr_array_free (state
->styles_array
, TRUE
);
3220 g_hash_table_destroy (state
->dxfs_hash
);
3221 g_ptr_array_free (state
->dxfs_array
, TRUE
);
3222 g_hash_table_destroy (state
->axids
);
3224 xlsx_dir_close (&state
->sheet_dir
);
3225 xlsx_dir_close (&state
->chart_dir
);
3226 xlsx_dir_close (&state
->drawing_dir
);
3227 xlsx_dir_close (&state
->legacy_drawing_dir
);
3228 xlsx_dir_close (&state
->media_dir
);
3229 xlsx_dir_close (&state
->pivotCache_dir
);
3230 xlsx_dir_close (&state
->pivotTable_dir
);
3232 gsf_output_close (GSF_OUTPUT (wb_part
));
3233 g_object_unref (wb_part
);
3235 gsf_output_close (GSF_OUTPUT (xl_dir
));
3236 g_object_unref (xl_dir
);
3238 g_ptr_array_free (sheetIds
, TRUE
);
3241 G_MODULE_EXPORT
void
3242 xlsx_file_save (GOFileSaver
const *fs
, GOIOContext
*io_context
,
3243 gconstpointer wb_view
, GsfOutput
*output
);
3245 xlsx_file_save (G_GNUC_UNUSED GOFileSaver
const *fs
, GOIOContext
*io_context
,
3246 gconstpointer wb_view
, GsfOutput
*output
)
3248 XLSXWriteState state
;
3249 GsfOutfile
*root_part
;
3253 locale
= gnm_push_C_locale ();
3255 state
.version
= ECMA_376_2006
;
3256 state
.with_extension
= TRUE
;
3257 state
.io_context
= io_context
;
3258 state
.base
.wb
= wb_view_get_workbook (wb_view
);
3260 state
.custom_prop_id
= 29;
3261 state
.drawing_elem_id
= 1024;
3263 zip
= gsf_outfile_zip_new (output
, NULL
);
3264 root_part
= gsf_outfile_open_pkg_new (zip
);
3265 g_object_unref (zip
);
3267 xlsx_write_workbook (&state
, root_part
);
3268 gsf_output_close (GSF_OUTPUT (root_part
));
3269 g_object_unref (root_part
);
3271 gnm_pop_C_locale (locale
);
3274 G_MODULE_EXPORT
void
3275 xlsx2_file_save (GOFileSaver
const *fs
, GOIOContext
*io_context
,
3276 gconstpointer wb_view
, GsfOutput
*output
);
3278 xlsx2_file_save (G_GNUC_UNUSED GOFileSaver
const *fs
, GOIOContext
*io_context
,
3279 gconstpointer wb_view
, GsfOutput
*output
)
3281 XLSXWriteState state
;
3282 GsfOutfile
*root_part
;
3286 locale
= gnm_push_C_locale ();
3287 state
.version
= ECMA_376_2008
;
3288 state
.with_extension
= TRUE
;
3289 state
.io_context
= io_context
;
3290 state
.base
.wb
= wb_view_get_workbook (wb_view
);
3292 state
.custom_prop_id
= 29;
3293 state
.drawing_elem_id
= 1024;
3295 zip
= gsf_outfile_zip_new (output
, NULL
);
3296 root_part
= gsf_outfile_open_pkg_new (zip
);
3297 g_object_unref (zip
);
3299 xlsx_write_workbook (&state
, root_part
);
3300 gsf_output_close (GSF_OUTPUT (root_part
));
3301 g_object_unref (root_part
);
3303 gnm_pop_C_locale (locale
);