Arrays: code cleanup.
[gnumeric.git] / plugins / openoffice / openoffice-write.c
blob305b7800271f90fe0398d52eac595d6a8839c839
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * openoffice-write.c : export OpenOffice OASIS .ods files
6 * Copyright (C) 2004-2006 Jody Goldberg (jody@gnome.org)
8 * Copyright (C) 2006-2011 Andreas J. Guelzow (aguelzow@pyrshep.ca)
10 * Copyright (C) 2005 INdT - Instituto Nokia de Tecnologia
11 * Author: Luciano Wolf (luciano.wolf@indt.org.br)
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) version 3.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
26 * USA
29 /*****************************************************************************/
31 #include <gnumeric-config.h>
32 #include <gnumeric.h>
33 #include <workbook-view.h>
34 #include <goffice/goffice.h>
35 #include <gnm-format.h>
36 #include <workbook.h>
37 #include <workbook-priv.h> /* Workbook::names */
38 #include <cell.h>
39 #include <cellspan.h>
40 #include <sheet.h>
41 #include <print-info.h>
42 #include <sheet-view.h>
43 #include <sheet-style.h>
44 #include <sheet-merge.h>
45 #include <style-color.h>
46 #include <expr.h>
47 #include <expr-impl.h>
48 #include <expr-name.h>
49 #include <func.h>
50 #include <value.h>
51 #include <ranges.h>
52 #include <mstyle.h>
53 #include <input-msg.h>
54 #include <style-border.h>
55 #include <validation.h>
56 #include <validation-combo.h>
57 #include <hlink.h>
58 #include <sheet-filter.h>
59 #include <print-info.h>
60 #include <parse-util.h>
61 #include <tools/dao.h>
62 #include <gutils.h>
63 #include <style-conditions.h>
65 #include <sheet-object.h>
66 #include <sheet-object-graph.h>
67 #include <sheet-object-cell-comment.h>
68 #include <sheet-object-image.h>
69 #include <sheet-object-widget.h>
70 #include <gnm-so-filled.h>
71 #include <gnm-so-line.h>
72 #include <gnm-so-path.h>
73 #include <sheet-filter-combo.h>
74 #include <xml-sax.h>
76 #include <gsf/gsf-libxml.h>
77 #include <gsf/gsf-output.h>
78 #include <gsf/gsf-outfile.h>
79 #include <gsf/gsf-outfile-zip.h>
80 #include <gsf/gsf-utils.h>
81 #include <gsf/gsf-opendoc-utils.h>
82 #include <gsf/gsf-doc-meta-data.h>
83 #include <gsf/gsf-meta-names.h>
84 #include <string.h>
86 #include <glib.h>
87 #include <glib/gi18n-lib.h>
89 #define MANIFEST "manifest:"
90 #define OFFICE "office:"
91 #define STYLE "style:"
92 #define TABLE "table:"
93 #define TEXT "text:"
94 #define DUBLINCORE "dc:"
95 #define FOSTYLE "fo:"
96 #define NUMBER "number:"
97 #define DRAW "draw:"
98 #define CHART "chart:"
99 #define SVG "svg:"
100 #define XLINK "xlink:"
101 #define CONFIG "config:"
102 #define FORM "form:"
103 #define SCRIPT "script:"
104 #define OOO "ooo:"
105 #define TABLEOOO "tableooo:"
106 #define XML "xml:"
107 #define CSS "css3t:"
108 #define LOEXT "loext:"
109 #define CALCEXT "calcext:"
110 #define GNMSTYLE "gnm:" /* We use this for attributes and elements not supported by ODF */
112 typedef struct {
113 GsfXMLOut *xml;
114 GsfOutfile *outfile;
115 GOIOContext *ioc;
116 WorkbookView const *wbv;
117 Workbook const *wb;
118 Sheet const *sheet;
119 GnmConventions *conv;
120 GHashTable *openformula_namemap;
121 GHashTable *openformula_handlermap;
122 GSList *row_styles;
123 GSList *col_styles;
124 GHashTable *cell_styles;
125 GHashTable *named_cell_styles;
126 GHashTable *named_cell_style_regions;
127 GHashTable *so_styles;
128 GHashTable *xl_styles;
129 GHashTable *style_names[10];
130 GnmStyleRegion *default_style_region;
131 ColRowInfo const *row_default;
132 ColRowInfo const *column_default;
133 GHashTable *graphs;
134 GHashTable *graph_dashes;
135 GHashTable *graph_hatches;
136 GHashTable *graph_fill_images;
137 GHashTable *graph_gradients;
138 GHashTable *chart_props_hash;
139 GHashTable *arrow_markers;
140 GHashTable *images;
141 GHashTable *controls;
142 GHashTable *text_colours;
143 GHashTable *font_sizes;
145 gboolean with_extension;
146 int odf_version;
147 char *odf_version_string;
148 GOFormat const *time_fmt;
149 GOFormat const *date_fmt;
150 GOFormat const *date_long_fmt;
152 char const *object_name;
153 GogView *root_view;
155 /* for the manifest */
156 GSList *fill_image_files; /* image/png */
158 float last_progress;
159 float graph_progress;
160 float sheet_progress;
161 } GnmOOExport;
163 typedef struct {
164 GnmConventions base;
165 GnmOOExport *state;
166 } ODFConventions;
169 typedef struct {
170 char *name;
171 ColRowInfo const *ci;
172 } col_row_styles_t;
174 static struct {
175 char const *key;
176 char const *url;
177 } const ns[] = {
178 { "xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0" },
179 { "xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"},
180 { "xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0" },
181 { "xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0" },
182 { "xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" },
183 { "xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:" "xsl-fo-compatible:1.0"},
184 { "xmlns:xlink", "http://www.w3.org/1999/xlink" },
185 { "xmlns:dc", "http://purl.org/dc/elements/1.1/" },
186 { "xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0" },
187 { "xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" },
188 { "xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" },
189 { "xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0" },
190 { "xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" },
191 { "xmlns:config", "urn:oasis:names:tc:opendocument:xmlns:config:1.0"},
192 { "xmlns:math", "http://www.w3.org/1998/Math/MathML" },
193 { "xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0" },
194 { "xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0" },
195 { "xmlns:ooo", "http://openoffice.org/2004/office" },
196 { "xmlns:ooow", "http://openoffice.org/2004/writer" },
197 { "xmlns:oooc", "http://openoffice.org/2004/calc" },
198 { "xmlns:tableooo", "http://openoffice.org/2009/table" },
199 { "xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2" },
200 { "xmlns:dom", "http://www.w3.org/2001/xml-events" },
201 { "xmlns:xforms", "http://www.w3.org/2002/xforms" },
202 { "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" },
203 { "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" },
204 { "xmlns:gnm", "http://www.gnumeric.org/odf-extension/1.0"},
205 { "xmlns:css3t", "http://www.w3.org/TR/css3-text/"},
206 { "xmlns:loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"},
207 { "xmlns:calcext", "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"},
210 /*****************************************************************************/
212 static void odf_write_fill_images_info (GOImage *image, char const *name, GnmOOExport *state);
213 static void odf_write_gradient_info (GOStyle const *style, char const *name, GnmOOExport *state);
214 static void odf_write_hatch_info (GOPattern *pattern, char const *name, GnmOOExport *state);
215 static void odf_write_dash_info (char const *name, gpointer data, GnmOOExport *state);
216 static void odf_write_arrow_marker_info (GOArrow const *arrow, char const *name, GnmOOExport *state);
218 static void odf_write_gog_style_graphic (GnmOOExport *state, GOStyle const *style, gboolean write_border);
219 static void odf_write_gog_style_text (GnmOOExport *state, GOStyle const *style);
222 /*****************************************************************************/
224 #define PROGRESS_STEPS 500
225 static void
226 odf_update_progress (GnmOOExport *state, float delta)
228 int old = state->last_progress;
229 int new;
231 state->last_progress += delta;
232 new = state->last_progress;
234 if (new != old)
235 go_io_value_progress_update (state->ioc, new);
238 /*****************************************************************************/
240 typedef enum {
241 OO_ITEM_TABLE_STYLE,
242 OO_ITEM_TABLE_MASTER_PAGE_STYLE,
243 OO_ITEM_PAGE_LAYOUT,
244 OO_ITEM_UNSTYLED_GRAPH_OBJECT,
245 OO_ITEM_GRAPH_STYLE,
246 OO_ITEM_SHEET_OBJECT,
247 OO_ITEM_SHEET_OBJECT_LINE,
248 OO_ITEM_MSTYLE,
249 OO_ITEM_VALIDATION,
250 OO_ITEM_INPUT_MSG
251 } OONamedItemType;
254 static char *
255 oo_item_name (GnmOOExport *state, OONamedItemType typ, gconstpointer ptr)
257 static const char * const
258 prefixes[G_N_ELEMENTS (state->style_names)] = {
259 "ta",
260 "ta-mp",
261 "pl",
262 "GOG-",
263 "GOG",
264 "so-g",
265 "so-g-l",
266 "ACE",
267 "VAL",
268 "VAL-IM"
270 char *name;
272 g_return_val_if_fail ((size_t)typ <= G_N_ELEMENTS (prefixes), NULL);
274 name = g_hash_table_lookup (state->style_names[typ], ptr);
275 if (name) {
276 if (!g_str_has_prefix (name, prefixes[typ]))
277 g_warning ("Style name confusion.");
278 } else {
279 name = g_strdup_printf
280 ("%s-%u", prefixes[typ],
281 g_hash_table_size (state->style_names[typ]));
282 g_hash_table_replace (state->style_names[typ],
283 (gpointer)ptr,
284 name);
286 return g_strdup (name);
290 static char *
291 table_style_name (GnmOOExport *state, Sheet const *sheet)
293 return oo_item_name (state, OO_ITEM_TABLE_STYLE, sheet);
296 static char *
297 table_master_page_style_name (GnmOOExport *state, Sheet const *sheet)
299 return oo_item_name (state, OO_ITEM_TABLE_MASTER_PAGE_STYLE, sheet);
302 static char *
303 page_layout_name (GnmOOExport *state, GnmPrintInformation *pi)
305 return oo_item_name (state, OO_ITEM_PAGE_LAYOUT, pi);
309 /*****************************************************************************/
311 static void
312 odf_write_mimetype (G_GNUC_UNUSED GnmOOExport *state, GsfOutput *child)
314 gsf_output_puts (child, "application/vnd.oasis.opendocument.spreadsheet");
317 /*****************************************************************************/
319 static void
320 odf_add_range (GnmOOExport *state, GnmRange const *r)
322 g_return_if_fail (range_is_sane (r));
324 gsf_xml_out_add_int (state->xml, GNMSTYLE "start-col", r->start.col);
325 gsf_xml_out_add_int (state->xml, GNMSTYLE "start-row", r->start.row);
326 gsf_xml_out_add_int (state->xml, GNMSTYLE "end-col", r->end.col);
327 gsf_xml_out_add_int (state->xml, GNMSTYLE "end-row", r->end.row);
330 static void
331 odf_add_font_weight (GnmOOExport *state, int weight)
333 weight = ((weight+50)/100)*100;
334 if (weight > 900)
335 weight = 900;
336 if (weight < 100)
337 weight = 100;
339 /* MS Excel 2007/2010 is badly confused about which weights are normal */
340 /* and/or bold, so we don't just save numbers. See */
341 /* http://msdn.microsoft.com/en-us/library/ff528991%28v=office.12%29.aspx */
342 /* although ODF refers to */
343 /* http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight */
344 /* where it is clear that 400 == normal and 700 == bold */
345 if (weight == PANGO_WEIGHT_NORMAL)
346 gsf_xml_out_add_cstr_unchecked (state->xml, FOSTYLE "font-weight",
347 "normal");
348 else if (weight == PANGO_WEIGHT_BOLD)
349 gsf_xml_out_add_cstr_unchecked (state->xml, FOSTYLE "font-weight",
350 "bold");
351 else
352 gsf_xml_out_add_int (state->xml, FOSTYLE "font-weight", weight);
356 static void
357 odf_add_chars_non_white (GnmOOExport *state, char const *text, int len)
359 char * str;
361 g_return_if_fail (len > 0);
363 str = g_strndup (text, len);
364 gsf_xml_out_add_cstr (state->xml, NULL, str);
365 g_free (str);
368 static void
369 odf_add_chars (GnmOOExport *state, char const *text, int len, gboolean *white_written)
371 int nw = strcspn(text, " \n\t");
373 if (nw >= len) {
374 odf_add_chars_non_white (state, text, len);
375 *white_written = FALSE;
376 return;
379 if (nw > 0) {
380 odf_add_chars_non_white (state, text, nw);
381 text += nw;
382 len -= nw;
383 *white_written = FALSE;
386 switch (*text) {
387 case ' ':
389 int white = strspn(text, " ");
391 if (!*white_written) {
392 gsf_xml_out_add_cstr (state->xml, NULL, " ");
393 len--;
394 white--;
395 text++;
396 *white_written = TRUE;
398 if (white > 0) {
399 gsf_xml_out_start_element (state->xml, TEXT "s");
400 if (white > 1)
401 gsf_xml_out_add_int (state->xml, TEXT "c", white);
402 gsf_xml_out_end_element (state->xml);
403 len -= white;
404 text += white;
407 break;
408 case '\n':
409 gsf_xml_out_start_element (state->xml, TEXT "line-break");
410 gsf_xml_out_end_element (state->xml);
411 text++;
412 len--;
413 break;
414 case '\t':
415 gsf_xml_out_start_element (state->xml, TEXT "tab");
416 gsf_xml_out_end_element (state->xml);
417 text++;
418 len--;
419 break;
420 default:
421 /* This really shouldn't happen */
422 g_warning ("How can we get here?");
423 break;
426 if (len > 0)
427 odf_add_chars (state, text, len, white_written);
430 static int
431 odf_attrs_as_string (GnmOOExport *state, PangoAttribute *a)
433 int spans = 0;
435 switch (a->klass->type) {
436 case PANGO_ATTR_FAMILY :
437 break; /* ignored */
438 case PANGO_ATTR_SIZE :
440 char * str;
441 gint size = ((PangoAttrInt *)a)->value/PANGO_SCALE;
442 str = g_strdup_printf ("NS-font-size%i", size);
443 spans += 1;
444 gsf_xml_out_start_element (state->xml, TEXT "span");
445 gsf_xml_out_add_cstr (state->xml, TEXT "style-name", str);
446 g_hash_table_insert (state->font_sizes,
447 str, GINT_TO_POINTER (size));
449 break;
450 case PANGO_ATTR_RISE:
451 gsf_xml_out_start_element (state->xml, TEXT "span");
452 if (((PangoAttrInt *)a)->value != 0) {
453 gsf_xml_out_add_cstr (state->xml, TEXT "style-name",
454 (((PangoAttrInt *)a)->value < 0)
455 ? "AC-subscript" : "AC-superscript");
456 } else
457 gsf_xml_out_add_cstr (state->xml, TEXT "style-name", "AC-script");
458 spans += 1;
459 break;
460 case PANGO_ATTR_STYLE :
461 spans += 1;
462 gsf_xml_out_start_element (state->xml, TEXT "span");
463 gsf_xml_out_add_cstr (state->xml, TEXT "style-name",
464 (((PangoAttrInt *)a)->value
465 == PANGO_STYLE_ITALIC)
466 ? "AC-italic" : "AC-roman");
467 break;
468 case PANGO_ATTR_WEIGHT :
470 char * str = g_strdup_printf ("AC-weight%i",
471 ((((PangoAttrInt *)a)->value
472 +50)/100)*100);
473 spans += 1;
474 gsf_xml_out_start_element (state->xml, TEXT "span");
475 gsf_xml_out_add_cstr (state->xml, TEXT "style-name", str);
476 g_free (str);
478 break;
479 case PANGO_ATTR_STRIKETHROUGH :
480 spans += 1;
481 gsf_xml_out_start_element (state->xml, TEXT "span");
482 gsf_xml_out_add_cstr (state->xml, TEXT "style-name",
483 ((PangoAttrInt *)a)->value
484 ? "AC-strikethrough-solid"
485 : "AC-strikethrough-none");
486 break;
487 case PANGO_ATTR_UNDERLINE :
489 char const *name = NULL;
490 switch (((PangoAttrInt *)a)->value) {
491 case PANGO_UNDERLINE_NONE :
492 name = "AC-underline-none";
493 break;
494 case PANGO_UNDERLINE_SINGLE :
495 name = "AC-underline-single";
496 break;
497 case PANGO_UNDERLINE_DOUBLE :
498 name = "AC-underline-double";
499 break;
500 case PANGO_UNDERLINE_LOW :
501 name = "AC-underline-low";
502 break;
503 case PANGO_UNDERLINE_ERROR :
504 name = "AC-underline-error";
505 break;
506 default:
507 return spans;
509 spans += 1;
510 gsf_xml_out_start_element (state->xml, TEXT "span");
511 gsf_xml_out_add_cstr (state->xml, TEXT "style-name", name);
513 break;
514 case PANGO_ATTR_FOREGROUND :
516 PangoColor const *c;
517 gchar *c_str;
518 gchar *name;
520 c = &((PangoAttrColor *)a)->color;
521 c_str = g_strdup_printf ("#%02x%02x%02x",
522 ((c->red & 0xff00) >> 8),
523 ((c->green & 0xff00) >> 8),
524 ((c->blue & 0xff00) >> 8));
525 name = g_strdup_printf ("NS-colour-%s", c_str + 1);
526 gsf_xml_out_start_element (state->xml, TEXT "span");
527 gsf_xml_out_add_cstr (state->xml, TEXT "style-name", name);
528 spans += 1;
529 g_hash_table_insert (state->text_colours, name, c_str);
531 break;
532 default :
533 if (a->klass->type ==
534 go_pango_attr_subscript_get_attr_type ()) {
535 gsf_xml_out_start_element (state->xml, TEXT "span");
536 gsf_xml_out_add_cstr (state->xml, TEXT "style-name",
537 ((GOPangoAttrSubscript *)a)->val ?
538 "AC-subscript" : "AC-script");
539 spans += 1;
540 } else if (a->klass->type ==
541 go_pango_attr_superscript_get_attr_type ()) {
542 gsf_xml_out_start_element (state->xml, TEXT "span");
543 gsf_xml_out_add_cstr (state->xml, TEXT "style-name",
544 ((GOPangoAttrSuperscript *)a)->val ?
545 "AC-superscript" : "AC-script");
546 spans += 1;
548 break; /* ignored otherwise */
551 return spans;
554 static void
555 odf_new_markup (GnmOOExport *state, const PangoAttrList *markup, char const *text)
557 int handled = 0;
558 PangoAttrIterator * iter;
559 int from, to;
560 int len = text ? strlen (text) : 0;
561 /* Since whitespace at the beginning of a <text:p> will be deleted upon */
562 /* reading, we need to behave as if we have already written whitespace and */
563 /* use <text:s> if necessary */
564 gboolean white_written = TRUE;
566 if (len == 0)
567 return;
568 if (markup == NULL) {
569 odf_add_chars (state, text, len, &white_written);
570 return;
573 iter = pango_attr_list_get_iterator ((PangoAttrList *) markup);
575 do {
576 GSList *list, *l;
577 int spans = 0;
579 pango_attr_iterator_range (iter, &from, &to);
580 to = (to > len) ? len : to; /* Since "to" can be really big! */
581 from = (from > len) ? len : from; /* Since "from" can also be really big! */
582 if (from > handled)
583 odf_add_chars (state, text + handled, from - handled, &white_written);
584 list = pango_attr_iterator_get_attrs (iter);
585 for (l = list; l != NULL; l = l->next) {
586 PangoAttribute *a = l->data;
587 spans += odf_attrs_as_string (state, a);
588 pango_attribute_destroy (a);
590 g_slist_free (list);
591 if (to > from) {
592 odf_add_chars (state, text + from, to - from, &white_written);
594 while (spans-- > 0)
595 gsf_xml_out_end_element (state->xml); /* </text:span> */
596 handled = to;
597 } while (pango_attr_iterator_next (iter));
599 pango_attr_iterator_destroy (iter);
601 return;
605 /*****************************************************************************/
607 static void
608 odf_add_bool (GsfXMLOut *xml, char const *id, gboolean val)
610 gsf_xml_out_add_cstr_unchecked (xml, id, val ? "true" : "false");
613 static void
614 odf_add_angle (GsfXMLOut *xml, char const *id, int val)
616 if (val == -1)
617 val = 90;
618 gsf_xml_out_add_int (xml, id, val);
621 static void
622 odf_add_percent (GsfXMLOut *xml, char const *id, double val)
624 GString *str = g_string_new (NULL);
626 g_string_append_printf (str, "%.2f%%", val * 100.);
627 gsf_xml_out_add_cstr_unchecked (xml, id, str->str);
628 g_string_free (str, TRUE);
632 static void
633 odf_add_pt (GsfXMLOut *xml, char const *id, double l)
635 GString *str = g_string_new (NULL);
636 go_dtoa (str, "!g", l);
637 g_string_append (str, "pt");
638 gsf_xml_out_add_cstr_unchecked (xml, id, str->str);
639 g_string_free (str, TRUE);
642 static char *
643 odf_go_color_to_string (GOColor color)
645 return g_strdup_printf ("#%.2x%.2x%.2x",
646 GO_COLOR_UINT_R (color),
647 GO_COLOR_UINT_G (color),
648 GO_COLOR_UINT_B (color));
651 static double
652 odf_go_color_opacity (GOColor color)
654 return (GO_COLOR_UINT_A (color)/255.);
657 static void
658 gnm_xml_out_add_hex_color (GsfXMLOut *o, char const *id, GnmColor const *c, int pattern)
660 g_return_if_fail (c != NULL);
662 if (pattern == 0)
663 gsf_xml_out_add_cstr_unchecked (o, id, "transparent");
664 else {
665 char *color;
666 color = odf_go_color_to_string (c->go_color);
667 gsf_xml_out_add_cstr_unchecked (o, id, color);
668 g_free (color);
672 static void
673 odf_write_plot_style_int (GsfXMLOut *xml, GogObject const *plot,
674 char const *property, char const *id)
676 int i;
677 if (gnm_object_has_readable_prop (plot, property, G_TYPE_INT, &i))
678 gsf_xml_out_add_int (xml, id, i);
681 static void
682 odf_write_plot_style_uint (GsfXMLOut *xml, GogObject const *plot,
683 char const *property, char const *id)
685 unsigned int ui;
686 if (gnm_object_has_readable_prop (plot, property, G_TYPE_UINT, &ui))
687 gsf_xml_out_add_uint (xml, id, ui);
690 static void
691 odf_write_plot_style_double (GsfXMLOut *xml, GogObject const *plot,
692 char const *property, char const *id)
694 double d;
695 if (gnm_object_has_readable_prop (plot, property, G_TYPE_DOUBLE, &d))
696 go_xml_out_add_double (xml, id, d);
699 static void
700 odf_write_plot_style_double_percent (GsfXMLOut *xml, GogObject const *plot,
701 char const *property, char const *id)
703 double d;
704 if (gnm_object_has_readable_prop (plot, property, G_TYPE_DOUBLE, &d))
705 odf_add_percent (xml, id, d);
708 static void
709 odf_write_plot_style_bool (GsfXMLOut *xml, GogObject const *plot,
710 char const *property, char const *id)
712 gboolean b;
713 if (gnm_object_has_readable_prop (plot, property, G_TYPE_BOOLEAN, &b))
714 odf_add_bool (xml, id, b);
717 static void
718 odf_write_plot_style_from_bool (GsfXMLOut *xml, GogObject const *plot,
719 char const *property, char const *id,
720 char const *t_val, char const *f_val)
722 gboolean b;
723 if (gnm_object_has_readable_prop (plot, property, G_TYPE_BOOLEAN, &b))
724 gsf_xml_out_add_cstr (xml, id, b ? t_val : f_val);
727 static void
728 odf_start_style (GsfXMLOut *xml, char const *name, char const *family)
730 gsf_xml_out_start_element (xml, STYLE "style");
731 gsf_xml_out_add_cstr_unchecked (xml, STYLE "name", name);
732 gsf_xml_out_add_cstr_unchecked (xml, STYLE "family", family);
735 static void
736 odf_write_table_style (GnmOOExport *state, Sheet const *sheet)
738 char *name = table_style_name (state, sheet);
739 char *mp_name = table_master_page_style_name (state, sheet);
741 odf_start_style (state->xml, name, "table");
742 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "master-page-name", mp_name);
744 gsf_xml_out_start_element (state->xml, STYLE "table-properties");
745 odf_add_bool (state->xml, TABLE "display",
746 sheet->visibility == GNM_SHEET_VISIBILITY_VISIBLE);
747 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "writing-mode",
748 sheet->text_is_rtl ? "rl-tb" : "lr-tb");
749 if (state->with_extension) {
750 if (state->odf_version < 103) {
751 if (sheet->tab_color && !sheet->tab_color->is_auto) {
752 gnm_xml_out_add_hex_color (state->xml, GNMSTYLE "tab-color",
753 sheet->tab_color, 1);
754 gnm_xml_out_add_hex_color (state->xml, TABLEOOO "tab-color",
755 sheet->tab_color, 1);
757 if (sheet->tab_text_color && !sheet->tab_text_color->is_auto) {
758 gnm_xml_out_add_hex_color (state->xml,
759 GNMSTYLE "tab-text-color",
760 sheet->tab_text_color, 1);
763 odf_add_bool (state->xml, GNMSTYLE "display-formulas", sheet->display_formulas);
764 odf_add_bool (state->xml, GNMSTYLE "display-col-header", !sheet->hide_col_header);
765 odf_add_bool (state->xml, GNMSTYLE "display-row-header", !sheet->hide_row_header);
767 if (state->odf_version >= 103)
768 gnm_xml_out_add_hex_color (state->xml, TABLE "tab-color",
769 sheet->tab_color, 1);
770 gsf_xml_out_end_element (state->xml); /* </style:table-properties> */
772 gsf_xml_out_end_element (state->xml); /* </style:style> */
774 g_free (name);
775 g_free (mp_name);
778 static gchar*
779 odf_get_gog_style_name (GnmOOExport *state,
780 GOStyle const *style, GogObject const *obj)
782 if (style == NULL)
783 return oo_item_name (state, OO_ITEM_UNSTYLED_GRAPH_OBJECT, obj);
784 else
785 return oo_item_name (state, OO_ITEM_GRAPH_STYLE, style);
788 static gchar*
789 odf_get_gog_style_name_from_obj (GnmOOExport *state, GogObject const *obj)
791 GOStyle *style = NULL;
793 if (gnm_object_has_readable_prop (obj, "style", G_TYPE_NONE, &style)) {
794 char *name = odf_get_gog_style_name (state, style, obj);
795 g_object_unref (style);
796 return name;
797 } else
798 return odf_get_gog_style_name (state, NULL, obj);
801 static const char*
802 xl_find_format_xl (GnmOOExport *state, char const *xl)
804 char *found = g_hash_table_lookup (state->xl_styles, xl);
806 if (found == NULL) {
807 found = g_strdup_printf ("ND-%d",
808 g_hash_table_size (state->xl_styles));
809 g_hash_table_insert (state->xl_styles, g_strdup (xl), found);
811 return found;
814 static const char*
815 xl_find_format (GnmOOExport *state, GOFormat const *format)
817 return xl_find_format_xl (state, go_format_as_XL (format));
820 static void
821 odf_write_table_styles (GnmOOExport *state)
823 int i;
825 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
826 Sheet const *sheet = workbook_sheet_by_index (state->wb, i);
827 odf_write_table_style (state, sheet);
831 static gboolean
832 odf_match_arrow_markers (GOArrow const *old, GOArrow const *new)
834 return (old->typ == new->typ &&
835 old->a == new->a &&
836 old->b == new->b &&
837 old->c == new->c);
840 static gchar const*
841 odf_get_arrow_marker_name (GnmOOExport *state, GOArrow *arrow)
843 gchar const *name = g_hash_table_lookup (state->arrow_markers,
844 (gpointer) arrow);
845 gchar *new_name;
846 if (name != NULL)
847 return name;
849 new_name = g_strdup_printf ("gnm-arrow-%i-%.2f-%.2f-%.2f-%i",
850 arrow->typ,
851 arrow->a,
852 arrow->b,
853 arrow->c,
854 g_hash_table_size (state->arrow_markers));
855 g_hash_table_insert (state->arrow_markers,
856 (gpointer) arrow, new_name);
857 return new_name;
861 static char *
862 odf_write_sheet_object_style (GnmOOExport *state, SheetObject *so)
864 char *name = oo_item_name (state, OO_ITEM_SHEET_OBJECT, so);
865 GOStyle *style = NULL;
867 (void)gnm_object_has_readable_prop (so, "style", G_TYPE_NONE, &style);
869 odf_start_style (state->xml, name, "graphic");
870 gsf_xml_out_start_element (state->xml, STYLE "graphic-properties");
871 odf_write_gog_style_graphic (state, style, FALSE);
872 gsf_xml_out_end_element (state->xml); /* </style:graphic-properties> */
873 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
874 odf_write_gog_style_text (state, style);
875 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
876 gsf_xml_out_end_element (state->xml); /* </style:style> */
878 if (style != NULL)
879 g_object_unref (style);
880 return name;
883 static char *
884 odf_write_sheet_object_line_style (GnmOOExport *state, SheetObject *so)
886 char *name = oo_item_name (state, OO_ITEM_SHEET_OBJECT_LINE, so);
887 GOStyle *style = NULL;
888 GOArrow *start = NULL, *end = NULL;
889 char const *start_arrow_name = NULL;
890 char const *end_arrow_name = NULL;
892 g_object_get (G_OBJECT (so),
893 "style", &style,
894 "start-arrow", &start,
895 "end-arrow", &end, NULL);
897 if (start != NULL && start->typ != GO_ARROW_NONE)
898 start_arrow_name = odf_get_arrow_marker_name (state, start);
899 else
900 g_free (start);
901 if (end != NULL && end->typ != GO_ARROW_NONE)
902 end_arrow_name = odf_get_arrow_marker_name (state, end);
903 else
904 g_free (end);
906 odf_start_style (state->xml, name, "graphic");
907 gsf_xml_out_start_element (state->xml, STYLE "graphic-properties");
908 if (start_arrow_name != NULL)
909 gsf_xml_out_add_cstr (state->xml, DRAW "marker-start", start_arrow_name);
910 if (end_arrow_name != NULL)
911 gsf_xml_out_add_cstr (state->xml, DRAW "marker-end", end_arrow_name);
912 odf_write_gog_style_graphic (state, style, FALSE);
913 gsf_xml_out_end_element (state->xml); /* </style:graphic-properties> */
914 gsf_xml_out_end_element (state->xml); /* </style:style> */
916 if (style != NULL)
917 g_object_unref (style);
918 return name;
921 static void
922 odf_write_sheet_object_styles (GnmOOExport *state)
924 int i;
926 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
927 Sheet const *sheet = workbook_sheet_by_index (state->wb, i);
928 GSList *objects = sheet_objects_get (sheet, NULL, GNM_SO_FILLED_TYPE), *l;
929 for (l = objects; l != NULL; l = l->next) {
930 SheetObject *so = GNM_SO (l->data);
931 char *name = odf_write_sheet_object_style (state, so);
932 g_hash_table_replace (state->so_styles, so, name);
934 g_slist_free (objects);
935 objects = sheet_objects_get (sheet, NULL, GNM_SO_LINE_TYPE);
936 for (l = objects; l != NULL; l = l->next) {
937 SheetObject *so = GNM_SO (l->data);
938 char *name = odf_write_sheet_object_line_style (state, so);
939 g_hash_table_replace (state->so_styles, so, name);
941 g_slist_free (objects);
942 objects = sheet_objects_get (sheet, NULL, GNM_SO_PATH_TYPE);
943 for (l = objects; l != NULL; l = l->next) {
944 SheetObject *so = GNM_SO (l->data);
945 char *name = odf_write_sheet_object_style (state, so);
946 g_hash_table_replace (state->so_styles, so, name);
948 g_slist_free (objects);
952 static void
953 odf_write_gog_position_pts (GnmOOExport *state, GogObject const *title)
955 gboolean is_position_manual = TRUE;
957 g_object_get (G_OBJECT (title),
958 "is-position-manual", &is_position_manual,
959 NULL);
961 if (is_position_manual) {
962 GogView *view = gog_view_find_child_view (state->root_view, title);
963 odf_add_pt (state->xml, SVG "x", view->allocation.x);
964 odf_add_pt (state->xml, SVG "y", view->allocation.y);
968 static void
969 odf_write_gog_position (GnmOOExport *state, GogObject const *obj)
971 gboolean is_position_manual = TRUE;
972 gchar *position = NULL, *anchor = NULL, *compass = NULL;
974 if (!state->with_extension)
975 return;
977 (void)gnm_object_has_readable_prop (obj, "compass",
978 G_TYPE_NONE, &compass);
979 g_object_get (G_OBJECT (obj),
980 "is-position-manual", &is_position_manual,
981 "position", &position,
982 "anchor", &anchor,
983 NULL);
984 odf_add_bool (state->xml, GNMSTYLE "is-position-manual", is_position_manual);
985 if (is_position_manual) {
986 if (position)
987 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "position", position);
988 if (anchor)
989 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "anchor", anchor);
990 } else if (compass)
991 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "compass", position);
993 g_free (position);
994 g_free (anchor);
995 g_free (compass);
998 static void
999 odf_write_gog_plot_area_position (GnmOOExport *state, GogObject const *obj)
1001 gboolean is_position_manual = TRUE;
1002 gchar *position = NULL;
1004 if (!state->with_extension)
1005 return;
1007 g_object_get (G_OBJECT (obj),
1008 "is-plot-area-manual", &is_position_manual,
1009 "plot-area", &position,
1010 NULL);
1011 odf_add_bool (state->xml, GNMSTYLE "is-position-manual", is_position_manual);
1012 if (is_position_manual && position)
1013 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "position", position);
1015 g_free (position);
1018 static char *
1019 odf_get_border_format (GnmBorder *border)
1021 GString *str = g_string_new (NULL);
1022 double w = gnm_style_border_get_width (border->line_type);
1023 GnmColor *color = border->color;
1024 char const *border_type;
1026 switch (border->line_type) {
1027 case GNM_STYLE_BORDER_THIN:
1028 w = 1.;
1029 border_type = "solid";
1030 break;
1031 case GNM_STYLE_BORDER_MEDIUM:
1032 border_type = "solid";
1033 break;
1034 case GNM_STYLE_BORDER_DASHED:
1035 border_type = "dashed";
1036 break;
1037 case GNM_STYLE_BORDER_DOTTED:
1038 border_type = "dotted";
1039 break;
1040 case GNM_STYLE_BORDER_THICK:
1041 border_type = "solid";
1042 break;
1043 case GNM_STYLE_BORDER_DOUBLE:
1044 border_type = "double";
1045 break;
1046 case GNM_STYLE_BORDER_HAIR:
1047 w = 0.5;
1048 border_type = "solid";
1049 break;
1050 case GNM_STYLE_BORDER_MEDIUM_DASH:
1051 border_type = "dashed";
1052 break;
1053 case GNM_STYLE_BORDER_DASH_DOT:
1054 border_type = "dashed";
1055 break;
1056 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT:
1057 border_type = "dashed";
1058 break;
1059 case GNM_STYLE_BORDER_DASH_DOT_DOT:
1060 border_type = "dotted";
1061 break;
1062 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT:
1063 border_type = "dotted";
1064 break;
1065 case GNM_STYLE_BORDER_SLANTED_DASH_DOT:
1066 border_type = "dotted";
1067 break;
1068 case GNM_STYLE_BORDER_NONE:
1069 default:
1070 w = 0;
1071 border_type = "none";
1072 break;
1075 w = GO_PT_TO_CM (w);
1076 g_string_append_printf (str, "%.3fcm ", w);
1077 g_string_append (str, border_type);
1078 g_string_append_printf (str, " #%.2x%.2x%.2x",
1079 GO_COLOR_UINT_R (color->go_color),
1080 GO_COLOR_UINT_G (color->go_color),
1081 GO_COLOR_UINT_B (color->go_color));
1082 return g_string_free (str, FALSE);
1085 static char const *
1086 odf_get_gnm_border_format (GnmBorder *border)
1088 char const *border_type = NULL;
1090 switch (border->line_type) {
1091 case GNM_STYLE_BORDER_HAIR:
1092 border_type = "hair";
1093 break;
1094 case GNM_STYLE_BORDER_MEDIUM_DASH:
1095 border_type = "medium-dash";
1096 break;
1097 case GNM_STYLE_BORDER_DASH_DOT:
1098 border_type = "dash-dot";
1099 break;
1100 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT:
1101 border_type = "medium-dash-dot";
1102 break;
1103 case GNM_STYLE_BORDER_DASH_DOT_DOT:
1104 border_type = "dash-dot-dot";
1105 break;
1106 case GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT:
1107 border_type = "medium-dash-dot-dot";
1108 break;
1109 case GNM_STYLE_BORDER_SLANTED_DASH_DOT:
1110 border_type = "slanted-dash-dot";
1111 break;
1112 default:
1113 break;
1115 return border_type;
1119 /* ODF write style */
1120 /* */
1121 /* We have to write our style information and map them to ODF expectations */
1122 /* This is supposed to match how we read the styles again in openoffice-read.c */
1123 /* Note that we are introducing foreign elemetns as required for round tripping */
1126 #define BORDERSTYLE(msbw, msbwstr, msbwstr_wth, msbwstr_gnm) if (gnm_style_is_element_set (style, msbw)) { \
1127 GnmBorder *border = gnm_style_get_border (style, msbw); \
1128 if (border != NULL) { \
1129 char *border_style = odf_get_border_format (border); \
1130 char const *gnm_border_style = odf_get_gnm_border_format (border); \
1131 gsf_xml_out_add_cstr_unchecked (state->xml, msbwstr, border_style); \
1132 g_free (border_style); \
1133 if (gnm_border_style != NULL && state->with_extension) \
1134 gsf_xml_out_add_cstr_unchecked (state->xml, msbwstr_gnm, gnm_border_style); \
1135 if (border->line_type == GNM_STYLE_BORDER_DOUBLE) \
1136 gsf_xml_out_add_cstr_unchecked (state->xml, msbwstr_wth, "0.03cm 0.03cm 0.03cm "); \
1140 #define UNDERLINESPECS(type, style, width, low) gsf_xml_out_add_cstr (state->xml, \
1141 STYLE "text-underline-type", type); \
1142 gsf_xml_out_add_cstr (state->xml, \
1143 STYLE "text-underline-style", style); \
1144 gsf_xml_out_add_cstr (state->xml, \
1145 STYLE "text-underline-width", width); \
1146 gsf_xml_out_add_cstr_unchecked (state->xml, \
1147 STYLE "text-underline-color", "font-color"); \
1148 gsf_xml_out_add_cstr_unchecked (state->xml, \
1149 STYLE "text-underline-mode", "continuous"); \
1150 if (low && state->with_extension) \
1151 gsf_xml_out_add_cstr_unchecked (state->xml, \
1152 GNMSTYLE "text-underline-placement", "low"); \
1154 static void
1155 odf_write_style_cell_properties (GnmOOExport *state, GnmStyle const *style)
1157 gboolean test1, test2;
1159 gsf_xml_out_start_element (state->xml, STYLE "table-cell-properties");
1160 /* Background Color */
1161 if (gnm_style_is_element_set (style, MSTYLE_COLOR_BACK)) {
1162 gboolean pattern_set = gnm_style_is_element_set (style, MSTYLE_PATTERN);
1163 int pattern = pattern_set ? gnm_style_get_pattern (style) : 1;
1165 gnm_xml_out_add_hex_color (state->xml, FOSTYLE "background-color",
1166 gnm_style_get_back_color (style), pattern);
1167 if (state->with_extension) {
1168 /* We save this to retain as much state as possible. */
1169 gnm_xml_out_add_hex_color (state->xml, GNMSTYLE "background-colour",
1170 gnm_style_get_back_color (style), 1);
1171 if (gnm_style_is_element_set (style, MSTYLE_COLOR_PATTERN))
1172 gnm_xml_out_add_hex_color (state->xml, GNMSTYLE "pattern-colour",
1173 gnm_style_get_pattern_color (style), 1);
1174 gsf_xml_out_add_int (state->xml, GNMSTYLE "pattern", pattern);
1177 /* Borders */
1178 BORDERSTYLE(MSTYLE_BORDER_TOP,FOSTYLE "border-top", STYLE "border-line-width-top", GNMSTYLE "border-line-style-top");
1179 BORDERSTYLE(MSTYLE_BORDER_BOTTOM,FOSTYLE "border-bottom", STYLE "border-line-width-bottom", GNMSTYLE "border-line-style-bottom");
1180 BORDERSTYLE(MSTYLE_BORDER_LEFT,FOSTYLE "border-left", STYLE "border-line-width-left", GNMSTYLE "border-line-style-left");
1181 BORDERSTYLE(MSTYLE_BORDER_RIGHT,FOSTYLE "border-right", STYLE "border-line-width-right", GNMSTYLE "border-line-style-right");
1182 BORDERSTYLE(MSTYLE_BORDER_DIAGONAL,STYLE "diagonal-bl-tr", STYLE "diagonal-bl-tr-widths", GNMSTYLE "diagonal-bl-tr-line-style");
1183 BORDERSTYLE(MSTYLE_BORDER_REV_DIAGONAL,STYLE "diagonal-tl-br", STYLE "diagonal-tl-br-widths", GNMSTYLE "diagonal-tl-br-line-style");
1184 /* note that we are at this time not setting any of:
1185 fo:padding 18.209,
1186 fo:padding-bottom 18.210,
1187 fo:padding-left 18.211,
1188 fo:padding-right 18.212,
1189 fo:padding-top 18.213,
1190 style:shadow 18.347,
1193 /* Vertical Alignment */
1194 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_V)) {
1195 GnmVAlign align = gnm_style_get_align_v (style);
1196 char const *alignment = NULL;
1197 gboolean gnum_specs = FALSE;
1198 switch (align) {
1199 case GNM_VALIGN_TOP:
1200 alignment = "top";
1201 break;
1202 case GNM_VALIGN_BOTTOM:
1203 alignment= "bottom";
1204 break;
1205 case GNM_VALIGN_CENTER:
1206 alignment = "middle";
1207 break;
1208 case GNM_VALIGN_JUSTIFY:
1209 case GNM_VALIGN_DISTRIBUTED:
1210 default:
1211 alignment = "automatic";
1212 gnum_specs = TRUE;
1213 break;
1215 gsf_xml_out_add_cstr (state->xml, STYLE "vertical-align", alignment);
1216 if (gnum_specs && state->with_extension)
1217 gsf_xml_out_add_int (state->xml, GNMSTYLE "GnmVAlign", align);
1220 /* Wrapped Text */
1221 if (gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT))
1222 gsf_xml_out_add_cstr (state->xml, FOSTYLE "wrap-option",
1223 gnm_style_get_wrap_text (style) ? "wrap" : "no-wrap");
1225 /* Shrink-To-Fit */
1226 if (gnm_style_is_element_set (style, MSTYLE_SHRINK_TO_FIT))
1227 odf_add_bool (state->xml, STYLE "shrink-to-fit",
1228 gnm_style_get_shrink_to_fit (style));
1230 /* Text Direction */
1231 /* Note that fo:direction, style:writing-mode and style:writing-mode-automatic interact. */
1232 /* style:writing-mode-automatic is set in the paragraph properties below. */
1233 if (gnm_style_is_element_set (style, MSTYLE_TEXT_DIR)) {
1234 char const *writing_mode = NULL;
1235 char const *direction = NULL;
1236 switch (gnm_style_get_text_dir (style)) {
1237 case GNM_TEXT_DIR_RTL:
1238 writing_mode = "rl-tb";
1239 break;
1240 case GNM_TEXT_DIR_LTR:
1241 writing_mode = "lr-tb";
1242 direction = "ltr";
1243 break;
1244 case GNM_TEXT_DIR_CONTEXT:
1245 writing_mode = "page";
1246 /* Note that we will be setting style:writing-mode-automatic below */
1247 break;
1249 if (state->odf_version > 101)
1250 gsf_xml_out_add_cstr (state->xml, STYLE "writing-mode", writing_mode);
1251 if (direction != NULL)
1252 gsf_xml_out_add_cstr (state->xml, FOSTYLE "direction", direction);
1253 gsf_xml_out_add_cstr (state->xml, STYLE "glyph-orientation-vertical", "auto");
1256 /* Cell Protection */
1257 test1 = gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN);
1258 test2 = gnm_style_is_element_set (style, MSTYLE_CONTENTS_LOCKED);
1259 if (test1 || test2) {
1260 gboolean hidden = test1 && gnm_style_get_contents_hidden (style);
1261 gboolean protected = test2 && gnm_style_get_contents_locked (style);
1262 char const *label;
1264 if (hidden)
1265 label = protected ? "hidden-and-protected" : "formula-hidden";
1266 else
1267 label = protected ? "protected" : "none";
1268 gsf_xml_out_add_cstr (state->xml, STYLE "cell-protect", label);
1271 /* Rotation */
1272 if (gnm_style_is_element_set (style, MSTYLE_ROTATION)) {
1273 gsf_xml_out_add_cstr (state->xml, STYLE "rotation-align", "none");
1274 odf_add_angle (state->xml, STYLE "rotation-angle", gnm_style_get_rotation (style));
1277 /* Print Content */
1278 odf_add_bool (state->xml, STYLE "print-content", TRUE);
1280 /* Decimal Places (this is the maximum number of decimal places shown if not otherwise specified.) */
1281 /* Only interpreted in a default style. */
1282 gsf_xml_out_add_int (state->xml, STYLE "decimal-places", 13);
1284 /* Horizontal Alignment */
1285 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H)) {
1286 GnmHAlign align = gnm_style_get_align_h (style);
1287 char const *source = NULL;
1288 gboolean rep_content = FALSE;
1290 switch (align) {
1291 case GNM_HALIGN_DISTRIBUTED:
1292 case GNM_HALIGN_LEFT:
1293 case GNM_HALIGN_RIGHT:
1294 case GNM_HALIGN_CENTER:
1295 case GNM_HALIGN_JUSTIFY:
1296 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
1297 source = "fix";
1298 break;
1299 case GNM_HALIGN_FILL:
1300 rep_content = TRUE;
1301 case GNM_HALIGN_GENERAL:
1302 default:
1303 /* Note that since source is value-type, alignment should be ignored */
1304 /*(but isn't by OOo) */
1305 source = "value-type";
1306 break;
1308 gsf_xml_out_add_cstr (state->xml, STYLE "text-align-source", source);
1309 /* Repeat Content */
1310 odf_add_bool (state->xml, STYLE "repeat-content", rep_content);
1313 gsf_xml_out_end_element (state->xml); /* </style:table-cell-properties */
1316 static void
1317 odf_write_style_paragraph_properties (GnmOOExport *state, GnmStyle const *style)
1319 gsf_xml_out_start_element (state->xml, STYLE "paragraph-properties");
1320 /* Text Direction */
1321 /* Note that fo:direction, style:writing-mode and style:writing-mode-automatic interact. */
1322 /* fo:direction and style:writing-mode may have been set in the cell properties above. */
1323 if (gnm_style_is_element_set (style, MSTYLE_TEXT_DIR))
1324 odf_add_bool (state->xml, STYLE "writing-mode-automatic",
1325 (gnm_style_get_text_dir (style) == GNM_TEXT_DIR_CONTEXT));
1327 /* Horizontal Alignment */
1328 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H)) {
1329 GnmHAlign align = gnm_style_get_align_h (style);
1330 char const *alignment = NULL;
1331 gboolean gnum_specs = FALSE;
1332 switch (align) {
1333 case GNM_HALIGN_LEFT:
1334 alignment = "left";
1335 break;
1336 case GNM_HALIGN_RIGHT:
1337 alignment= "right";
1338 break;
1339 case GNM_HALIGN_CENTER:
1340 alignment = "center";
1341 break;
1342 case GNM_HALIGN_JUSTIFY:
1343 alignment = "justify";
1344 break;
1345 case GNM_HALIGN_DISTRIBUTED:
1346 alignment = "justify";
1347 gnum_specs = TRUE;
1348 break;
1349 case GNM_HALIGN_FILL:
1350 /* handled by repeat-content */
1351 break;
1352 case GNM_HALIGN_CENTER_ACROSS_SELECTION:
1353 alignment = "center";
1354 gnum_specs = TRUE;
1355 break;
1356 case GNM_HALIGN_GENERAL:
1357 default:
1358 /* Note that since source is value-type, alignment should be ignored */
1359 /*(but isn't by OOo) */
1360 alignment = "start";
1361 gnum_specs = TRUE;
1362 break;
1364 if (align != GNM_HALIGN_GENERAL && align != GNM_HALIGN_FILL)
1365 gsf_xml_out_add_cstr (state->xml, FOSTYLE "text-align", alignment);
1366 if (state->with_extension) {
1367 if (gnum_specs)
1368 gsf_xml_out_add_int (state->xml, GNMSTYLE "GnmHAlign", align);
1369 if (align == GNM_HALIGN_DISTRIBUTED)
1370 gsf_xml_out_add_cstr (state->xml, CSS "text-justify", "distribute");
1374 /* Text Indent */
1375 if (gnm_style_is_element_set (style, MSTYLE_INDENT))
1376 odf_add_pt (state->xml, FOSTYLE "margin-left", gnm_style_get_indent (style));
1378 gsf_xml_out_end_element (state->xml); /* </style:paragraph-properties */
1383 static void
1384 odf_write_style_text_properties (GnmOOExport *state, GnmStyle const *style)
1386 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1388 /* Hidden */
1389 if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN)) {
1390 char const *label = gnm_style_get_contents_hidden (style) ?
1391 "none":"true";
1392 gsf_xml_out_add_cstr (state->xml, TEXT "display", label);
1395 /* Font Weight */
1396 if (gnm_style_is_element_set (style, MSTYLE_FONT_BOLD))
1397 odf_add_font_weight (state,
1398 gnm_style_get_font_bold (style)
1399 ? PANGO_WEIGHT_BOLD
1400 : PANGO_WEIGHT_NORMAL);
1401 /* Font Style (Italic vs Roman) */
1402 if (gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC))
1403 gsf_xml_out_add_cstr (state->xml, FOSTYLE "font-style",
1404 gnm_style_get_font_italic (style)
1405 ? "italic" : "normal");
1406 /* Strikethrough */
1407 if (gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
1408 if (gnm_style_get_font_strike (style)) {
1409 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-type", "single");
1410 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-style", "solid");
1411 } else {
1412 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-type", "none");
1413 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-style", "none");
1416 /* Underline */
1417 if (gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE))
1418 switch (gnm_style_get_font_uline (style)) {
1419 case UNDERLINE_NONE:
1420 UNDERLINESPECS("none", "none", "auto", FALSE);
1421 break;
1422 case UNDERLINE_SINGLE:
1423 UNDERLINESPECS("single", "solid", "auto", FALSE);
1424 break;
1425 case UNDERLINE_DOUBLE:
1426 UNDERLINESPECS("double", "solid", "auto", FALSE);
1427 break;
1428 case UNDERLINE_SINGLE_LOW:
1429 UNDERLINESPECS("single", "dash", "auto", TRUE);
1430 break;
1431 case UNDERLINE_DOUBLE_LOW:
1432 UNDERLINESPECS("double", "dash", "auto", TRUE);
1433 break;
1435 /* Superscript/Subscript */
1436 if (gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT))
1437 switch (gnm_style_get_font_script (style)) {
1438 case GO_FONT_SCRIPT_SUB:
1439 gsf_xml_out_add_cstr (state->xml,
1440 STYLE "text-position", "sub 80%");
1441 break;
1442 case GO_FONT_SCRIPT_STANDARD:
1443 gsf_xml_out_add_cstr (state->xml,
1444 STYLE "text-position", "0% 100%");
1445 break;
1446 case GO_FONT_SCRIPT_SUPER:
1447 gsf_xml_out_add_cstr (state->xml,
1448 STYLE "text-position", "super 80%");
1449 break;
1451 /* Font Size */
1452 if (gnm_style_is_element_set (style, MSTYLE_FONT_SIZE))
1453 odf_add_pt (state->xml, FOSTYLE "font-size",
1454 gnm_style_get_font_size (style));
1455 /* Foreground Color */
1456 if (gnm_style_is_element_set (style, MSTYLE_FONT_COLOR))
1457 gnm_xml_out_add_hex_color (state->xml, FOSTYLE "color",
1458 gnm_style_get_font_color (style), 1);
1459 /* Font Family */
1460 if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME))
1461 gsf_xml_out_add_cstr (state->xml, FOSTYLE "font-family",
1462 gnm_style_get_font_name (style));
1464 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1468 static void
1469 odf_write_style_goformat_name (GnmOOExport *state, GOFormat const *gof)
1471 char const *name;
1473 if ((gof == NULL) || go_format_is_markup (gof))
1474 return;
1476 if (go_format_is_general (gof))
1477 name = "General";
1478 else
1479 name = xl_find_format (state, gof);
1481 gsf_xml_out_add_cstr (state->xml, STYLE "data-style-name", name);
1484 static const char*
1485 odf_find_style (GnmOOExport *state, GnmStyle const *style)
1487 char const *found = g_hash_table_lookup (state->named_cell_styles, style);
1489 if (found == NULL) {
1490 found = g_hash_table_lookup (state->cell_styles, style);
1493 if (found == NULL) {
1494 g_printerr ("Could not find style %p\n", style);
1495 return NULL;
1498 return found;
1501 static void
1502 odf_save_style_map_single_f (GnmOOExport *state, GString *str, GnmExprTop const *texpr, GnmParsePos *pp)
1504 char *formula;
1506 formula = gnm_expr_top_as_string (texpr, pp, state->conv);
1507 g_string_append (str, formula);
1508 g_free (formula);
1512 static void
1513 odf_save_style_map_double_f (GnmOOExport *state, GString *str, GnmStyleCond const *cond, GnmParsePos *pp)
1515 g_string_append_c (str, '(');
1516 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), pp);
1517 g_string_append_c (str, ',');
1518 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 1), pp);
1519 g_string_append_c (str, ')');
1522 static char *
1523 odf_strip_brackets (char *string)
1525 char *closing;
1526 closing = strrchr(string, ']');
1527 if (closing != NULL && *(closing+1) == '\0')
1528 *closing = '\0';
1529 return ((*string == '[') ? (string + 1) : string);
1532 static void
1533 odf_determine_base (GnmOOExport *state, GnmRange *r, GnmParsePos *pp)
1535 if (r)
1536 parse_pos_init (pp, (Workbook *) state->wb, state->sheet, r->start.col, r->start.row);
1537 else {
1538 g_warning ("Unable to determine an appropriate base cell address.");
1539 parse_pos_init (pp, (Workbook *) state->wb, state->sheet, 0, 0);
1543 static void
1544 odf_save_style_map (GnmOOExport *state, GnmStyleCond const *cond, GnmRange *r)
1546 char const *name = odf_find_style (state, cond->overlay);
1547 GString *str;
1548 gchar *address;
1549 GnmExprTop const *texpr = NULL;
1550 GnmCellRef ref;
1551 GnmParsePos pp;
1552 GnmStyleCondOp op = cond->op;
1554 g_return_if_fail (name != NULL);
1556 str = g_string_new (NULL);
1558 switch (op) {
1559 case GNM_STYLE_COND_CONTAINS_STR:
1560 case GNM_STYLE_COND_NOT_CONTAINS_STR:
1561 case GNM_STYLE_COND_BEGINS_WITH_STR:
1562 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR:
1563 case GNM_STYLE_COND_ENDS_WITH_STR:
1564 case GNM_STYLE_COND_NOT_ENDS_WITH_STR:
1565 case GNM_STYLE_COND_CONTAINS_ERR:
1566 case GNM_STYLE_COND_NOT_CONTAINS_ERR:
1567 case GNM_STYLE_COND_CONTAINS_BLANKS:
1568 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS:
1569 texpr = gnm_style_cond_get_alternate_expr (cond);
1570 op = GNM_STYLE_COND_CUSTOM;
1571 break;
1572 default:
1573 break;
1576 switch (op) {
1577 case GNM_STYLE_COND_BETWEEN:
1578 odf_determine_base (state, r, &pp);
1579 g_string_append (str, "of:cell-content-is-between");
1580 odf_save_style_map_double_f (state, str, cond, &pp);
1581 break;
1582 case GNM_STYLE_COND_NOT_BETWEEN:
1583 odf_determine_base (state, r, &pp);
1584 g_string_append (str, "of:cell-content-is-not-between");
1585 odf_save_style_map_double_f (state, str, cond, &pp);
1586 break;
1587 case GNM_STYLE_COND_EQUAL:
1588 odf_determine_base (state, r, &pp);
1589 g_string_append (str, "of:cell-content()=");
1590 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1591 break;
1592 case GNM_STYLE_COND_NOT_EQUAL:
1593 odf_determine_base (state, r, &pp);
1594 g_string_append (str, "of:cell-content()!=");
1595 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1596 break;
1597 case GNM_STYLE_COND_GT:
1598 odf_determine_base (state, r, &pp);
1599 g_string_append (str, "of:cell-content()>");
1600 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1601 break;
1602 case GNM_STYLE_COND_LT:
1603 odf_determine_base (state, r, &pp);
1604 g_string_append (str, "of:cell-content()<");
1605 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1606 break;
1607 case GNM_STYLE_COND_GTE:
1608 odf_determine_base (state, r, &pp);
1609 g_string_append (str, "of:cell-content()>=");
1610 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1611 break;
1612 case GNM_STYLE_COND_LTE:
1613 odf_determine_base (state, r, &pp);
1614 g_string_append (str, "of:cell-content()<=");
1615 odf_save_style_map_single_f (state, str, gnm_style_cond_get_expr (cond, 0), &pp);
1616 break;
1617 case GNM_STYLE_COND_CUSTOM:
1618 odf_determine_base (state, r, &pp);
1619 g_string_append (str, "of:is-true-formula(");
1620 odf_save_style_map_single_f (state, str, texpr ? texpr : gnm_style_cond_get_expr (cond, 0), &pp);
1621 g_string_append (str, ")");
1622 break;
1623 default:
1624 g_string_free (str, TRUE);
1625 g_warning ("Unknown style condition %d", op);
1626 return;
1629 if (texpr) {
1630 gnm_expr_top_unref (texpr);
1631 texpr = NULL;
1634 gsf_xml_out_start_element (state->xml, STYLE "map");
1636 gsf_xml_out_add_cstr (state->xml, STYLE "apply-style-name", name);
1637 gsf_xml_out_add_cstr (state->xml, STYLE "condition", str->str);
1639 /* ODF 1.2 requires a sheet name for the base-cell-address */
1640 /* This is really only needed if we include a formula */
1641 gnm_cellref_init (&ref, (Sheet *)state->sheet,
1642 pp.eval.col, pp.eval.row, FALSE);
1643 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
1644 parse_pos_init_sheet (&pp, state->sheet);
1645 address = gnm_expr_top_as_string (texpr, &pp, state->conv);
1646 gsf_xml_out_add_cstr (state->xml, STYLE "base-cell-address", odf_strip_brackets (address));
1647 g_free (address);
1648 gnm_expr_top_unref (texpr);
1650 gsf_xml_out_end_element (state->xml); /* </style:map> */
1652 g_string_free (str, TRUE);
1655 static void
1656 odf_write_style (GnmOOExport *state, GnmStyle const *style, GnmRange *r, gboolean is_default)
1658 GnmStyleConditions const *sc;
1659 GPtrArray const *conds;
1660 guint i;
1662 if ((!is_default) && gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
1663 GOFormat const *format = gnm_style_get_format(style);
1664 if (format != NULL)
1665 odf_write_style_goformat_name (state, format);
1668 odf_write_style_cell_properties (state, style);
1669 odf_write_style_paragraph_properties (state, style);
1670 odf_write_style_text_properties (state, style);
1672 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
1673 NULL != (sc = gnm_style_get_conditions (style)) &&
1674 NULL != (conds = gnm_style_conditions_details (sc))) {
1675 for (i = 0 ; i < conds->len ; i++) {
1676 GnmStyleCond const *cond = g_ptr_array_index (conds, i);
1677 odf_save_style_map (state, cond, r);
1681 /* MSTYLE_VALIDATION validations need to be written at a different place and time in ODF */
1682 /* MSTYLE_HLINK hyperlinks can not be attached to styles but need to be attached to the cell content */
1685 #undef UNDERLINESPECS
1686 #undef BORDERSTYLE
1688 static gint
1689 odf_compare_ci (gconstpointer a, gconstpointer b)
1691 col_row_styles_t const *old_style = a;
1692 ColRowInfo const *new_style = b;
1694 return !colrow_equal (new_style, old_style->ci);
1697 static void
1698 col_row_styles_free (gpointer data)
1700 col_row_styles_t *style = data;
1702 if (data) {
1703 g_free (style->name);
1704 g_free (style);
1708 static void
1709 odf_write_row_style (GnmOOExport *state, ColRowInfo const *ci)
1711 gsf_xml_out_start_element (state->xml, STYLE "table-row-properties");
1712 odf_add_pt (state->xml, STYLE "row-height", ci->size_pts);
1713 odf_add_bool (state->xml, STYLE "use-optimal-row-height",
1714 !ci->hard_size);
1715 gsf_xml_out_end_element (state->xml); /* </style:table-row-properties> */
1718 static const char*
1719 odf_find_row_style (GnmOOExport *state, ColRowInfo const *ci, gboolean write)
1721 col_row_styles_t *new_style;
1722 GSList *found = g_slist_find_custom (state->row_styles, ci, odf_compare_ci);
1724 if (found) {
1725 new_style = found->data;
1726 return new_style->name;
1727 } else {
1728 if (write) {
1729 new_style = g_new0 (col_row_styles_t,1);
1730 new_style->ci = ci;
1731 new_style->name = g_strdup_printf ("AROW-%i", g_slist_length (state->row_styles));
1732 state->row_styles = g_slist_prepend (state->row_styles, new_style);
1733 odf_start_style (state->xml, new_style->name, "table-row");
1734 if (ci != NULL)
1735 odf_write_row_style (state, ci);
1736 gsf_xml_out_end_element (state->xml); /* </style:style> */
1737 return new_style->name;
1738 } else {
1739 g_warning("We forgot to export a required row style!");
1740 return "Missing-Row-Style";
1745 static void
1746 odf_write_col_style (GnmOOExport *state, ColRowInfo const *ci)
1748 gsf_xml_out_start_element (state->xml, STYLE "table-column-properties");
1749 odf_add_pt (state->xml, STYLE "column-width", ci->size_pts);
1750 odf_add_bool (state->xml, STYLE "use-optimal-column-width",
1751 !ci->hard_size);
1752 gsf_xml_out_end_element (state->xml); /* </style:table-column-properties> */
1755 static const char*
1756 odf_find_col_style (GnmOOExport *state, ColRowInfo const *ci, gboolean write)
1758 col_row_styles_t *new_style;
1759 GSList *found = g_slist_find_custom (state->col_styles, ci, odf_compare_ci);
1761 if (found) {
1762 new_style = found->data;
1763 return new_style->name;
1764 } else {
1765 if (write) {
1766 new_style = g_new0 (col_row_styles_t,1);
1767 new_style->ci = ci;
1768 new_style->name = g_strdup_printf ("ACOL-%i", g_slist_length (state->col_styles));
1769 state->col_styles = g_slist_prepend (state->col_styles, new_style);
1770 odf_start_style (state->xml, new_style->name, "table-column");
1771 if (ci != NULL)
1772 odf_write_col_style (state, ci);
1773 gsf_xml_out_end_element (state->xml); /* </style:style> */
1774 return new_style->name;
1775 } else {
1776 g_warning("We forgot to export a required column style!");
1777 return "Missing-Column-Style";
1782 static void
1783 odf_save_this_style_with_name (GnmStyleRegion *sr, char const *name, GnmOOExport *state)
1785 odf_start_style (state->xml, name, "table-cell");
1786 odf_write_style (state, sr->style, &sr->range, FALSE);
1787 gsf_xml_out_end_element (state->xml); /* </style:style */
1790 static void
1791 odf_store_this_named_style (GnmStyle *style, char const *name, GnmRange *r, GnmOOExport *state)
1793 char *real_name;
1794 GnmStyleConditions const *sc;
1796 if (name == NULL) {
1797 int i = g_hash_table_size (state->named_cell_styles);
1798 /* All styles referenced by a style:map need to be named, so in that case */
1799 /* we make up a name, that ought to look nice */
1800 real_name = g_strdup_printf ("Gnumeric-%i", i);
1801 } else
1802 real_name = g_strdup (name);
1804 g_hash_table_insert (state->named_cell_styles, style, real_name);
1805 g_hash_table_insert (state->named_cell_style_regions, gnm_style_region_new (r, style),
1806 g_strdup (real_name));
1808 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
1809 NULL != (sc = gnm_style_get_conditions (style))) {
1810 GPtrArray const *conds = gnm_style_conditions_details (sc);
1811 if (conds != NULL) {
1812 guint i;
1813 for (i = 0 ; i < conds->len ; i++) {
1814 GnmStyleCond const *cond =
1815 g_ptr_array_index (conds, i);
1816 odf_store_this_named_style (cond->overlay, NULL, r, state);
1822 static void
1823 odf_save_this_style (G_GNUC_UNUSED gconstpointer dummy, GnmStyleRegion *sr, GnmOOExport *state)
1825 char *name;
1826 GnmStyleConditions const *sc;
1828 if (NULL != g_hash_table_lookup (state->cell_styles, sr->style))
1829 return;
1831 name = oo_item_name (state, OO_ITEM_MSTYLE, sr->style);
1832 g_hash_table_insert (state->cell_styles, sr->style, name);
1834 if (gnm_style_is_element_set (sr->style, MSTYLE_CONDITIONS) &&
1835 NULL != (sc = gnm_style_get_conditions (sr->style))) {
1836 GPtrArray const *conds = gnm_style_conditions_details (sc);
1837 if (conds != NULL) {
1838 guint i;
1839 for (i = 0 ; i < conds->len ; i++) {
1840 GnmStyleCond const *cond =
1841 g_ptr_array_index (conds, i);
1842 odf_store_this_named_style (cond->overlay, NULL, &sr->range, state);
1847 odf_save_this_style_with_name (sr, name, state);
1850 static void
1851 odf_write_text_colours (char const *name, G_GNUC_UNUSED gpointer data, GnmOOExport *state)
1853 char const *colour = data;
1854 char *display = g_strdup_printf ("Font Color %s", colour);
1855 odf_start_style (state->xml, name, "text");
1856 gsf_xml_out_add_cstr (state->xml, STYLE "display-name", display);
1857 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1858 gsf_xml_out_add_cstr (state->xml, FOSTYLE "color", colour);
1859 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1860 gsf_xml_out_end_element (state->xml); /* </style:style> */
1861 g_free (display);
1864 static void
1865 odf_write_font_sizes (gpointer key, gpointer value, gpointer user_data)
1867 GnmOOExport *state = user_data;
1868 gint i = GPOINTER_TO_INT (value);
1869 char * str = key;
1870 char *display = g_strdup_printf ("Font Size %ipt", i);
1871 odf_start_style (state->xml, str, "text");
1872 gsf_xml_out_add_cstr (state->xml, STYLE "display-name", display);
1873 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1874 odf_add_pt (state->xml, FOSTYLE "font-size", (double) i);
1875 odf_add_pt (state->xml, STYLE "font-size-asian", (double) i);
1876 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1877 gsf_xml_out_end_element (state->xml); /* </style:style> */
1878 g_free (display);
1881 static void
1882 odf_write_character_styles (GnmOOExport *state)
1884 int i;
1886 for (i = 100; i <= 1000; i+=100) {
1887 char * str = g_strdup_printf ("AC-weight%i", i);
1888 odf_start_style (state->xml, str, "text");
1889 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1890 odf_add_font_weight (state, i);
1891 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1892 gsf_xml_out_end_element (state->xml); /* </style:style> */
1893 g_free (str);
1896 odf_start_style (state->xml, "AC-italic", "text");
1897 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1898 gsf_xml_out_add_cstr (state->xml, FOSTYLE "font-style", "italic");
1899 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1900 gsf_xml_out_end_element (state->xml); /* </style:style> */
1902 odf_start_style (state->xml, "AC-roman", "text");
1903 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1904 gsf_xml_out_add_cstr (state->xml, FOSTYLE "font-style", "normal");
1905 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1906 gsf_xml_out_end_element (state->xml); /* </style:style> */
1908 odf_start_style (state->xml, "AC-subscript", "text");
1909 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1910 gsf_xml_out_add_cstr (state->xml, STYLE "text-position", "sub 83%");
1911 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1912 gsf_xml_out_end_element (state->xml); /* </style:style> */
1914 odf_start_style (state->xml, "AC-superscript", "text");
1915 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1916 gsf_xml_out_add_cstr (state->xml, STYLE "text-position", "super 83%");
1917 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1918 gsf_xml_out_end_element (state->xml); /* </style:style> */
1920 odf_start_style (state->xml, "AC-script", "text");
1921 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1922 gsf_xml_out_add_cstr (state->xml, STYLE "text-position", "0% 100%");
1923 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1924 gsf_xml_out_end_element (state->xml); /* </style:style> */
1926 odf_start_style (state->xml, "AC-strikethrough-solid", "text");
1927 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1928 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-type", "single");
1929 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-style", "solid");
1930 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1931 gsf_xml_out_end_element (state->xml); /* </style:style> */
1933 odf_start_style (state->xml, "AC-strikethrough-none", "text");
1934 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1935 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-type", "none");
1936 gsf_xml_out_add_cstr (state->xml, STYLE "text-line-through-style", "none");
1937 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1938 gsf_xml_out_end_element (state->xml); /* </style:style> */
1940 odf_start_style (state->xml, "AC-underline-none", "text");
1941 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1942 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-type", "none");
1943 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-style", "none");
1944 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-width", "auto");
1945 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1946 gsf_xml_out_end_element (state->xml); /* </style:style> */
1948 odf_start_style (state->xml, "AC-underline-single", "text");
1949 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1950 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-type", "single");
1951 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-style", "solid");
1952 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-width", "auto");
1953 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1954 gsf_xml_out_end_element (state->xml); /* </style:style> */
1956 odf_start_style (state->xml, "AC-underline-double", "text");
1957 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1958 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-type", "double");
1959 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-style", "solid");
1960 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-width", "auto");
1961 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1962 gsf_xml_out_end_element (state->xml); /* </style:style> */
1964 odf_start_style (state->xml, "AC-underline-low", "text");
1965 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1966 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-type", "single");
1967 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-style", "solid");
1968 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-width", "bold");
1969 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1970 gsf_xml_out_end_element (state->xml); /* </style:style> */
1972 odf_start_style (state->xml, "AC-underline-error", "text");
1973 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
1974 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-type", "single");
1975 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-style", "wave");
1976 gsf_xml_out_add_cstr (state->xml, STYLE "text-underline-width", "auto");
1977 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
1978 gsf_xml_out_end_element (state->xml); /* </style:style> */
1980 if (state->row_default != NULL)
1981 odf_find_row_style (state, state->row_default, TRUE);
1982 if (state->column_default != NULL)
1983 odf_find_col_style (state, state->column_default, TRUE);
1987 static void
1988 odf_write_cell_styles (GnmOOExport *state)
1990 int i;
1991 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
1992 state->sheet = workbook_sheet_by_index (state->wb, i);
1993 sheet_style_range_foreach (state->sheet, NULL,
1994 (GHFunc) odf_save_this_style,
1995 state);
1997 state->sheet = NULL;
2000 static void
2001 odf_write_column_styles (GnmOOExport *state)
2003 /* We have to figure out which automatic styles we need */
2004 /* This is really annoying since we have to scan through */
2005 /* all columnss. If we could store these styless in styles.xml */
2006 /* we could create them as we need them, but these styles */
2007 /* have to go into the beginning of content.xml. */
2008 int j;
2010 for (j = 0; j < workbook_sheet_count (state->wb); j++) {
2011 Sheet const *sheet = workbook_sheet_by_index (state->wb, j);
2012 int max_cols = gnm_sheet_get_max_cols (sheet);
2013 int i;
2014 ColRowInfo const *last_ci;
2016 odf_find_col_style (state, &sheet->cols.default_style, TRUE);
2018 last_ci = sheet_col_get (sheet, 0);
2019 odf_find_col_style (state, last_ci , TRUE);
2021 for (i = 1; i < max_cols; i++) {
2022 ColRowInfo const *this_ci = sheet_col_get (sheet, i);
2023 if (!colrow_equal (last_ci, this_ci))
2024 odf_find_col_style (state, (last_ci = this_ci), TRUE);
2028 return;
2031 static void
2032 odf_write_row_styles (GnmOOExport *state)
2034 /* We have to figure out which automatic styles we need */
2035 /* This is really annoying since we have to scan through */
2036 /* all rows. If we could store these styless in styles.xml */
2037 /* we could create them as we need them, but these styles */
2038 /* have to go into the beginning of content.xml. */
2039 int j;
2041 for (j = 0; j < workbook_sheet_count (state->wb); j++) {
2042 Sheet const *sheet = workbook_sheet_by_index (state->wb, j);
2043 int max_rows = gnm_sheet_get_max_rows (sheet);
2044 int i;
2045 ColRowInfo const *last_ci;
2047 odf_find_row_style (state, &sheet->rows.default_style, TRUE);
2049 last_ci = sheet_row_get (sheet, 0);
2050 odf_find_row_style (state, last_ci , TRUE);
2052 for (i = 1; i < max_rows; i++) {
2053 ColRowInfo const *this_ci = sheet_row_get (sheet, i);
2054 if (!colrow_equal (last_ci, this_ci))
2055 odf_find_row_style (state, (last_ci = this_ci), TRUE);
2059 return;
2062 static void
2063 odf_print_string (GnmConventionsOut *out, char const *str, char quote)
2065 GString *target = out->accum;
2067 /* Strings are surrounded by quote characters; a literal quote character '"'*/
2068 /* as string content is escaped by duplicating it. */
2070 g_string_append_c (target, quote);
2071 /* This loop should be UTF-8 safe. */
2072 for (; *str; str++) {
2073 g_string_append_c (target, *str);
2074 if (*str == quote)
2075 g_string_append_c (target, quote);
2077 g_string_append_c (target, quote);
2081 static void
2082 odf_cellref_as_string_base (GnmConventionsOut *out,
2083 GnmCellRef const *cell_ref,
2084 gboolean no_sheetname)
2086 GString *target = out->accum;
2087 GnmCellPos pos;
2088 Sheet const *sheet = cell_ref->sheet;
2089 Sheet const *size_sheet = eval_sheet (sheet, out->pp->sheet);
2090 GnmSheetSize const *ss =
2091 gnm_sheet_get_size2 (size_sheet, out->pp->wb);
2093 if (sheet != NULL && !no_sheetname) {
2094 if (NULL != out->pp->wb && sheet->workbook != out->pp->wb) {
2095 char const *ext_ref;
2096 ext_ref = go_doc_get_uri ((GODoc *)(sheet->workbook));
2097 odf_print_string (out, ext_ref, '\'');
2098 g_string_append_c (target, '#');
2100 g_string_append_c (target, '$');
2101 odf_print_string (out, sheet->name_unquoted, '\'');
2103 g_string_append_c (target, '.');
2105 gnm_cellpos_init_cellref_ss (&pos, cell_ref, &out->pp->eval, ss);
2107 if (!cell_ref->col_relative)
2108 g_string_append_c (target, '$');
2109 g_string_append (target, col_name (pos.col));
2111 if (!cell_ref->row_relative)
2112 g_string_append_c (target, '$');
2113 g_string_append (target, row_name (pos.row));
2117 static void
2118 odf_cellref_as_string (GnmConventionsOut *out,
2119 GnmCellRef const *cell_ref,
2120 gboolean no_sheetname)
2122 g_string_append (out->accum, "[");
2123 odf_cellref_as_string_base (out, cell_ref, no_sheetname);
2124 g_string_append (out->accum, "]");
2127 static void
2128 odf_rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
2130 g_string_append (out->accum, "[");
2131 odf_cellref_as_string_base (out, &(ref->a), FALSE);
2132 g_string_append_c (out->accum, ':');
2133 odf_cellref_as_string_base (out, &(ref->b), ref->b.sheet == ref->a.sheet);
2134 g_string_append (out->accum, "]");
2137 static gboolean
2138 odf_func_r_dchisq_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2140 if (func->argc == 2) {
2141 GString *target = out->accum;
2142 GnmExprConstPtr const *ptr = func->argv;
2143 g_string_append (target, "CHISQDIST(");
2144 gnm_expr_as_gstring (ptr[0], out);
2145 g_string_append_c (out->accum, ';');
2146 gnm_expr_as_gstring (ptr[1], out);
2147 g_string_append (out->accum, ";FALSE())");
2148 return TRUE;
2150 return FALSE;
2153 static gboolean
2154 odf_func_r_pchisq_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2156 if (func->argc == 2) {
2157 GString *target = out->accum;
2158 g_string_append (target, "CHISQDIST");
2159 gnm_expr_list_as_string (func->argc, func->argv, out);
2160 return TRUE;
2162 return FALSE;
2165 static gboolean
2166 odf_func_r_qchisq_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2168 if (func->argc == 2) {
2169 GString *target = out->accum;
2170 g_string_append (target, "CHISQINV");
2171 gnm_expr_list_as_string (func->argc, func->argv, out);
2172 return TRUE;
2174 return FALSE;
2177 static gboolean
2178 odf_func_floor_ceiling_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2180 GString *target = out->accum;
2181 GnmExprConstPtr const *ptr = func->argv;
2182 g_string_append (target, func->func->name);
2183 g_string_append_c (target, '(');
2184 if (func->argc > 0) {
2185 gnm_expr_as_gstring (ptr[0], out);
2186 g_string_append_c (target, ';');
2187 if (func->argc > 1)
2188 gnm_expr_as_gstring (ptr[1], out);
2189 else {
2190 g_string_append (target, "SIGN(");
2191 gnm_expr_as_gstring (ptr[0], out);
2192 g_string_append_c (target, ')');
2194 g_string_append (target, ";1)");
2195 } else {
2196 g_string_append (target, func->func->name);
2197 g_string_append (target, "()");
2199 return TRUE;
2202 static gboolean
2203 odf_func_eastersunday_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2205 if (func->argc == 1) {
2206 GString *target = out->accum;
2207 GnmExprConstPtr const *ptr = func->argv;
2208 /* OOo incorrectly stores this without an ORG.OPENOFFICE. prefix. */
2209 g_string_append (target, "EASTERSUNDAY(");
2210 gnm_expr_as_gstring (ptr[0], out);
2211 g_string_append (out->accum, ")");
2212 return TRUE;
2214 return FALSE;
2217 static void
2218 odf_expr_func_handler (GnmConventionsOut *out, GnmExprFunction const *func)
2220 static struct {
2221 char const *gnm_name;
2222 gpointer handler;
2223 } const sc_func_handlers[] = {
2224 {"CEILING", odf_func_floor_ceiling_handler},
2225 {"FLOOR", odf_func_floor_ceiling_handler},
2226 {"R.QCHISQ", odf_func_r_qchisq_handler},
2227 {"R.DCHISQ", odf_func_r_dchisq_handler},
2228 {"R.PCHISQ", odf_func_r_pchisq_handler},
2229 {"EASTERSUNDAY", odf_func_eastersunday_handler},
2230 {NULL, NULL}
2233 static struct {
2234 char const *gnm_name;
2235 char const *odf_name;
2236 } const sc_func_renames[] = {
2238 /* The default behaviour is to precede each function name with */
2239 /* ORG.GNUMERIC. So we need not list gnumeric unique names or those that */
2240 /* come from unknown plugins */
2242 /* The following are functions that exist in OpenFormula or with another */
2243 /* known prefix. This listing is */
2244 /* alphabetical by the second entry, the OpenFormula or foreign name (w/o prefix). */
2246 { "ABS","ABS" },
2247 { "ACCRINT","ACCRINT" },
2248 { "ACCRINTM","ACCRINTM" },
2249 { "ACOS","ACOS" },
2250 { "ACOSH","ACOSH" },
2251 { "ACOT","ACOT" },
2252 { "ACOTH","ACOTH" },
2253 { "ADDRESS","ADDRESS" },
2254 { "AMORDEGRC","AMORDEGRC" },
2255 { "AMORLINC","AMORLINC" },
2256 { "AND","AND" },
2257 { "ARABIC","ARABIC" },
2258 { "AREAS","AREAS" },
2259 { "ASC","ASC" },
2260 { "ASIN","ASIN" },
2261 { "ASINH","ASINH" },
2262 { "ATAN","ATAN" },
2263 { "ATAN2","ATAN2" },
2264 { "ATANH","ATANH" },
2265 { "AVEDEV","AVEDEV" },
2266 { "AVERAGE","AVERAGE" },
2267 { "AVERAGEA","AVERAGEA" },
2268 { "AVERAGEIF","AVERAGEIF" },
2269 { "AVERAGEIFS","AVERAGEIFS" },
2270 { "BINOM.DIST.RANGE","B" },
2271 { "BASE","BASE" },
2272 { "BESSELI","BESSELI" },
2273 { "BESSELJ","BESSELJ" },
2274 { "BESSELK","BESSELK" },
2275 { "BESSELY","BESSELY" },
2276 { "BETADIST","BETADIST" },
2277 { "BETAINV","BETAINV" },
2278 { "BIN2DEC","BIN2DEC" },
2279 { "BIN2HEX","BIN2HEX" },
2280 { "BIN2OCT","BIN2OCT" },
2281 { "BINOMDIST","BINOMDIST" },
2282 { "BITAND","BITAND" },
2283 { "BITLSHIFT","BITLSHIFT" },
2284 { "BITOR","BITOR" },
2285 { "BITRSHIFT","BITRSHIFT" },
2286 { "BITXOR","BITXOR" },
2287 { "CEIL", "CEILING" },
2288 /* { "ODF.CEILING","CEILING" }, see the handler code for CEILING*/
2289 { "CELL","CELL" },
2290 { "CHAR","CHAR" },
2291 /* { "ODF.CHISQDIST","CHISQDIST" }, we have related r.*chisq functions */
2292 /* { "ODF.CHISQINV","CHISQINV" }, we have related r.*chisq functions */
2293 { "CHOOSE","CHOOSE" },
2294 { "CLEAN","CLEAN" },
2295 { "CODE","CODE" },
2296 { "COLUMN","COLUMN" },
2297 { "COLUMNS","COLUMNS" },
2298 { "CONFIDENCE.T","COM.MICROSOFT.CONFIDENCE.T" },
2299 { "COMBIN","COMBIN" },
2300 { "COMBINA","COMBINA" },
2301 { "COMPLEX","COMPLEX" },
2302 { "CONCAT","COM.MICROSOFT.CONCAT" },
2303 { "CONCATENATE","COM.MICROSOFT.CONCAT" },
2304 { "CONFIDENCE","CONFIDENCE" },
2305 { "CONVERT","CONVERT" },
2306 { "CORREL","CORREL" },
2307 { "COS","COS" },
2308 { "COSH","COSH" },
2309 { "COT","COT" },
2310 { "COTH","COTH" },
2311 { "COUNT","COUNT" },
2312 { "COUNTA","COUNTA" },
2313 { "COUNTBLANK","COUNTBLANK" },
2314 { "COUNTIF","COUNTIF" },
2315 { "COUNTIFS","COUNTIFS" },
2316 { "COUPDAYBS","COUPDAYBS" },
2317 { "COUPDAYS","COUPDAYS" },
2318 { "COUPDAYSNC","COUPDAYSNC" },
2319 { "COUPNCD","COUPNCD" },
2320 { "COUPNUM","COUPNUM" },
2321 { "COUPPCD","COUPPCD" },
2322 { "COVAR","COVAR" },
2323 { "CRITBINOM","CRITBINOM" },
2324 { "CSC","CSC" },
2325 { "CSCH","CSCH" },
2326 { "CUMIPMT","CUMIPMT" },
2327 { "CUMPRINC","CUMPRINC" },
2328 { "DATE","DATE" },
2329 { "DATEDIF","DATEDIF" },
2330 { "DATEVALUE","DATEVALUE" },
2331 { "DAVERAGE","DAVERAGE" },
2332 { "DAY","DAY" },
2333 { "DAYS","DAYS" },
2334 { "DAYS360","DAYS360" },
2335 { "DB","DB" },
2336 { "DCOUNT","DCOUNT" },
2337 { "DCOUNTA","DCOUNTA" },
2338 { "DDB","DDB" },
2339 /* { "DDE","DDE" }, not implemented */
2340 { "DEC2BIN","DEC2BIN" },
2341 { "DEC2HEX","DEC2HEX" },
2342 { "DEC2OCT","DEC2OCT" },
2343 { "DECIMAL","DECIMAL" },
2344 { "DEGREES","DEGREES" },
2345 { "DELTA","DELTA" },
2346 { "DEVSQ","DEVSQ" },
2347 { "DGET","DGET" },
2348 { "DISC","DISC" },
2349 { "DMAX","DMAX" },
2350 { "DMIN","DMIN" },
2351 { "DOLLAR","DOLLAR" },
2352 { "DOLLARDE","DOLLARDE" },
2353 { "DOLLARFR","DOLLARFR" },
2354 { "DPRODUCT","DPRODUCT" },
2355 { "DSTDEV","DSTDEV" },
2356 { "DSTDEVP","DSTDEVP" },
2357 { "DSUM","DSUM" },
2358 { "DURATION","DURATION" },
2359 { "DVAR","DVAR" },
2360 { "DVARP","DVARP" },
2361 { "EDATE","EDATE" },
2362 { "EFFECT","EFFECT" },
2363 { "EOMONTH","EOMONTH" },
2364 { "ERF","ERF" },
2365 { "ERFC","ERFC" },
2366 { "ERROR.TYPE","ERROR.TYPE" },
2367 { "EUROCONVERT","EUROCONVERT" },
2368 { "EVEN","EVEN" },
2369 { "EXACT","EXACT" },
2370 { "EXP","EXP" },
2371 { "EXPONDIST","EXPONDIST" },
2372 { "FACT","FACT" },
2373 { "FACTDOUBLE","FACTDOUBLE" },
2374 { "FALSE","FALSE" },
2375 { "FIND","FIND" },
2376 { "FINDB","FINDB" },
2377 { "FISHER","FISHER" },
2378 { "FISHERINV","FISHERINV" },
2379 { "FIXED","FIXED" },
2380 { "FLOOR","FLOOR" },
2381 { "FORECAST","FORECAST" },
2382 { "GET.FORMULA","FORMULA" },
2383 { "FREQUENCY","FREQUENCY" },
2384 { "FTEST","FTEST" },
2385 { "FV","FV" },
2386 { "FVSCHEDULE","FVSCHEDULE" },
2387 { "GAMMA","GAMMA" },
2388 { "GAMMADIST","GAMMADIST" },
2389 { "GAMMAINV","GAMMAINV" },
2390 { "GAMMALN","GAMMALN" },
2391 /* { "GAUSS","GAUSS" }, converted to ERF on import */
2392 { "GCD","GCD" },
2393 { "GEOMEAN","GEOMEAN" },
2394 { "GESTEP","GESTEP" },
2395 { "GETPIVOTDATA","GETPIVOTDATA" },
2396 { "GROWTH","GROWTH" },
2397 { "HARMEAN","HARMEAN" },
2398 { "HEX2BIN","HEX2BIN" },
2399 { "HEX2DEC","HEX2DEC" },
2400 { "HEX2OCT","HEX2OCT" },
2401 { "HLOOKUP","HLOOKUP" },
2402 { "HOUR","HOUR" },
2403 { "HYPERLINK","HYPERLINK" },
2404 { "HYPGEOMDIST","HYPGEOMDIST" },
2405 { "IF","IF" },
2406 { "IFERROR","IFERROR" },
2407 { "IFNA","IFNA" },
2408 { "IFS","COM.MICROSOFT.IFS" },
2409 { "IMABS","IMABS" },
2410 { "IMAGINARY","IMAGINARY" },
2411 { "IMARGUMENT","IMARGUMENT" },
2412 { "IMCONJUGATE","IMCONJUGATE" },
2413 { "IMCOS","IMCOS" },
2414 { "IMCOT","IMCOT" },
2415 { "IMCSC","IMCSC" },
2416 { "IMCSCH","IMCSCH" },
2417 { "IMDIV","IMDIV" },
2418 { "IMEXP","IMEXP" },
2419 { "IMLN","IMLN" },
2420 { "IMLOG10","IMLOG10" },
2421 { "IMLOG2","IMLOG2" },
2422 { "IMPOWER","IMPOWER" },
2423 { "IMPRODUCT","IMPRODUCT" },
2424 { "IMREAL","IMREAL" },
2425 { "IMSEC","IMSEC" },
2426 { "IMSECH","IMSECH" },
2427 { "IMSIN","IMSIN" },
2428 { "IMSQRT","IMSQRT" },
2429 { "IMSUB","IMSUB" },
2430 { "IMSUM","IMSUM" },
2431 { "IMTAN","IMTAN" },
2432 { "INDEX","INDEX" },
2433 { "INDIRECT","INDIRECT" },
2434 { "INFO","INFO" },
2435 { "INT","INT" },
2436 { "INTERCEPT","INTERCEPT" },
2437 { "INTRATE","INTRATE" },
2438 { "IPMT","IPMT" },
2439 { "IRR","IRR" },
2440 { "ISBLANK","ISBLANK" },
2441 { "ISERR","ISERR" },
2442 { "ISERROR","ISERROR" },
2443 { "ISEVEN","ISEVEN" },
2444 { "ISFORMULA","ISFORMULA" },
2445 { "ISLOGICAL","ISLOGICAL" },
2446 { "ISNA","ISNA" },
2447 { "ISNONTEXT","ISNONTEXT" },
2448 { "ISNUMBER","ISNUMBER" },
2449 { "ISODD","ISODD" },
2450 { "ISOWEEKNUM","ISOWEEKNUM" },
2451 { "ISPMT","ISPMT" },
2452 { "ISREF","ISREF" },
2453 { "ISTEXT","ISTEXT" },
2454 { "JIS","JIS" },
2455 { "KURT","KURT" },
2456 { "LARGE","LARGE" },
2457 { "LCM","LCM" },
2458 { "LEFT","LEFT" },
2459 { "LEFTB","LEFTB" },
2460 { "CHIDIST","LEGACY.CHIDIST" },
2461 { "CHIINV","LEGACY.CHIINV" },
2462 { "CHITEST","LEGACY.CHITEST" },
2463 { "FDIST","LEGACY.FDIST" },
2464 { "FINV","LEGACY.FINV" },
2465 { "NORMSDIST","LEGACY.NORMSDIST" },
2466 { "NORMSINV","LEGACY.NORMSINV" },
2467 { "TDIST","LEGACY.TDIST" },
2468 { "LEN","LEN" },
2469 { "LENB","LENB" },
2470 { "LINEST","LINEST" },
2471 { "LN","LN" },
2472 { "LOG","LOG" },
2473 { "LOG10","LOG10" },
2474 { "LOGEST","LOGEST" },
2475 { "LOGINV","LOGINV" },
2476 { "LOGNORMDIST","LOGNORMDIST" },
2477 { "LOOKUP","LOOKUP" },
2478 { "LOWER","LOWER" },
2479 { "MATCH","MATCH" },
2480 { "MAX","MAX" },
2481 { "MAXA","MAXA" },
2482 { "MAXIFS","COM.MICROSOFT.MAXIFS" },
2483 { "MDETERM","MDETERM" },
2484 { "MDURATION","MDURATION" },
2485 { "MEDIAN","MEDIAN" },
2486 { "MID","MID" },
2487 { "MIDB","MIDB" },
2488 { "MIN","MIN" },
2489 { "MINA","MINA" },
2490 { "MINIFS","COM.MICROSOFT.MINIFS" },
2491 { "MINUTE","MINUTE" },
2492 { "MINVERSE","MINVERSE" },
2493 { "MIRR","MIRR" },
2494 { "MMULT","MMULT" },
2495 { "MOD","MOD" },
2496 { "MODE","MODE" },
2497 { "MODE.MULT","COM.MICROSOFT.MODE.MULT" },
2498 { "MONTH","MONTH" },
2499 { "MROUND","MROUND" },
2500 { "MULTINOMIAL","MULTINOMIAL" },
2501 /* { "MULTIPLE.OPERATIONS","MULTIPLE.OPERATIONS" }, not implemented */
2502 { "MUNIT","MUNIT" },
2503 { "N","N" },
2504 { "NA","NA" },
2505 { "NEGBINOMDIST","NEGBINOMDIST" },
2506 { "NETWORKDAYS","NETWORKDAYS" },
2507 { "NOMINAL","NOMINAL" },
2508 { "NORMDIST","NORMDIST" },
2509 { "NORMINV","NORMINV" },
2510 { "NOT","NOT" },
2511 { "NOW","NOW" },
2512 { "NPER","NPER" },
2513 { "NPV","NPV" },
2514 { "NUMBERVALUE","NUMBERVALUE" },
2515 { "OCT2BIN","OCT2BIN" },
2516 { "OCT2DEC","OCT2DEC" },
2517 { "OCT2HEX","OCT2HEX" },
2518 { "ODD","ODD" },
2519 { "ODDFPRICE","ODDFPRICE" },
2520 { "ODDFYIELD","ODDFYIELD" },
2521 { "ODDLPRICE","ODDLPRICE" },
2522 { "ODDLYIELD","ODDLYIELD" },
2523 { "OFFSET","OFFSET" },
2524 { "OR","OR" },
2525 { "G_DURATION","PDURATION" },
2526 { "PEARSON","PEARSON" },
2527 { "PERCENTILE","PERCENTILE" },
2528 { "PERCENTILE.EXC","COM.MICROSOFT.PERCENTILE.EXC" },
2529 { "PERCENTRANK","PERCENTRANK" },
2530 { "PERCENTRANK.EXC","COM.MICROSOFT.PERCENTRANK.EXC" },
2531 { "PERMUT","PERMUT" },
2532 { "PERMUTATIONA","PERMUTATIONA" },
2533 /* { "PHI","PHI" }, converted to NORMDIST on import */
2534 { "PI","PI" },
2535 { "PMT","PMT" },
2536 { "POISSON","POISSON" },
2537 { "POWER","POWER" },
2538 { "PPMT","PPMT" },
2539 { "PRICE","PRICE" },
2540 { "PRICEDISC","PRICEDISC" },
2541 { "PRICEMAT","PRICEMAT" },
2542 { "PROB","PROB" },
2543 { "PRODUCT","PRODUCT" },
2544 { "PROPER","PROPER" },
2545 { "PV","PV" },
2546 { "QUARTILE","QUARTILE" },
2547 { "QUARTILE.EXC","COM.MICROSOFT.QUARTILE.EXC" },
2548 { "QUOTIENT","QUOTIENT" },
2549 { "RADIANS","RADIANS" },
2550 { "RAND","RAND" },
2551 { "RANDBETWEEN","RANDBETWEEN" },
2552 { "RANK","RANK" },
2553 { "RANK.AVG","COM.MICROSOFT.RANK.AVG" },
2554 { "RATE","RATE" },
2555 { "RECEIVED","RECEIVED" },
2556 { "REPLACE","REPLACE" },
2557 { "REPLACEB","REPLACEB" },
2558 { "REPT","REPT" },
2559 { "RIGHT","RIGHT" },
2560 { "RIGHTB","RIGHTB" },
2561 { "ROMAN","ROMAN" },
2562 { "ROUND","ROUND" },
2563 { "ROUNDDOWN","ROUNDDOWN" },
2564 { "ROUNDUP","ROUNDUP" },
2565 { "ROW","ROW" },
2566 { "ROWS","ROWS" },
2567 { "RRI","RRI" },
2568 { "RSQ","RSQ" },
2569 { "SEARCH","SEARCH" },
2570 { "SEARCHB","SEARCHB" },
2571 { "SEC","SEC" },
2572 { "SECH","SECH" },
2573 { "SECOND","SECOND" },
2574 { "SERIESSUM","SERIESSUM" },
2575 { "SHEET","SHEET" },
2576 { "SHEETS","SHEETS" },
2577 { "SIGN","SIGN" },
2578 { "SIN","SIN" },
2579 { "SINH","SINH" },
2580 { "SKEW","SKEW" },
2581 { "SKEWP","SKEWP" },
2582 { "SLN","SLN" },
2583 { "SLOPE","SLOPE" },
2584 { "SMALL","SMALL" },
2585 { "SQRT","SQRT" },
2586 { "SQRTPI","SQRTPI" },
2587 { "STANDARDIZE","STANDARDIZE" },
2588 { "STDEV","STDEV" },
2589 { "STDEVA","STDEVA" },
2590 { "STDEVP","STDEVP" },
2591 { "STDEVPA","STDEVPA" },
2592 { "STEYX","STEYX" },
2593 { "SUBSTITUTE","SUBSTITUTE" },
2594 { "SUBTOTAL","SUBTOTAL" },
2595 { "SUM","SUM" },
2596 { "SUMIF","SUMIF" },
2597 { "SUMIFS","SUMIFS" },
2598 { "ODF.SUMPRODUCT","SUMPRODUCT" },
2599 { "SUMSQ","SUMSQ" },
2600 { "SUMX2MY2","SUMX2MY2" },
2601 { "SUMX2PY2","SUMX2PY2" },
2602 { "SUMXMY2","SUMXMY2" },
2603 { "SWITCH", "COM.MICROSOFT.SWITCH" },
2604 { "SYD","SYD" },
2605 { "T","T" },
2606 { "TAN","TAN" },
2607 { "TANH","TANH" },
2608 { "TBILLEQ","TBILLEQ" },
2609 { "TBILLPRICE","TBILLPRICE" },
2610 { "TBILLYIELD","TBILLYIELD" },
2611 { "TEXT","TEXT" },
2612 { "TEXTJOIN","COM.MICROSOFT.TEXTJOIN" },
2613 { "ODF.TIME","TIME" },
2614 { "TIMEVALUE","TIMEVALUE" },
2615 { "TINV","TINV" },
2616 { "TODAY","TODAY" },
2617 { "TRANSPOSE","TRANSPOSE" },
2618 { "TREND","TREND" },
2619 { "TRIM","TRIM" },
2620 { "TRIMMEAN","TRIMMEAN" },
2621 { "TRUE","TRUE" },
2622 { "TRUNC","TRUNC" },
2623 { "TTEST","TTEST" },
2624 { "TYPE","TYPE" },
2625 { "UNICHAR","UNICHAR" },
2626 { "UNICODE","UNICODE" },
2627 /* { "USDOLLAR","USDOLLAR" }, this is a synonym to DOLLAR */
2628 { "UPPER","UPPER" },
2629 { "VALUE","VALUE" },
2630 { "VAR","VAR" },
2631 { "VARA","VARA" },
2632 { "VARP","VARP" },
2633 { "VARPA","VARPA" },
2634 { "VDB","VDB" },
2635 { "VLOOKUP","VLOOKUP" },
2636 { "WEEKDAY","WEEKDAY" },
2637 { "WEEKNUM","WEEKNUM" },
2638 { "WEIBULL","WEIBULL" },
2639 { "WORKDAY","WORKDAY" },
2640 { "XIRR","XIRR" },
2641 { "XNPV","XNPV" },
2642 { "XOR","XOR" },
2643 { "YEAR","YEAR" },
2644 { "YEARFRAC","YEARFRAC" },
2645 { "YIELD","YIELD" },
2646 { "YIELDDISC","YIELDDISC" },
2647 { "YIELDMAT","YIELDMAT" },
2648 { "ZTEST","ZTEST" },
2649 { NULL, NULL }
2651 ODFConventions *oconv = (ODFConventions *)(out->convs);
2652 GHashTable *namemap;
2653 GHashTable *handlermap;
2655 char const *name = gnm_func_get_name (func->func, FALSE);
2656 gboolean (*handler) (GnmConventionsOut *out, GnmExprFunction const *func);
2658 if (NULL == oconv->state->openformula_namemap) {
2659 guint i;
2660 namemap = g_hash_table_new (go_ascii_strcase_hash,
2661 go_ascii_strcase_equal);
2662 for (i = 0; sc_func_renames[i].gnm_name; i++)
2663 g_hash_table_insert (namemap,
2664 (gchar *) sc_func_renames[i].gnm_name,
2665 (gchar *) sc_func_renames[i].odf_name);
2666 oconv->state->openformula_namemap = namemap;
2667 } else
2668 namemap = oconv->state->openformula_namemap;
2670 if (NULL == oconv->state->openformula_handlermap) {
2671 guint i;
2672 handlermap = g_hash_table_new (go_ascii_strcase_hash,
2673 go_ascii_strcase_equal);
2674 for (i = 0; sc_func_handlers[i].gnm_name; i++)
2675 g_hash_table_insert (handlermap,
2676 (gchar *) sc_func_handlers[i].gnm_name,
2677 sc_func_handlers[i].handler);
2678 oconv->state->openformula_handlermap = handlermap;
2679 } else
2680 handlermap = oconv->state->openformula_handlermap;
2682 handler = g_hash_table_lookup (handlermap, name);
2684 if (handler == NULL || !handler (out, func)) {
2685 char const *new_name = g_hash_table_lookup (namemap, name);
2686 GString *target = out->accum;
2688 if (new_name == NULL) {
2689 if (0 == g_ascii_strncasecmp (name, "ODF.", 4)) {
2690 char *new_u_name;
2691 new_u_name = g_ascii_strup (name + 4, -1);
2692 g_string_append (target, new_u_name);
2693 g_free (new_u_name);
2694 } else {
2695 char *new_u_name;
2696 g_string_append (target, "ORG.GNUMERIC.");
2697 new_u_name = g_ascii_strup (name, -1);
2698 g_string_append (target, new_u_name);
2699 g_free (new_u_name);
2702 else
2703 g_string_append (target, new_name);
2705 gnm_expr_list_as_string (func->argc, func->argv, out);
2707 return;
2710 static void
2711 odf_string_handler (GnmConventionsOut *out, GOString const *str)
2713 /* Constant strings are surrounded by double-quote characters */
2714 /* (QUOTATION MARK, U+0022); a literal double-quote character '"'*/
2715 /* (QUOTATION MARK, U+0022) as */
2716 /* string content is escaped by duplicating it. */
2718 odf_print_string (out, str->str, '"');
2721 static void
2722 odf_boolean_handler (GnmConventionsOut *out, gboolean val)
2724 g_string_append (out->accum, val ? "TRUE()" : "FALSE()");
2728 static GnmConventions *
2729 odf_expr_conventions_new (GnmOOExport *state)
2731 GnmConventions *conv = gnm_conventions_new_full
2732 (sizeof (ODFConventions));
2733 ODFConventions *oconv = (ODFConventions *)conv;
2734 gnm_float l10;
2736 conv->sheet_name_sep = '.';
2737 conv->arg_sep = ';';
2738 conv->array_col_sep = ';';
2739 conv->array_row_sep = '|';
2740 conv->intersection_char = '!';
2741 conv->decimal_sep_dot = TRUE;
2742 conv->output.string = odf_string_handler;
2743 conv->output.cell_ref = odf_cellref_as_string;
2744 conv->output.range_ref = odf_rangeref_as_string;
2745 conv->output.func = odf_expr_func_handler;
2746 conv->output.boolean = odf_boolean_handler;
2748 l10 = gnm_log10 (FLT_RADIX);
2749 conv->output.decimal_digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
2750 (l10 == (int)l10 ? 0 : 1);
2752 oconv->state = state;
2754 return conv;
2757 static gboolean
2758 odf_cell_is_covered (G_GNUC_UNUSED Sheet const *sheet,
2759 G_GNUC_UNUSED GnmCell *current_cell,
2760 int col, int row, GnmRange const *merge_range,
2761 GSList **merge_ranges)
2763 GSList *l;
2765 if (merge_range != NULL) {
2766 GnmRange *new_range = g_new(GnmRange, 1);
2767 *new_range = *merge_range;
2768 (*merge_ranges) = g_slist_prepend (*merge_ranges, new_range);
2769 return FALSE;
2772 if ((*merge_ranges) == NULL)
2773 return FALSE;
2775 *merge_ranges = g_slist_remove_all (*merge_ranges, NULL);
2777 for (l = *merge_ranges; l != NULL; l = g_slist_next(l)) {
2778 GnmRange *r = l->data;
2779 if (r->end.row < row) {
2780 /* We do not need this range anymore */
2781 g_free (r);
2782 l->data = NULL;
2783 continue;
2785 /* no need to check for beginning rows */
2786 /* we have to check for column range */
2787 if ((r->start.col <= col) && (col <= r->end.col))
2788 return TRUE;
2790 return FALSE;
2793 static void
2794 odf_write_comment (GnmOOExport *state, GnmComment const *cc)
2796 char *author = NULL;
2797 char *text = NULL;
2798 PangoAttrList * markup = NULL;
2799 gboolean pp = TRUE;
2801 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
2803 g_object_get (G_OBJECT (cc), "text", &text,
2804 "markup", &markup, "author", &author, NULL);
2806 gsf_xml_out_start_element (state->xml, OFFICE "annotation");
2807 if (author != NULL) {
2808 gsf_xml_out_start_element (state->xml, DUBLINCORE "creator");
2809 gsf_xml_out_add_cstr (state->xml, NULL, author);
2810 gsf_xml_out_end_element (state->xml); /* DUBLINCORE "creator" */;
2811 g_free (author);
2813 if (text != NULL) {
2814 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
2815 gsf_xml_out_start_element (state->xml, TEXT "p");
2816 odf_new_markup (state, markup, text);
2817 gsf_xml_out_end_element (state->xml); /* p */
2818 g_free (text);
2819 if (markup != NULL)
2820 pango_attr_list_unref (markup);
2823 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
2824 gsf_xml_out_end_element (state->xml); /* OFFICE "annotation" */
2827 static char *
2828 odf_graph_get_series (GnmOOExport *state, GogGraph *sog, GnmParsePos *pp)
2830 GSList *list = gog_graph_get_data (sog);
2831 GString *str = g_string_new (NULL);
2833 for (;list != NULL; list = list->next) {
2834 GOData *dat = list->data;
2835 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
2836 if (texpr != NULL && gnm_expr_top_is_rangeref (texpr)) {
2837 char *formula = gnm_expr_top_as_string (texpr, pp, state->conv);
2838 g_string_append (str, odf_strip_brackets (formula));
2839 g_string_append_c (str, ' ');
2840 g_free (formula);
2844 return g_string_free (str, FALSE);
2847 static void
2848 odf_write_frame_size (GnmOOExport *state, SheetObject *so)
2850 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
2851 double res_pts[4] = {0.,0.,0.,0.};
2852 GnmRange const *r = &anchor->cell_bound;
2853 GnmCellRef ref;
2854 GnmExprTop const *texpr;
2855 GnmParsePos pp;
2856 char *formula;
2857 Sheet *sheet;
2859 sheet_object_anchor_to_offset_pts (anchor, state->sheet, res_pts);
2861 switch (anchor->mode) {
2862 case GNM_SO_ANCHOR_TWO_CELLS:
2863 odf_add_pt (state->xml, SVG "x", res_pts[0]);
2864 odf_add_pt (state->xml, SVG "y", res_pts[1]);
2865 odf_add_pt (state->xml, TABLE "end-x", res_pts[2]);
2866 odf_add_pt (state->xml, TABLE "end-y", res_pts[3]);
2867 /* The next 3 lines should not be needed, but older versions of Gnumeric used the */
2868 /* width and height. */
2869 sheet_object_anchor_to_pts (anchor, state->sheet, res_pts);
2870 odf_add_pt (state->xml, SVG "width", res_pts[2] - res_pts[0]);
2871 odf_add_pt (state->xml, SVG "height", res_pts[3] - res_pts[1]);
2873 gnm_cellref_init (&ref, (Sheet *) state->sheet, r->end.col, r->end.row, TRUE);
2874 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
2875 parse_pos_init_sheet (&pp, state->sheet);
2876 formula = gnm_expr_top_as_string (texpr, &pp, state->conv);
2877 gnm_expr_top_unref (texpr);
2878 gsf_xml_out_add_cstr (state->xml, TABLE "end-cell-address",
2879 odf_strip_brackets (formula));
2880 g_free (formula);
2881 break;
2882 case GNM_SO_ANCHOR_ONE_CELL:
2883 odf_add_pt (state->xml, SVG "x", res_pts[0]);
2884 odf_add_pt (state->xml, SVG "y", res_pts[1]);
2885 odf_add_pt (state->xml, SVG "width", anchor->offset[2]);
2886 odf_add_pt (state->xml, SVG "height", anchor->offset[3]);
2887 break;
2888 case GNM_SO_ANCHOR_ABSOLUTE:
2889 odf_add_pt (state->xml, SVG "x", anchor->offset[0]);
2890 odf_add_pt (state->xml, SVG "y", anchor->offset[1]);
2891 odf_add_pt (state->xml, SVG "width", anchor->offset[2]);
2892 odf_add_pt (state->xml, SVG "height", anchor->offset[3]);
2893 break;
2896 sheet = sheet_object_get_sheet (so);
2897 if (sheet) {
2898 int z;
2899 z = g_slist_length (sheet->sheet_objects)
2900 - sheet_object_get_stacking (so);
2901 gsf_xml_out_add_int (state->xml, DRAW "z-index", z);
2905 static void
2906 odf_write_multi_chart_frame_size (GnmOOExport *state, SheetObject *so, GogObject *obj, guint tr, guint tc)
2908 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
2909 double abs_pts[4] = {0.,0.,0.,0.};
2910 double off_pts[4] = {0.,0.,0.,0.};
2911 double res_pts[4] = {0.,0.,0.,0.};
2912 GnmRange const *r = &anchor->cell_bound;
2913 GnmCellRef ref;
2914 GnmExprTop const *texpr;
2915 GnmParsePos pp;
2916 char *formula;
2917 Sheet const *sheet = state->sheet;
2918 unsigned int xpos = 0, ypos = 0, columns = 1, rows = 1;
2919 double height, width;
2921 if (!gog_chart_get_position (GOG_CHART (obj),
2922 &xpos, &ypos, &columns, &rows)) {
2923 odf_write_frame_size (state, so);
2924 return;
2927 sheet_object_anchor_to_pts (anchor, sheet, abs_pts);
2928 sheet_object_anchor_to_offset_pts (anchor, sheet, off_pts);
2930 res_pts[0] = off_pts[0] + ((tc == 0) ? 0 : (xpos * (abs_pts[2]-abs_pts[0])/tc));
2931 res_pts[1] = off_pts[1] + ((tr == 0) ? 0 : (ypos * (abs_pts[3]-abs_pts[1])/tr));
2932 res_pts[2] = off_pts[0] + ((tc == 0) ? (abs_pts[2]-abs_pts[0]) :
2933 ((xpos + columns) * (abs_pts[2]-abs_pts[0])/tc));
2934 res_pts[3] = off_pts[1] + ((tr == 0) ? (abs_pts[3]-abs_pts[1]) :
2935 ((ypos + rows) * (abs_pts[3]-abs_pts[1])/tr));
2936 width = res_pts[2] - res_pts[0];
2937 height = res_pts[3] - res_pts[1];
2939 res_pts[2] -= sheet_col_get_distance_pts (sheet, r->start.col,
2940 r->end.col);
2941 res_pts[3] -= sheet_row_get_distance_pts (sheet, r->start.row,
2942 r->end.row);
2944 odf_add_pt (state->xml, SVG "x", res_pts[0]);
2945 odf_add_pt (state->xml, SVG "y", res_pts[1]);
2946 odf_add_pt (state->xml, TABLE "end-x", res_pts[2]);
2947 odf_add_pt (state->xml, TABLE "end-y", res_pts[3]);
2949 odf_add_pt (state->xml, SVG "width", width);
2950 odf_add_pt (state->xml, SVG "height", height);
2953 gnm_cellref_init (&ref, (Sheet *) sheet, r->end.col, r->end.row, TRUE);
2954 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
2955 parse_pos_init_sheet (&pp, state->sheet);
2956 formula = gnm_expr_top_as_string (texpr, &pp, state->conv);
2957 gnm_expr_top_unref (texpr);
2958 gsf_xml_out_add_cstr (state->xml, TABLE "end-cell-address",
2959 odf_strip_brackets (formula));
2960 g_free (formula);
2962 if (sheet) {
2963 int z;
2964 z = g_slist_length (sheet->sheet_objects)
2965 - sheet_object_get_stacking (so);
2966 gsf_xml_out_add_int (state->xml, DRAW "z-index", z);
2970 static guint
2971 odf_n_charts (GnmOOExport *state, SheetObject *so)
2973 GogGraph const *graph = sheet_object_graph_get_gog (so);
2974 GogObjectRole const *role = gog_object_find_role_by_name (GOG_OBJECT (graph), "Chart");
2975 GSList *list = gog_object_get_children (GOG_OBJECT (graph), role);
2976 guint n = g_slist_length (list);
2977 g_slist_free (list);
2978 return n;
2981 static void
2982 odf_write_graph (GnmOOExport *state, SheetObject *so, char const *name)
2984 GnmParsePos pp;
2985 parse_pos_init_sheet (&pp, state->sheet);
2987 if (name != NULL) {
2988 GogGraph *graph = sheet_object_graph_get_gog (so);
2989 GogObjectRole const *role = gog_object_find_role_by_name (GOG_OBJECT (graph), "Chart");
2990 GSList *list = gog_object_get_children (GOG_OBJECT (graph), role);
2991 if (list != NULL) {
2992 GSList *l = list;
2993 gboolean multichart = (NULL != list->next);
2994 char *series_name = odf_graph_get_series (state, graph, &pp);
2995 guint i = 0, total_rows, total_columns;
2997 if (multichart) {
2998 total_columns = gog_graph_num_cols (graph);
2999 total_rows = gog_graph_num_rows (graph);
3002 while (l) {
3003 char *full_name = g_strdup_printf ("%s-%i/", name, i);
3004 gsf_xml_out_start_element (state->xml, DRAW "frame");
3005 if (multichart)
3006 odf_write_multi_chart_frame_size (state, so, GOG_OBJECT (l->data),
3007 total_rows, total_columns);
3008 else
3009 odf_write_frame_size (state, so);
3010 gsf_xml_out_start_element (state->xml, DRAW "object");
3011 gsf_xml_out_add_cstr (state->xml, XLINK "href", full_name);
3012 g_free (full_name);
3013 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
3014 gsf_xml_out_add_cstr (state->xml, XLINK "show", "embed");
3015 gsf_xml_out_add_cstr (state->xml, XLINK "actuate", "onLoad");
3016 gsf_xml_out_add_cstr (state->xml, DRAW "notify-on-update-of-ranges",
3017 series_name);
3018 gsf_xml_out_end_element (state->xml); /* DRAW "object" */
3019 full_name = g_strdup_printf ("Pictures/%s-%i", name, i);
3020 gsf_xml_out_start_element (state->xml, DRAW "image");
3021 gsf_xml_out_add_cstr (state->xml, XLINK "href", full_name);
3022 g_free (full_name);
3023 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
3024 gsf_xml_out_add_cstr (state->xml, XLINK "show", "embed");
3025 gsf_xml_out_add_cstr (state->xml, XLINK "actuate", "onLoad");
3026 gsf_xml_out_end_element (state->xml); /* DRAW "image" */
3027 full_name = g_strdup_printf ("Pictures/%s-%i.png", name,i);
3028 gsf_xml_out_start_element (state->xml, DRAW "image");
3029 gsf_xml_out_add_cstr (state->xml, XLINK "href", full_name);
3030 g_free (full_name);
3031 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
3032 gsf_xml_out_add_cstr (state->xml, XLINK "show", "embed");
3033 gsf_xml_out_add_cstr (state->xml, XLINK "actuate", "onLoad");
3034 gsf_xml_out_end_element (state->xml); /* DRAW "image" */
3035 gsf_xml_out_end_element (state->xml); /* DRAW "frame" */
3036 i++;
3037 l = l->next;
3039 g_free (series_name);
3040 g_slist_free (list);
3042 } else
3043 g_warning ("Graph is missing from hash.");
3046 static void
3047 odf_write_image (GnmOOExport *state, SheetObject *so, char const *name)
3049 if (name != NULL) {
3050 char *image_type;
3051 char *fullname;
3052 GOImage *image = NULL;
3054 g_object_get (G_OBJECT (so),
3055 "image-type", &image_type,
3056 "image", &image,
3057 NULL);
3059 if (image) {
3060 /* Write attribute for surrounding draw:frame */
3061 const char *image_name = go_image_get_name (image);
3062 if (image_name)
3063 gsf_xml_out_add_cstr (state->xml, DRAW "name", image_name);
3064 g_object_unref (image);
3067 fullname = g_strdup_printf ("Pictures/%s.%s", name, image_type);
3069 gsf_xml_out_start_element (state->xml, DRAW "image");
3070 gsf_xml_out_add_cstr (state->xml, XLINK "href", fullname);
3071 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
3072 gsf_xml_out_add_cstr (state->xml, XLINK "show", "embed");
3073 gsf_xml_out_add_cstr (state->xml, XLINK "actuate", "onLoad");
3074 gsf_xml_out_end_element (state->xml); /* DRAW "image" */
3076 g_free(fullname);
3077 g_free (image_type);
3078 } else
3079 g_warning ("Image is missing from hash.");
3082 static void
3083 odf_write_frame (GnmOOExport *state, SheetObject *so)
3085 if (GNM_IS_SO_GRAPH (so))
3086 odf_write_graph (state, so, g_hash_table_lookup (state->graphs, so));
3087 else if (GNM_IS_SO_IMAGE (so)) {
3088 gsf_xml_out_start_element (state->xml, DRAW "frame");
3089 odf_write_frame_size (state, so);
3090 odf_write_image (state, so, g_hash_table_lookup (state->images, so));
3091 gsf_xml_out_end_element (state->xml); /* DRAW "frame" */
3092 } else {
3093 gsf_xml_out_start_element (state->xml, DRAW "frame");
3094 odf_write_frame_size (state, so);
3095 gsf_xml_out_start_element (state->xml, DRAW "text-box");
3096 gsf_xml_out_simple_element (state->xml, TEXT "p",
3097 "Missing Framed Sheet Object");
3098 gsf_xml_out_end_element (state->xml); /* DRAW "text-box" */
3099 gsf_xml_out_end_element (state->xml); /* DRAW "frame" */
3103 static void
3104 custom_shape_path_collector (GOPath *path, GString *gstr)
3106 char *path_string = NULL;
3107 path_string = go_path_to_svg (path);
3108 g_string_append (gstr, " N ");
3109 g_string_append (gstr, path_string);
3110 g_free (path_string);
3113 static void
3114 odf_write_custom_shape (GnmOOExport *state, SheetObject *so)
3116 gchar const *style_name = g_hash_table_lookup (state->so_styles, so);
3117 gchar *text = NULL;
3118 PangoAttrList * markup = NULL;
3119 gboolean pp = TRUE;
3120 GOPath *path = NULL;
3121 GPtrArray *paths;
3122 char *path_string = NULL;
3123 char *view_box = NULL;
3125 g_object_get (G_OBJECT (so), "text", &text, "markup", &markup, "path", &path,
3126 "paths", &paths, "viewbox", &view_box, NULL);
3128 gsf_xml_out_start_element (state->xml, DRAW "custom-shape");
3130 if (style_name != NULL)
3131 gsf_xml_out_add_cstr (state->xml, DRAW "style-name", style_name);
3132 odf_write_frame_size (state, so);
3134 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
3135 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
3136 gsf_xml_out_start_element (state->xml, TEXT "p");
3137 odf_new_markup (state, markup, text);
3138 gsf_xml_out_end_element (state->xml); /* p */
3139 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
3141 if (path) {
3142 char *ps = go_path_to_svg (path);
3143 path_string = g_strconcat (ps, " N", NULL);
3144 g_free(ps);
3146 if (paths) {
3147 GString *gstr = g_string_new (path_string);
3148 g_ptr_array_foreach (paths, (GFunc)custom_shape_path_collector, gstr);
3149 g_string_append (gstr, " N");
3150 path_string = g_string_free (gstr, FALSE);
3152 if (path_string) {
3153 gsf_xml_out_start_element (state->xml, DRAW "enhanced-geometry");
3154 gsf_xml_out_add_cstr (state->xml, SVG "viewBox", view_box);
3155 gsf_xml_out_add_cstr (state->xml, DRAW "enhanced-path", path_string);
3156 gsf_xml_out_end_element (state->xml); /* DRAW "enhanced-geometry" */
3158 gsf_xml_out_end_element (state->xml); /* DRAW "custom-shape" */
3160 g_free (text);
3161 g_free (path_string);
3162 g_free (view_box);
3163 if (markup)
3164 pango_attr_list_unref (markup);
3165 if (paths)
3166 g_ptr_array_unref (paths);
3167 if (path)
3168 go_path_free (path);
3172 static void
3173 odf_write_control (GnmOOExport *state, SheetObject *so, char const *id)
3175 gsf_xml_out_start_element (state->xml, DRAW "control");
3176 odf_write_frame_size (state, so);
3177 gsf_xml_out_add_cstr (state->xml, DRAW "control", id);
3178 gsf_xml_out_end_element (state->xml); /* DRAW "control" */
3181 static void
3182 odf_write_so_filled (GnmOOExport *state, SheetObject *so)
3184 char const *element;
3185 gboolean is_oval = FALSE;
3186 gchar *text = NULL;
3187 PangoAttrList * markup = NULL;
3188 gchar const *style_name = g_hash_table_lookup (state->so_styles, so);
3189 gboolean pp = TRUE;
3191 g_object_get (G_OBJECT (so), "is-oval", &is_oval, "text", &text, "markup", &markup, NULL);
3192 element = is_oval ? DRAW "ellipse" : DRAW "rect";
3194 gsf_xml_out_start_element (state->xml, element);
3195 if (style_name != NULL)
3196 gsf_xml_out_add_cstr (state->xml, DRAW "style-name", style_name);
3197 odf_write_frame_size (state, so);
3199 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
3200 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
3201 gsf_xml_out_start_element (state->xml, TEXT "p");
3202 odf_new_markup (state, markup, text);
3203 gsf_xml_out_end_element (state->xml); /* p */
3204 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
3206 g_free (text);
3207 if (markup)
3208 pango_attr_list_unref (markup);
3210 gsf_xml_out_end_element (state->xml); /* DRAW "rect" or "ellipse" */
3213 static void
3214 odf_write_line (GnmOOExport *state, SheetObject *so)
3216 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
3217 double res_pts[4] = {0.,0.,0.,0.};
3218 GnmRange const *r = &anchor->cell_bound;
3219 GnmCellRef ref;
3220 GnmExprTop const *texpr;
3221 GnmParsePos pp;
3222 char *formula;
3223 double x1, y1, x2, y2;
3224 gchar const *style_name = g_hash_table_lookup (state->so_styles, so);
3225 int z;
3227 gsf_xml_out_start_element (state->xml, DRAW "line");
3228 if (style_name != NULL)
3229 gsf_xml_out_add_cstr (state->xml, DRAW "style-name", style_name);
3230 z = g_slist_length (state->sheet->sheet_objects) -
3231 sheet_object_get_stacking (so);
3232 gsf_xml_out_add_int (state->xml, DRAW "z-index", z);
3234 sheet_object_anchor_to_pts (anchor, state->sheet, res_pts);
3236 switch (anchor->base.direction) {
3237 default:
3238 case GOD_ANCHOR_DIR_UNKNOWN:
3239 case GOD_ANCHOR_DIR_UP_RIGHT:
3240 x1 = res_pts[0];
3241 x2 = res_pts[2];
3242 y1 = res_pts[3];
3243 y2 = res_pts[1];
3244 break;
3245 case GOD_ANCHOR_DIR_DOWN_RIGHT:
3246 x1 = res_pts[0];
3247 x2 = res_pts[2];
3248 y1 = res_pts[1];
3249 y2 = res_pts[3];
3250 break;
3251 case GOD_ANCHOR_DIR_UP_LEFT:
3252 x1 = res_pts[2];
3253 x2 = res_pts[0];
3254 y1 = res_pts[3];
3255 y2 = res_pts[1];
3256 break;
3257 case GOD_ANCHOR_DIR_DOWN_LEFT:
3258 x1 = res_pts[2];
3259 x2 = res_pts[0];
3260 y1 = res_pts[1];
3261 y2 = res_pts[3];
3262 break;
3265 odf_add_pt (state->xml, SVG "x1", x1);
3266 odf_add_pt (state->xml, SVG "y1", y1);
3267 odf_add_pt (state->xml, SVG "x2", x2);
3268 odf_add_pt (state->xml, SVG "y2", y2);
3270 if (anchor->mode == GNM_SO_ANCHOR_TWO_CELLS) {
3271 sheet_object_anchor_to_offset_pts (anchor, state->sheet, res_pts);
3272 odf_add_pt (state->xml, TABLE "end-x", res_pts[2]);
3273 odf_add_pt (state->xml, TABLE "end-y", res_pts[3]);
3275 gnm_cellref_init (&ref, (Sheet *) state->sheet, r->end.col, r->end.row, TRUE);
3276 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
3277 parse_pos_init_sheet (&pp, state->sheet);
3278 formula = gnm_expr_top_as_string (texpr, &pp, state->conv);
3279 gnm_expr_top_unref (texpr);
3280 gsf_xml_out_add_cstr (state->xml, TABLE "end-cell-address",
3281 odf_strip_brackets (formula));
3282 g_free (formula);
3285 gsf_xml_out_end_element (state->xml); /* DRAW "line" */
3288 static void
3289 odf_write_objects (GnmOOExport *state, GSList *objects)
3291 GSList *l;
3293 for (l = objects; l != NULL; l = l->next) {
3294 SheetObject *so = l->data;
3295 char const *id = g_hash_table_lookup (state->controls, so);
3296 if (so == NULL) {
3297 g_warning ("NULL sheet object encountered.");
3298 continue;
3300 if (GNM_IS_FILTER_COMBO (so) || GNM_IS_VALIDATION_COMBO(so))
3301 continue;
3302 if (id != NULL)
3303 odf_write_control (state, so, id);
3304 else if (GNM_IS_CELL_COMMENT (so))
3305 odf_write_comment (state, GNM_CELL_COMMENT (so));
3306 else if (GNM_IS_SO_FILLED (so))
3307 odf_write_so_filled (state, so);
3308 else if (GNM_IS_SO_LINE (so))
3309 odf_write_line (state, so);
3310 else if (GNM_IS_SO_PATH (so))
3311 odf_write_custom_shape (state, so);
3312 else
3313 odf_write_frame (state, so);
3318 static void
3319 odf_write_link_start (GnmOOExport *state, GnmHLink *lnk)
3321 GType const t = G_OBJECT_TYPE (lnk);
3322 char *link_text = NULL;
3324 gsf_xml_out_start_element (state->xml, TEXT "a");
3325 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
3326 gsf_xml_out_add_cstr (state->xml, XLINK "actuate", "onRequest");
3328 if (g_type_is_a (t, gnm_hlink_url_get_type ())) {
3329 // This includes email
3330 link_text = g_strdup (gnm_hlink_get_target (lnk));
3331 } else if (g_type_is_a (t, gnm_hlink_cur_wb_get_type ())) {
3332 GnmExprTop const *texpr = gnm_hlink_get_target_expr (lnk);
3333 GnmSheetRange sr;
3335 if (texpr && GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_NAME) {
3336 GnmParsePos pp;
3337 char *s;
3338 parse_pos_init_sheet (&pp, gnm_hlink_get_sheet (lnk));
3339 s = gnm_expr_top_as_string (texpr, &pp, state->conv);
3340 link_text = g_strconcat ("#", s, NULL);
3341 g_free (s);
3342 } else if (gnm_hlink_get_range_target (lnk, &sr)) {
3343 link_text = g_strconcat
3344 ("#",
3345 sr.sheet->name_unquoted, ".",
3346 range_as_string (&sr.range),
3347 NULL);
3349 } else {
3350 g_warning ("Unexpected hyperlink type");
3353 gsf_xml_out_add_cstr (state->xml, XLINK "href", link_text ? link_text : "#");
3354 g_free (link_text);
3356 gsf_xml_out_add_cstr (state->xml, OFFICE "title", gnm_hlink_get_tip (lnk));
3359 static void
3360 odf_write_link_end (GnmOOExport *state, GnmHLink *lnk)
3362 gsf_xml_out_end_element (state->xml); /* a */
3366 static void
3367 odf_write_empty_cell (GnmOOExport *state, int num, GnmStyle const *style, GSList *objects)
3369 if (num > 0) {
3370 gsf_xml_out_start_element (state->xml, TABLE "table-cell");
3371 if (num > 1)
3372 gsf_xml_out_add_int (state->xml,
3373 TABLE "number-columns-repeated",
3374 num);
3375 if (style != NULL) {
3376 char const * name = odf_find_style (state, style);
3377 GnmValidation const *val = gnm_style_get_validation (style);
3378 GnmInputMsg *im;
3379 if (name != NULL)
3380 gsf_xml_out_add_cstr (state->xml,
3381 TABLE "style-name", name);
3382 if (val != NULL) {
3383 char *vname = oo_item_name (state, OO_ITEM_VALIDATION, val);
3384 gsf_xml_out_add_cstr (state->xml,
3385 TABLE "content-validation-name", vname);
3386 g_free (vname);
3387 } else if (NULL != (im = gnm_style_get_input_msg (style))) {
3388 char *vname = oo_item_name (state, OO_ITEM_INPUT_MSG, im);
3389 gsf_xml_out_add_cstr (state->xml,
3390 TABLE "content-validation-name", vname);
3391 g_free (vname);
3395 odf_write_objects (state, objects);
3396 gsf_xml_out_end_element (state->xml); /* table-cell */
3400 static void
3401 odf_write_covered_cell (GnmOOExport *state, int *num)
3403 if (*num > 0) {
3404 gsf_xml_out_start_element (state->xml, TABLE "covered-table-cell");
3405 if (*num > 1)
3406 gsf_xml_out_add_int (state->xml,
3407 TABLE "number-columns-repeated",
3408 *num);
3409 gsf_xml_out_end_element (state->xml); /* covered-table-cell */
3410 *num = 0;
3414 static gboolean
3415 odf_cellspan_is_empty (int col, GnmCell const *ok_span_cell)
3417 Sheet *sheet = ok_span_cell->base.sheet;
3418 int row = ok_span_cell->pos.row;
3419 ColRowInfo *ri = sheet_row_get (sheet, row);
3420 CellSpanInfo const *span = row_span_get (ri, col);
3421 GnmCell const *tmp;
3422 GnmCellPos pos;
3424 if (span != NULL && span->cell != ok_span_cell)
3425 return FALSE;
3427 pos.row = row;
3428 pos.col = col;
3429 if (gnm_sheet_merge_contains_pos (sheet, &pos) != NULL)
3430 return FALSE;
3432 tmp = sheet_cell_get (sheet, col, row);
3434 return (tmp == NULL || tmp->value == NULL ||
3435 (VALUE_IS_EMPTY (tmp->value) && !gnm_cell_has_expr(tmp)));
3438 static void
3439 odf_write_cell (GnmOOExport *state, GnmCell *cell, GnmRange const *merge_range,
3440 GnmStyle const *style, GSList *objects)
3442 int rows_spanned = 0, cols_spanned = 0;
3443 GnmHLink *lnk = NULL;
3444 gboolean col_spanned_fake = FALSE;
3446 if (merge_range != NULL) {
3447 rows_spanned = merge_range->end.row - merge_range->start.row + 1;
3448 cols_spanned = merge_range->end.col - merge_range->start.col + 1;
3451 if (style && cell && cols_spanned <= 1 && gnm_style_get_align_h (style) == GNM_HALIGN_CENTER_ACROSS_SELECTION) {
3452 /* We have to simulate GNM_HALIGN_CENTER_ACROSS_SELECTION by a merge */
3453 int cell_col = cell->pos.col;
3454 int cell_row = cell->pos.row;
3455 int max_col_spanned = gnm_sheet_get_max_cols (state->sheet) - cell_col;
3456 cols_spanned = 1;
3457 while (cols_spanned < max_col_spanned) {
3458 ColRowInfo const *ci;
3459 cell_col++;
3460 ci = sheet_col_get_info (state->sheet, cell_col);
3461 if (ci->visible) {
3462 if (odf_cellspan_is_empty (cell_col, cell)) {
3463 GnmStyle const * const cstyle =
3464 sheet_style_get (state->sheet, cell_col, cell_row);
3465 if (gnm_style_get_align_h (cstyle) != GNM_HALIGN_CENTER_ACROSS_SELECTION)
3466 break;
3467 } else
3468 break;
3470 cols_spanned++;
3472 col_spanned_fake = (cols_spanned > 1);
3475 gsf_xml_out_start_element (state->xml, TABLE "table-cell");
3477 if (cols_spanned > 1) {
3478 gsf_xml_out_add_int (state->xml,
3479 TABLE "number-columns-spanned", cols_spanned);
3480 if (col_spanned_fake && state->with_extension)
3481 odf_add_bool (state->xml, GNMSTYLE "columns-spanned-fake", TRUE);
3483 if (rows_spanned > 1)
3484 gsf_xml_out_add_int (state->xml,
3485 TABLE "number-rows-spanned", rows_spanned);
3486 if (style) {
3487 char const * name = odf_find_style (state, style);
3488 GnmValidation const *val = gnm_style_get_validation (style);
3489 if (name != NULL)
3490 gsf_xml_out_add_cstr (state->xml,
3491 TABLE "style-name", name);
3492 if (val != NULL) {
3493 char *vname = oo_item_name (state, OO_ITEM_VALIDATION, val);
3494 gsf_xml_out_add_cstr (state->xml,
3495 TABLE "content-validation-name", vname);
3496 g_free (vname);
3498 lnk = gnm_style_get_hlink (style);
3501 if (cell != NULL) {
3502 if ((NULL != cell->base.texpr) &&
3503 !gnm_expr_top_is_array_elem (cell->base.texpr, NULL, NULL)) {
3504 char *formula, *eq_formula;
3505 GnmParsePos pp;
3507 if (gnm_cell_is_array (cell)) {
3508 if (gnm_expr_top_is_array_corner (cell->base.texpr)) {
3509 int cols, rows;
3511 gnm_expr_top_get_array_size (cell->base.texpr, &cols, &rows);
3512 gsf_xml_out_add_uint (state->xml,
3513 TABLE "number-matrix-columns-spanned",
3514 (unsigned int)cols);
3515 gsf_xml_out_add_uint (state->xml,
3516 TABLE "number-matrix-rows-spanned",
3517 (unsigned int)rows);
3521 parse_pos_init_cell (&pp, cell);
3522 formula = gnm_expr_top_as_string (cell->base.texpr,
3523 &pp,
3524 state->conv);
3525 eq_formula = g_strdup_printf ("of:=%s", formula);
3527 gsf_xml_out_add_cstr (state->xml,
3528 TABLE "formula",
3529 eq_formula);
3530 g_free (formula);
3531 g_free (eq_formula);
3534 switch (cell->value->v_any.type) {
3535 case VALUE_EMPTY:
3536 break;
3537 case VALUE_BOOLEAN:
3538 gsf_xml_out_add_cstr_unchecked (state->xml,
3539 OFFICE "value-type", "boolean");
3540 odf_add_bool (state->xml, OFFICE "boolean-value",
3541 value_get_as_bool (cell->value, NULL));
3542 break;
3543 case VALUE_FLOAT: {
3544 GOFormat const *fmt = gnm_cell_get_format_given_style (cell, style);
3545 if (go_format_is_date (fmt)) {
3546 char *str;
3547 gnm_float f = value_get_as_float (cell->value);
3548 if (f == gnm_floor (f)) {
3549 gsf_xml_out_add_cstr_unchecked (state->xml,
3550 OFFICE "value-type", "date");
3551 str = format_value (state->date_fmt, cell->value, -1, workbook_date_conv (state->wb));
3552 gsf_xml_out_add_cstr (state->xml, OFFICE "date-value", str);
3553 } else {
3554 gsf_xml_out_add_cstr_unchecked (state->xml,
3555 OFFICE "value-type", "date");
3556 str = format_value (state->date_long_fmt, cell->value, -1, workbook_date_conv (state->wb));
3557 gsf_xml_out_add_cstr (state->xml, OFFICE "date-value", str);
3559 g_free (str);
3560 } else if (go_format_is_time (fmt) && (value_get_as_float (cell->value) >= 0.)) {
3561 char *str;
3562 gsf_xml_out_add_cstr_unchecked (state->xml,
3563 OFFICE "value-type", "time");
3564 str = format_value (state->time_fmt, cell->value, -1, workbook_date_conv (state->wb));
3565 gsf_xml_out_add_cstr (state->xml, OFFICE "time-value", str);
3566 g_free (str);
3567 } else {
3568 GString *str = g_string_new (NULL);
3570 gsf_xml_out_add_cstr_unchecked (state->xml,
3571 OFFICE "value-type", "float");
3572 value_get_as_gstring (cell->value, str, state->conv);
3573 gsf_xml_out_add_cstr (state->xml, OFFICE "value", str->str);
3575 g_string_free (str, TRUE);
3577 break;
3579 case VALUE_ERROR:
3580 if (NULL == cell->base.texpr) {
3581 /* see https://bugzilla.gnome.org/show_bug.cgi?id=610175 */
3582 /* this is the same that Excel does, OOo does not have */
3583 /* error literals. */
3584 char const *cv = value_peek_string (cell->value);
3585 char *eq_formula = g_strdup_printf ("of:=%s", cv);
3587 if (state->with_extension)
3588 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "error-value", cv);
3590 gsf_xml_out_add_cstr (state->xml,
3591 TABLE "formula",
3592 eq_formula);
3593 g_free (eq_formula);
3595 gsf_xml_out_add_cstr_unchecked (state->xml,
3596 OFFICE "value-type", "string");
3597 gsf_xml_out_add_cstr (state->xml,
3598 OFFICE "string-value",
3599 value_peek_string (cell->value));
3600 break;
3601 case VALUE_STRING:
3602 gsf_xml_out_add_cstr_unchecked (state->xml,
3603 OFFICE "value-type", "string");
3604 /* If this is a non-formula cell we show only the real formatted content */
3605 /* If the alignmnet type is 'FILL' we do need to give the string value! */
3606 if (NULL != cell->base.texpr || gnm_style_get_align_h (style) == GNM_HALIGN_FILL)
3607 gsf_xml_out_add_cstr (state->xml,
3608 OFFICE "string-value",
3609 value_peek_string (cell->value));
3610 break;
3611 case VALUE_CELLRANGE:
3612 case VALUE_ARRAY:
3613 default:
3614 break;
3618 odf_write_objects (state, objects);
3620 if (cell != NULL && cell->value != NULL) {
3621 gboolean pprint = TRUE;
3622 g_object_get (G_OBJECT (state->xml), "pretty-print", &pprint, NULL);
3623 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
3625 if ((VALUE_FMT (cell->value) == NULL)
3626 || (!VALUE_IS_STRING (cell->value))
3627 || (!go_format_is_markup (VALUE_FMT (cell->value)))) {
3628 char *rendered_string = gnm_cell_get_rendered_text (cell);
3629 gboolean white_written = TRUE;
3631 gsf_xml_out_start_element (state->xml, TEXT "p");
3632 if (lnk) odf_write_link_start (state, lnk);
3633 if (*rendered_string != '\0')
3634 odf_add_chars (state, rendered_string, strlen (rendered_string),
3635 &white_written);
3636 if (lnk) odf_write_link_end (state, lnk);
3637 gsf_xml_out_end_element (state->xml); /* p */
3638 g_free (rendered_string);
3639 } else {
3640 GString *str = g_string_new (NULL);
3641 const PangoAttrList * markup;
3643 value_get_as_gstring (cell->value, str, state->conv);
3644 markup = go_format_get_markup (VALUE_FMT (cell->value));
3646 gsf_xml_out_start_element (state->xml, TEXT "p");
3647 if (lnk) odf_write_link_start (state, lnk);
3648 odf_new_markup (state, markup, str->str);
3649 if (lnk) odf_write_link_end (state, lnk);
3650 gsf_xml_out_end_element (state->xml); /* p */
3652 g_string_free (str, TRUE);
3654 g_object_set (G_OBJECT (state->xml), "pretty-print", pprint, NULL);
3658 gsf_xml_out_end_element (state->xml); /* table-cell */
3661 static GnmStyle *
3662 filter_style (GnmStyle *default_style, GnmStyle * this)
3664 return ((default_style == this) ? NULL : this);
3667 static void
3668 write_col_style (GnmOOExport *state, GnmStyle *col_style, ColRowInfo const *ci,
3669 Sheet const *sheet)
3671 char const * name;
3673 if (col_style != NULL) {
3674 name = odf_find_style (state, col_style);
3675 if (name != NULL)
3676 gsf_xml_out_add_cstr (state->xml,
3677 TABLE "default-cell-style-name", name);
3679 name = odf_find_col_style (state,
3680 (ci == NULL) ? &sheet->cols.default_style: ci,
3681 FALSE);
3682 if (name != NULL)
3683 gsf_xml_out_add_cstr (state->xml, TABLE "style-name", name);
3685 if (ci != NULL && !ci->visible)
3686 gsf_xml_out_add_cstr (state->xml, TABLE "visibility", ci->in_filter ? "filter" : "collapse");
3689 static void
3690 odf_write_formatted_columns (GnmOOExport *state, Sheet const *sheet, GnmStyle **col_styles, int from, int to)
3692 int number_cols_rep;
3693 ColRowInfo const *last_ci;
3694 GnmStyle *last_col_style = NULL;
3695 int i;
3697 gsf_xml_out_start_element (state->xml, TABLE "table-column");
3698 number_cols_rep = 1;
3699 last_col_style = filter_style (state->default_style_region->style, col_styles[0]);
3700 last_ci = sheet_col_get (sheet, 0);
3701 write_col_style (state, last_col_style, last_ci, sheet);
3703 for (i = from+1; i < to; i++) {
3704 GnmStyle *this_col_style = filter_style (state->default_style_region->style, col_styles[i]);
3705 ColRowInfo const *this_ci = sheet_col_get (sheet, i);
3707 if ((this_col_style == last_col_style) && colrow_equal (last_ci, this_ci))
3708 number_cols_rep++;
3709 else {
3710 if (number_cols_rep > 1)
3711 gsf_xml_out_add_int (state->xml, TABLE "number-columns-repeated",
3712 number_cols_rep);
3713 gsf_xml_out_end_element (state->xml); /* table-column */
3715 gsf_xml_out_start_element (state->xml, TABLE "table-column");
3716 number_cols_rep = 1;
3717 last_col_style = this_col_style;
3718 last_ci = this_ci;
3719 write_col_style (state, last_col_style, last_ci, sheet);
3723 if (number_cols_rep > 1)
3724 gsf_xml_out_add_int (state->xml, TABLE "number-columns-repeated",
3725 number_cols_rep);
3726 gsf_xml_out_end_element (state->xml); /* table-column */
3729 static void
3730 write_row_style (GnmOOExport *state, ColRowInfo const *ci,
3731 Sheet const *sheet)
3733 char const * name;
3735 name = odf_find_row_style (state,
3736 (ci == NULL) ? &sheet->rows.default_style: ci,
3737 FALSE);
3738 if (name != NULL)
3739 gsf_xml_out_add_cstr (state->xml, TABLE "style-name", name);
3741 if (ci != NULL && !ci->visible)
3742 gsf_xml_out_add_cstr (state->xml, TABLE "visibility", ci->in_filter ? "filter" : "collapse");
3745 static gboolean
3746 row_info_equal (GnmOOExport *state, Sheet const *sheet,
3747 ColRowInfo const *ci1, ColRowInfo const *ci2)
3749 if (ci1 == ci2)
3750 return TRUE;
3751 else {
3752 char const *n1 =
3753 odf_find_row_style (state,
3754 (ci1 == NULL) ? &sheet->rows.default_style: ci1,
3755 FALSE);
3756 char const *n2 =
3757 odf_find_row_style (state,
3758 (ci2 == NULL) ? &sheet->rows.default_style: ci2,
3759 FALSE);
3760 return g_str_equal (n1, n2);
3764 static gboolean
3765 compare_row_styles (const Sheet *sheet, GnmStyle **styles, int orow)
3767 GnmStyle **ostyles = sheet_style_get_row2 (sheet, orow);
3768 gboolean res;
3770 res = !memcmp (styles, ostyles,
3771 gnm_sheet_get_max_cols (sheet) * sizeof (GnmStyle *));
3773 g_free (ostyles);
3775 return res;
3778 static GSList *
3779 odf_sheet_objects_get (Sheet const *sheet, GnmCellPos const *pos)
3781 GSList *res = NULL;
3782 GSList *ptr;
3784 g_return_val_if_fail (IS_SHEET (sheet), NULL);
3786 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = ptr->next ) {
3787 SheetObject *so = GNM_SO (ptr->data);
3788 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
3789 if (anchor->mode == GNM_SO_ANCHOR_ABSOLUTE) {
3790 if (pos == NULL)
3791 res = g_slist_prepend (res, so);
3792 } else if (pos && gnm_cellpos_equal (&anchor->cell_bound.start, pos))
3793 res = g_slist_prepend (res, so);
3795 return res;
3798 enum {
3799 RF_CELL = 1,
3800 RF_PAGEBREAK = 2,
3801 RF_OBJECT = 4,
3802 RF_STYLE = 8
3805 static void
3806 odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to,
3807 int row_length,
3808 GSList **sheet_merges, GnmPageBreaks *pb, GnmStyle **col_styles)
3810 int row;
3811 GPtrArray *all_cells;
3812 guint cno = 0;
3813 guint8 *row_flags;
3815 row_flags = g_new0 (guint8, gnm_sheet_get_max_rows (sheet));
3817 /* Find out what rows have objects. */
3819 GSList *ptr;
3821 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = ptr->next ) {
3822 SheetObject *so = GNM_SO (ptr->data);
3823 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
3824 int row = anchor->cell_bound.start.row;
3825 row_flags[row] |= RF_OBJECT;
3829 /* Find out what rows have page breaks. */
3830 for (row = from; row < to; row++) {
3831 if (gnm_page_breaks_get_break (pb, row) != GNM_PAGE_BREAK_NONE)
3832 row_flags[row] |= RF_PAGEBREAK;
3835 /* Find out what rows have cells. */
3837 GnmRange fake_extent;
3838 unsigned ui;
3839 range_init_rows (&fake_extent, sheet, from, to - 1);
3840 all_cells = sheet_cells ((Sheet*)sheet, &fake_extent);
3841 for (ui = 0; ui < all_cells->len; ui++) {
3842 GnmCell *cell = g_ptr_array_index (all_cells, ui);
3843 row_flags[cell->pos.row] |= RF_CELL;
3845 /* Add a NULL to simplify code. */
3846 g_ptr_array_add (all_cells, NULL);
3849 /* Find out what rows have style not covered by column styles. */
3851 guint8 *non_defaults_rows =
3852 sheet_style_get_nondefault_rows (sheet, col_styles);
3853 for (row = from; row < to; row++)
3854 if (non_defaults_rows[row])
3855 row_flags[row] |= RF_STYLE;
3856 g_free (non_defaults_rows);
3859 for (row = from; row < to; /* nothing here */) {
3860 ColRowInfo const *ci = sheet_row_get (sheet, row);
3861 GnmStyle const *null_style = NULL;
3862 int null_cell = 0;
3863 int covered_cell = 0;
3864 GnmCellPos pos;
3865 int repeat_count = 1;
3866 guint8 rf = row_flags[row];
3867 GnmStyle **row_styles = (rf & RF_STYLE)
3868 ? sheet_style_get_row2 (sheet, row)
3869 : NULL;
3871 pos.row = row;
3873 if (rf & RF_PAGEBREAK)
3874 gsf_xml_out_simple_element (state->xml,
3875 TEXT "soft-page-break",
3876 NULL);
3878 gsf_xml_out_start_element (state->xml, TABLE "table-row");
3879 write_row_style (state, ci, sheet);
3881 if ((rf & ~RF_STYLE) == 0) {
3883 * We have nothing but style (possibly default) in this
3884 * row, so see if some rows following this one are
3885 * identical.
3887 int row2;
3888 while ((row2 = row + repeat_count) < to &&
3889 row_flags[row2] == rf &&
3890 row_info_equal (state, sheet, ci, sheet_row_get (sheet, row2)) &&
3891 (rf == 0 || compare_row_styles (sheet, row_styles, row2)))
3892 repeat_count++;
3894 if (repeat_count > 1)
3895 gsf_xml_out_add_int (state->xml, TABLE "number-rows-repeated",
3896 repeat_count);
3899 if (rf) {
3900 int col;
3902 for (col = 0; col < row_length; col++) {
3903 GnmCell *current_cell;
3904 GnmRange const *merge_range;
3905 GSList *objects;
3906 GnmStyle const *this_style = row_styles
3907 ? row_styles[col]
3908 : col_styles[col];
3910 current_cell = g_ptr_array_index (all_cells, cno);
3911 if (current_cell &&
3912 current_cell->pos.row == row &&
3913 current_cell->pos.col == col)
3914 cno++;
3915 else
3916 current_cell = NULL;
3918 pos.col = col;
3920 merge_range = gnm_sheet_merge_is_corner (sheet, &pos);
3922 if (odf_cell_is_covered (sheet, current_cell, col, row,
3923 merge_range, sheet_merges)) {
3924 odf_write_empty_cell (state, null_cell, null_style, NULL);
3925 null_cell = 0;
3926 covered_cell++;
3927 continue;
3930 objects = (rf & RF_OBJECT)
3931 ? odf_sheet_objects_get (sheet, &pos)
3932 : NULL;
3934 if ((!(current_cell && gnm_cell_has_expr(current_cell))) &&
3935 (merge_range == NULL) && (objects == NULL) &&
3936 gnm_cell_is_empty (current_cell) &&
3937 !gnm_style_get_hlink (this_style)) {
3938 if ((null_cell == 0) || (null_style == this_style)) {
3939 null_style = this_style;
3940 if (covered_cell > 0)
3941 odf_write_covered_cell (state, &covered_cell);
3942 null_cell++;
3943 } else {
3944 odf_write_empty_cell (state, null_cell, null_style, NULL);
3945 null_style = this_style;
3946 null_cell = 1;
3948 continue;
3951 odf_write_empty_cell (state, null_cell, null_style, NULL);
3952 null_cell = 0;
3953 if (covered_cell > 0)
3954 odf_write_covered_cell (state, &covered_cell);
3955 odf_write_cell (state, current_cell, merge_range, this_style, objects);
3957 g_slist_free (objects);
3960 } else
3961 null_cell = row_length;
3963 odf_write_empty_cell (state, null_cell, null_style, NULL);
3964 null_cell = 0;
3965 if (covered_cell > 0)
3966 odf_write_covered_cell (state, &covered_cell);
3968 gsf_xml_out_end_element (state->xml); /* table-row */
3970 row += repeat_count;
3971 g_free (row_styles);
3974 g_ptr_array_free (all_cells, TRUE);
3975 g_free (row_flags);
3978 static void
3979 odf_write_sheet (GnmOOExport *state)
3981 /* While ODF allows the TABLE "table-columns" wrapper, */
3982 /* and TABLE "table-rows" wrapper, */
3983 /* MS Excel 2010 stumbles over it */
3984 /* So we may not use them! */
3986 Sheet const *sheet = state->sheet;
3987 int max_cols = gnm_sheet_get_max_cols (sheet);
3988 int max_rows = gnm_sheet_get_max_rows (sheet);
3989 GnmStyle **col_styles;
3990 GnmRange r;
3991 GSList *sheet_merges = NULL;
3992 GnmPageBreaks *pb = sheet->print_info->page_breaks.v;
3994 col_styles = sheet_style_most_common (sheet, TRUE);
3996 /* ODF does not allow us to mark soft page breaks between columns */
3997 if (print_load_repeat_range (sheet->print_info->repeat_left, &r, sheet)) {
3998 int repeat_left_start, repeat_left_end;
3999 repeat_left_start = r.start.col;
4000 repeat_left_end = r.end.col;
4002 if (repeat_left_start > 0)
4003 odf_write_formatted_columns (state, sheet, col_styles,
4004 0, repeat_left_start);
4005 gsf_xml_out_start_element
4006 (state->xml, TABLE "table-header-columns");
4007 odf_write_formatted_columns (state, sheet, col_styles,
4008 repeat_left_start,
4009 repeat_left_end + 1);
4010 gsf_xml_out_end_element (state->xml);
4011 if (repeat_left_end < max_cols)
4012 odf_write_formatted_columns (state, sheet, col_styles,
4013 repeat_left_end + 1, max_cols);
4014 } else
4015 odf_write_formatted_columns (state, sheet, col_styles, 0, max_cols);
4017 if (print_load_repeat_range (sheet->print_info->repeat_top, &r, sheet)) {
4018 int repeat_top_start, repeat_top_end;
4019 repeat_top_start = r.start.row;
4020 repeat_top_end = r.end.row;
4021 if (repeat_top_start > 0)
4022 odf_write_content_rows (state, sheet,
4023 0, repeat_top_start,
4024 max_cols, &sheet_merges, pb, col_styles);
4025 gsf_xml_out_start_element
4026 (state->xml, TABLE "table-header-rows");
4027 odf_write_content_rows (state, sheet,
4028 repeat_top_start, repeat_top_end + 1,
4029 max_cols, &sheet_merges, pb, col_styles);
4030 gsf_xml_out_end_element (state->xml);
4031 if (repeat_top_end < max_rows)
4032 odf_write_content_rows (state, sheet,
4033 repeat_top_end + 1, max_rows,
4034 max_cols, &sheet_merges, pb, col_styles);
4035 } else
4036 odf_write_content_rows (state, sheet,
4037 0, max_rows,
4038 max_cols, &sheet_merges, pb, col_styles);
4040 g_slist_free_full (sheet_merges, g_free);
4041 g_free (col_styles);
4045 static char const *
4046 odf_write_sheet_controls_get_id (GnmOOExport *state, SheetObject *so)
4048 char *id = g_strdup_printf ("CTRL%.4i",g_hash_table_size (state->controls));
4049 g_hash_table_replace (state->controls, so, id);
4050 return id;
4053 static void
4054 odf_write_sheet_control_content (GnmOOExport *state, GnmExprTop const *texpr)
4056 if (texpr && gnm_expr_top_is_rangeref (texpr)) {
4057 char *lnk = NULL;
4058 GnmParsePos pp;
4060 parse_pos_init_sheet (&pp, state->sheet);
4061 lnk = gnm_expr_top_as_string (texpr, &pp, state->conv);
4063 if (state->odf_version > 101)
4064 gsf_xml_out_add_cstr (state->xml,
4065 FORM "source-cell-range",
4066 odf_strip_brackets (lnk));
4067 else
4068 gsf_xml_out_add_cstr (state->xml,
4069 GNMSTYLE "source-cell-range",
4070 odf_strip_brackets (lnk));
4071 g_free (lnk);
4072 gnm_expr_top_unref (texpr);
4076 static void
4077 odf_write_sheet_control_linked_cell (GnmOOExport *state, GnmExprTop const *texpr)
4079 if (texpr && gnm_expr_top_is_rangeref (texpr)) {
4080 char *lnk = NULL;
4081 GnmParsePos pp;
4083 parse_pos_init_sheet (&pp, state->sheet);
4084 lnk = gnm_expr_top_as_string (texpr, &pp, state->conv);
4086 if (state->odf_version > 101)
4087 gsf_xml_out_add_cstr (state->xml, FORM "linked-cell",
4088 odf_strip_brackets (lnk));
4089 else
4090 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "linked-cell",
4091 odf_strip_brackets (lnk));
4092 g_free (lnk);
4093 gnm_expr_top_unref (texpr);
4097 static void
4098 odf_sheet_control_start_element (GnmOOExport *state, SheetObject *so,
4099 char const *element)
4101 char const *id = odf_write_sheet_controls_get_id (state, so);
4102 gsf_xml_out_start_element (state->xml, element);
4103 gsf_xml_out_add_cstr (state->xml, XML "id", id);
4104 gsf_xml_out_add_cstr (state->xml, FORM "id", id);
4108 static void
4109 odf_write_sheet_control_scrollbar (GnmOOExport *state, SheetObject *so,
4110 char const *implementation)
4112 GtkAdjustment *adj = sheet_widget_adjustment_get_adjustment (so);
4113 GnmExprTop const *texpr = sheet_widget_adjustment_get_link (so);
4115 odf_sheet_control_start_element (state, so, FORM "value-range");
4117 if (implementation != NULL)
4118 gsf_xml_out_add_cstr (state->xml,
4119 FORM "control-implementation",
4120 implementation);
4121 gsf_xml_out_add_cstr (state->xml, FORM "orientation",
4122 sheet_widget_adjustment_get_horizontal (so) ?
4123 "horizontal" : "vertical");
4124 go_xml_out_add_double (state->xml, FORM "value", gtk_adjustment_get_value (adj));
4125 go_xml_out_add_double (state->xml, FORM "min-value", gtk_adjustment_get_lower (adj));
4126 go_xml_out_add_double (state->xml, FORM "max-value", gtk_adjustment_get_upper (adj));
4127 gsf_xml_out_add_int (state->xml, FORM "step-size",
4128 (int)(gtk_adjustment_get_step_increment (adj) + 0.5));
4129 gsf_xml_out_add_int (state->xml, FORM "page-step-size",
4130 (int)(gtk_adjustment_get_page_increment (adj) + 0.5));
4131 /* OOo fails to import this control, but adding its control-implementation */
4132 /* crashes OOo */
4133 /* gsf_xml_out_add_cstr (state->xml, FORM "control-implementation", */
4134 /* OOO "com.sun.star.form.component.ScrollBar"); */
4136 odf_write_sheet_control_linked_cell (state, texpr);
4137 gsf_xml_out_end_element (state->xml); /* form:value-range */
4140 static void
4141 odf_write_sheet_control_checkbox (GnmOOExport *state, SheetObject *so)
4143 GnmExprTop const *texpr = sheet_widget_checkbox_get_link (so);
4144 char *label = NULL;
4145 gboolean active = FALSE;
4147 g_object_get (G_OBJECT (so), "text", &label, "active", &active, NULL);
4149 odf_sheet_control_start_element (state, so, FORM "checkbox");
4151 gsf_xml_out_add_cstr (state->xml, FORM "label", label);
4152 gsf_xml_out_add_cstr (state->xml, FORM "current-state", active ? "checked" : "unchecked");
4154 odf_write_sheet_control_linked_cell (state, texpr);
4156 gsf_xml_out_end_element (state->xml); /* form:checkbox */
4158 g_free (label);
4161 static void
4162 odf_write_sheet_control_frame (GnmOOExport *state, SheetObject *so)
4164 char *label = NULL;
4166 g_object_get (G_OBJECT (so), "text", &label, NULL);
4168 odf_sheet_control_start_element (state, so, FORM "generic-control");
4169 gsf_xml_out_add_cstr_unchecked (state->xml,
4170 FORM "control-implementation",
4171 GNMSTYLE "frame");
4173 gsf_xml_out_start_element (state->xml, FORM "properties");
4174 gsf_xml_out_start_element (state->xml, FORM "property");
4176 gsf_xml_out_add_cstr_unchecked (state->xml, FORM "property-name", GNMSTYLE "label");
4177 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "value-type", "string");
4178 gsf_xml_out_add_cstr (state->xml, OFFICE "string-value", label);
4179 gsf_xml_out_end_element (state->xml); /* form:property */
4180 gsf_xml_out_end_element (state->xml); /* form:properties */
4182 gsf_xml_out_end_element (state->xml); /* form:generic-control */
4184 g_free (label);
4187 static void
4188 odf_write_sheet_control_list (GnmOOExport *state, SheetObject *so,
4189 char const *element, gboolean is_listbox)
4191 GnmExprTop const *texpr = sheet_widget_list_base_get_result_link (so);
4192 gboolean as_index = sheet_widget_list_base_result_type_is_index (so);
4194 odf_sheet_control_start_element (state, so, element);
4196 odf_write_sheet_control_linked_cell (state, texpr);
4198 texpr = sheet_widget_list_base_get_content_link (so);
4199 odf_write_sheet_control_content (state, texpr);
4201 if (state->odf_version > 101 && is_listbox)
4202 gsf_xml_out_add_cstr_unchecked
4203 (state->xml, FORM "list-linkage-type",
4204 as_index ? "selection-indices" : "selection");
4205 else if (state->with_extension)
4206 gsf_xml_out_add_cstr_unchecked
4207 (state->xml, GNMSTYLE "list-linkage-type",
4208 as_index ? "selection-indices" : "selection");
4209 if (is_listbox)
4210 gsf_xml_out_add_int (state->xml, FORM "bound-column", 1);
4211 gsf_xml_out_end_element (state->xml);
4214 static void
4215 odf_write_sheet_control_radio_button (GnmOOExport *state, SheetObject *so)
4217 GnmExprTop const *texpr = sheet_widget_radio_button_get_link (so);
4218 GnmValue const *val = sheet_widget_radio_button_get_value (so);
4219 char *label = NULL;
4220 gboolean active = FALSE;
4222 g_object_get (G_OBJECT (so), "text", &label, "active", &active, NULL);
4224 odf_sheet_control_start_element (state, so, FORM "radio");
4226 gsf_xml_out_add_cstr (state->xml, FORM "label", label);
4227 odf_add_bool (state->xml, FORM "current-selected", active);
4229 if (val != NULL) {
4230 switch (val->v_any.type) {
4231 case VALUE_EMPTY:
4232 break;
4233 case VALUE_BOOLEAN:
4234 if (state->with_extension)
4235 gsf_xml_out_add_cstr_unchecked
4236 (state->xml,
4237 GNMSTYLE "value-type",
4238 "boolean");
4239 odf_add_bool (state->xml, FORM "value",
4240 value_get_as_bool (val, NULL));
4241 break;
4242 case VALUE_FLOAT: {
4243 GString *str = g_string_new (NULL);
4244 if (state->with_extension)
4245 gsf_xml_out_add_cstr_unchecked
4246 (state->xml,
4247 GNMSTYLE "value-type",
4248 "float");
4249 value_get_as_gstring (val, str, state->conv);
4250 gsf_xml_out_add_cstr (state->xml, FORM "value",
4251 str->str);
4252 g_string_free (str, TRUE);
4253 break;
4255 case VALUE_ERROR:
4256 case VALUE_STRING:
4257 if (state->with_extension)
4258 gsf_xml_out_add_cstr_unchecked
4259 (state->xml,
4260 GNMSTYLE "value-type",
4261 "string");
4262 gsf_xml_out_add_cstr (state->xml,
4263 FORM "value",
4264 value_peek_string (val));
4265 break;
4266 case VALUE_CELLRANGE:
4267 case VALUE_ARRAY:
4268 default:
4269 break;
4273 odf_write_sheet_control_linked_cell (state, texpr);
4275 gsf_xml_out_end_element (state->xml); /* form:checkbox */
4277 g_free (label);
4280 static void
4281 odf_write_sheet_control_button (GnmOOExport *state, SheetObject *so)
4283 GnmExprTop const *texpr = sheet_widget_button_get_link (so);
4284 char *label = NULL;
4286 odf_sheet_control_start_element (state, so, FORM "button");
4288 g_object_get (G_OBJECT (so), "text", &label, NULL);
4289 gsf_xml_out_add_cstr (state->xml, FORM "label", label);
4290 g_free (label);
4292 gsf_xml_out_add_cstr_unchecked (state->xml, FORM "button-type", "push");
4294 if (texpr != NULL ) {
4295 char *lnk = NULL, *name = NULL;
4296 GnmParsePos pp;
4298 parse_pos_init_sheet (&pp, state->sheet);
4299 lnk = gnm_expr_top_as_string (texpr, &pp, state->conv);
4301 gsf_xml_out_start_element (state->xml, OFFICE "event-listeners");
4303 gsf_xml_out_start_element (state->xml, SCRIPT "event-listener");
4304 gsf_xml_out_add_cstr_unchecked (state->xml, SCRIPT "event-name",
4305 "dom:mousedown");
4306 gsf_xml_out_add_cstr_unchecked (state->xml, SCRIPT "language",
4307 GNMSTYLE "short-macro");
4308 name = g_strdup_printf ("set-to-TRUE:%s", odf_strip_brackets (lnk));
4309 gsf_xml_out_add_cstr (state->xml, SCRIPT "macro-name", name);
4310 g_free (name);
4311 gsf_xml_out_end_element (state->xml); /* script:event-listener */
4313 gsf_xml_out_start_element (state->xml, SCRIPT "event-listener");
4314 gsf_xml_out_add_cstr_unchecked (state->xml, SCRIPT "event-name",
4315 "dom:mouseup");
4316 gsf_xml_out_add_cstr_unchecked (state->xml, SCRIPT "language",
4317 GNMSTYLE "short-macro");
4318 name = g_strdup_printf ("set-to-FALSE:%s", odf_strip_brackets (lnk));
4319 gsf_xml_out_add_cstr (state->xml, SCRIPT "macro-name", name);
4320 g_free (name);
4321 gsf_xml_out_end_element (state->xml); /* script:event-listener */
4323 gsf_xml_out_end_element (state->xml); /* office:event-listeners */
4325 g_free (lnk);
4326 gnm_expr_top_unref (texpr);
4329 gsf_xml_out_end_element (state->xml); /* form:checkbox */
4332 static void
4333 odf_write_sheet_controls (GnmOOExport *state)
4335 Sheet const *sheet = state->sheet;
4336 GSList *objects = sheet->sheet_objects, *l;
4338 gsf_xml_out_start_element (state->xml, OFFICE "forms");
4339 odf_add_bool (state->xml, FORM "automatic-focus", FALSE);
4340 odf_add_bool (state->xml, FORM "apply-design-mode", FALSE);
4341 gsf_xml_out_start_element (state->xml, FORM "form");
4343 for (l = objects; l != NULL; l = l->next) {
4344 SheetObject *so = l->data;
4346 if (GNM_IS_SOW_SCROLLBAR (so))
4347 odf_write_sheet_control_scrollbar
4348 (state, so, GNMSTYLE "scrollbar");
4349 else if (GNM_IS_SOW_SLIDER (so))
4350 odf_write_sheet_control_scrollbar
4351 (state, so, GNMSTYLE "slider");
4352 else if (GNM_IS_SOW_SPINBUTTON (so))
4353 odf_write_sheet_control_scrollbar
4354 (state, so, GNMSTYLE "spinbutton");
4355 else if (GNM_IS_SOW_CHECKBOX (so))
4356 odf_write_sheet_control_checkbox (state, so);
4357 else if (GNM_IS_SOW_RADIO_BUTTON (so))
4358 odf_write_sheet_control_radio_button (state, so);
4359 else if (GNM_IS_SOW_LIST (so))
4360 odf_write_sheet_control_list (state, so,
4361 FORM "listbox", TRUE);
4362 else if (GNM_IS_SOW_COMBO (so))
4363 odf_write_sheet_control_list (state, so,
4364 FORM "combobox", FALSE);
4365 else if (GNM_IS_SOW_BUTTON (so))
4366 odf_write_sheet_control_button (state, so);
4367 else if (GNM_IS_SOW_FRAME (so))
4368 odf_write_sheet_control_frame (state, so);
4371 gsf_xml_out_end_element (state->xml); /* form:form */
4372 gsf_xml_out_end_element (state->xml); /* office:forms */
4375 static void
4376 odf_write_filter_cond (GnmOOExport *state, GnmFilter const *filter, int i)
4378 GnmFilterCondition const *cond = gnm_filter_get_condition (filter, i);
4379 char const *op, *type = NULL;
4380 GString *val_str = NULL;
4382 if (cond == NULL)
4383 return;
4385 switch (cond->op[0]) {
4386 case GNM_FILTER_OP_EQUAL: op = "="; break;
4387 case GNM_FILTER_OP_GT: op = ">"; break;
4388 case GNM_FILTER_OP_LT: op = "<"; break;
4389 case GNM_FILTER_OP_GTE: op = ">="; break;
4390 case GNM_FILTER_OP_LTE: op = "<="; break;
4391 case GNM_FILTER_OP_NOT_EQUAL: op = "!="; break;
4392 case GNM_FILTER_OP_MATCH: op = "match"; break;
4393 case GNM_FILTER_OP_NO_MATCH: op = "!match"; break;
4395 case GNM_FILTER_OP_BLANKS: op = "empty"; break;
4396 case GNM_FILTER_OP_NON_BLANKS: op = "!empty"; break;
4397 case GNM_FILTER_OP_TOP_N: op = "top values"; break;
4398 case GNM_FILTER_OP_BOTTOM_N: op = "bottom values"; break;
4399 case GNM_FILTER_OP_TOP_N_PERCENT: op = "top percent"; break;
4400 case GNM_FILTER_OP_BOTTOM_N_PERCENT: op = "bottom percent"; break;
4401 /* remainder are not supported in ODF */
4402 default :
4403 return;
4406 if (GNM_FILTER_OP_TYPE_BUCKETS == (cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
4407 val_str = g_string_new (NULL);
4408 type = "number";
4409 g_string_printf (val_str, "%g", cond->count);
4410 } else if (GNM_FILTER_OP_TYPE_BLANKS != (cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
4411 val_str = g_string_new (NULL);
4412 type = VALUE_IS_FLOAT (cond->value[0]) ? "number" : "text";
4413 value_get_as_gstring (cond->value[0], val_str, state->conv);
4416 gsf_xml_out_start_element (state->xml, TABLE "filter-condition");
4417 gsf_xml_out_add_int (state->xml, TABLE "field-number", i);
4418 if (NULL != type && val_str != NULL) {
4419 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "data-type", type);
4420 gsf_xml_out_add_cstr (state->xml, TABLE "value", val_str->str);
4422 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "operator", op);
4423 gsf_xml_out_end_element (state->xml); /* </table:filter-condition> */
4425 if (val_str)
4426 g_string_free (val_str, TRUE);
4429 static void
4430 odf_write_autofilter (GnmOOExport *state, GnmFilter const *filter)
4432 GString *buf;
4433 unsigned i;
4435 gsf_xml_out_start_element (state->xml, TABLE "database-range");
4437 /* manually create a ref string with no '[]' bracing */
4438 buf = g_string_new (filter->sheet->name_quoted);
4439 g_string_append_c (buf, '.');
4440 g_string_append (buf, cellpos_as_string (&filter->r.start));
4441 g_string_append_c (buf, ':');
4442 g_string_append (buf, filter->sheet->name_quoted);
4443 g_string_append_c (buf, '.');
4444 g_string_append (buf, cellpos_as_string (&filter->r.end));
4445 gsf_xml_out_add_cstr (state->xml, TABLE "target-range-address", buf->str);
4446 g_string_free (buf, TRUE);
4448 odf_add_bool (state->xml, TABLE "display-filter-buttons", TRUE);
4450 if (filter->is_active) {
4451 gsf_xml_out_start_element (state->xml, TABLE "filter");
4452 if (filter->fields->len > 1) {
4453 gsf_xml_out_start_element (state->xml, TABLE "filter-and");
4454 for (i = 0 ; i < filter->fields->len ; i++)
4455 odf_write_filter_cond (state, filter, i);
4456 gsf_xml_out_end_element (state->xml); /* </table:filter-and> */
4457 } else if (filter->fields->len == 1)
4458 odf_write_filter_cond (state, filter, 0);
4459 gsf_xml_out_end_element (state->xml); /* </table:filter> */
4462 gsf_xml_out_end_element (state->xml); /* </table:database-range> */
4465 static void
4466 odf_validation_general_attributes (GnmOOExport *state, GnmValidation const *val)
4468 odf_add_bool (state->xml, TABLE "allow-empty-cell", val->allow_blank);
4469 gsf_xml_out_add_cstr (state->xml, TABLE "display-list",
4470 val->use_dropdown ? "unsorted" : "none");
4473 static void
4474 odf_validation_base_cell_address (GnmOOExport *state,
4475 Sheet *sheet, GnmStyleRegion const *sr,
4476 GnmParsePos *pp)
4478 GnmExprTop const *texpr;
4479 char *formula;
4480 GnmCellRef ref;
4482 gnm_cellref_init (&ref, sheet,
4483 sr->range.start.col,
4484 sr->range.start.row, TRUE);
4485 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
4486 parse_pos_init (pp, (Workbook *)state->wb, sheet,
4487 sr->range.start.col,
4488 sr->range.start.row);
4489 formula = gnm_expr_top_as_string (texpr, pp, state->conv);
4490 gsf_xml_out_add_cstr (state->xml, TABLE "base-cell-address",
4491 odf_strip_brackets (formula));
4492 g_free (formula);
4493 gnm_expr_top_unref (texpr);
4496 static void
4497 odf_validation_append_expression (GnmOOExport *state, GString *str, GnmExprTop const *texpr,
4498 GnmParsePos *pp)
4500 char *formula;
4502 formula = gnm_expr_top_as_string (texpr, pp, state->conv);
4503 g_string_append (str, formula);
4504 g_free (formula);
4507 static void
4508 odf_validation_append_expression_pair (GnmOOExport *state, GString *str,
4509 GnmValidation const *val,
4510 GnmParsePos *pp)
4512 g_string_append_c (str, '(');
4513 odf_validation_append_expression (state, str,
4514 val->deps[0].texpr, pp);
4515 g_string_append_c (str, ',');
4516 odf_validation_append_expression (state, str,
4517 val->deps[1].texpr, pp);
4518 g_string_append_c (str, ')');
4522 static void
4523 odf_validation_general (GnmOOExport *state, GnmValidation const *val,
4524 G_GNUC_UNUSED Sheet *sheet,
4525 G_GNUC_UNUSED GnmStyleRegion const *sr,
4526 char const *prefix, GnmParsePos *pp)
4528 GString *str = g_string_new ("of:");
4530 g_string_append (str, prefix);
4532 switch (val->op) {
4533 case GNM_VALIDATION_OP_NONE:
4534 g_string_append (str, "is-true-formula(1)");
4535 break;
4536 case GNM_VALIDATION_OP_BETWEEN:
4537 g_string_append (str, "cell-content-is-between");
4538 odf_validation_append_expression_pair (state, str, val, pp);
4539 break;
4540 case GNM_VALIDATION_OP_NOT_BETWEEN:
4541 g_string_append (str, "cell-content-is-not-between");
4542 odf_validation_append_expression_pair (state, str, val, pp);
4543 break;
4544 case GNM_VALIDATION_OP_EQUAL:
4545 g_string_append (str, "cell-content() = ");
4546 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4547 break;
4548 case GNM_VALIDATION_OP_NOT_EQUAL:
4549 g_string_append (str, "cell-content() != ");
4550 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4551 break;
4552 case GNM_VALIDATION_OP_GT:
4553 g_string_append (str, "cell-content() > ");
4554 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4555 break;
4556 case GNM_VALIDATION_OP_LT:
4557 g_string_append (str, "cell-content() < ");
4558 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4559 break;
4560 case GNM_VALIDATION_OP_GTE:
4561 g_string_append (str, "cell-content() >= ");
4562 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4563 break;
4564 case GNM_VALIDATION_OP_LTE:
4565 g_string_append (str, "cell-content() <= ");
4566 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4567 break;
4570 gsf_xml_out_add_cstr (state->xml, TABLE "condition", str->str);
4571 g_string_free (str, TRUE);
4574 static void
4575 odf_validation_length (GnmOOExport *state, GnmValidation const *val,
4576 G_GNUC_UNUSED Sheet *sheet,
4577 G_GNUC_UNUSED GnmStyleRegion const *sr, GnmParsePos *pp)
4579 GString *str = g_string_new ("of:");
4581 switch (val->op) {
4582 case GNM_VALIDATION_OP_NONE:
4583 g_string_append (str, "is-true-formula(1)");
4584 break;
4585 case GNM_VALIDATION_OP_BETWEEN:
4586 g_string_append (str, "cell-content-text-length-is-between");
4587 odf_validation_append_expression_pair (state, str, val, pp);
4588 break;
4589 case GNM_VALIDATION_OP_NOT_BETWEEN:
4590 g_string_append (str, "cell-content-text-length-is-not-between");
4591 odf_validation_append_expression_pair (state, str, val, pp);
4592 break;
4593 case GNM_VALIDATION_OP_EQUAL:
4594 g_string_append (str, "cell-content-text-length() = ");
4595 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4596 break;
4597 case GNM_VALIDATION_OP_NOT_EQUAL:
4598 g_string_append (str, "cell-content-text-length() != ");
4599 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4600 break;
4601 case GNM_VALIDATION_OP_GT:
4602 g_string_append (str, "cell-content-text-length() > ");
4603 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4604 break;
4605 case GNM_VALIDATION_OP_LT:
4606 g_string_append (str, "cell-content-text-length() < ");
4607 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4608 break;
4609 case GNM_VALIDATION_OP_GTE:
4610 g_string_append (str, "of:cell-content-text-length() >= ");
4611 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4612 break;
4613 case GNM_VALIDATION_OP_LTE:
4614 g_string_append (str, "cell-content-text-length() <= ");
4615 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4616 break;
4619 gsf_xml_out_add_cstr (state->xml, TABLE "condition", str->str);
4620 g_string_free (str, TRUE);
4623 static void
4624 odf_validation_custom (GnmOOExport *state, GnmValidation const *val,
4625 G_GNUC_UNUSED Sheet *sheet,
4626 G_GNUC_UNUSED GnmStyleRegion const *sr, GnmParsePos *pp)
4628 GString *str = g_string_new (NULL);
4630 g_string_append (str, "of:is-true-formula(");
4631 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4632 g_string_append_c (str, ')');
4634 gsf_xml_out_add_cstr (state->xml, TABLE "condition", str->str);
4635 g_string_free (str, TRUE);
4638 static void
4639 odf_validation_in_list (GnmOOExport *state, GnmValidation const *val,
4640 G_GNUC_UNUSED Sheet *sheet,
4641 G_GNUC_UNUSED GnmStyleRegion const *sr, GnmParsePos *pp)
4643 GString *str;
4645 str = g_string_new ("of:cell-content-is-in-list(");
4646 odf_validation_append_expression (state, str, val->deps[0].texpr, pp);
4647 g_string_append_c (str, ')');
4649 gsf_xml_out_add_cstr (state->xml, TABLE "condition", str->str);
4650 g_string_free (str, TRUE);
4653 static void
4654 odf_print_spreadsheet_content_validations (GnmOOExport *state)
4656 gboolean element_written = FALSE;
4657 int i;
4659 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
4660 Sheet *sheet = workbook_sheet_by_index (state->wb, i);
4661 GnmStyleList *list, *l;
4663 list = sheet_style_collect_validations (sheet, NULL);
4665 for (l = list; l != NULL; l = l->next) {
4666 GnmStyleRegion const *sr = l->data;
4667 GnmValidation const *val = gnm_style_get_validation (sr->style);
4668 GnmInputMsg const *msg = gnm_style_get_input_msg (sr->style);
4669 GnmParsePos pp;
4670 char const *message_type = NULL;
4671 char *name;
4673 if (val == NULL && msg == NULL) {
4674 g_warning ("Encountered NULL validation with NULL message!");
4675 continue;
4678 if (!element_written) {
4679 gsf_xml_out_start_element
4680 (state->xml, TABLE "content-validations");
4681 element_written = TRUE;
4683 gsf_xml_out_start_element (state->xml,
4684 TABLE "content-validation");
4686 name = val
4687 ? oo_item_name (state, OO_ITEM_VALIDATION, val)
4688 : oo_item_name (state, OO_ITEM_INPUT_MSG, msg);
4689 gsf_xml_out_add_cstr (state->xml, TABLE "name", name);
4690 g_free (name);
4692 if (val) {
4693 odf_validation_general_attributes (state, val);
4694 odf_validation_base_cell_address (state, sheet, sr, &pp);
4695 switch (val->type) {
4696 case GNM_VALIDATION_TYPE_ANY:
4697 odf_validation_general (state, val, sheet, sr, "", &pp);
4698 break;
4699 case GNM_VALIDATION_TYPE_AS_INT:
4700 odf_validation_general (state, val, sheet, sr,
4701 "cell-content-is-whole-number() and ", &pp);
4702 break;
4703 case GNM_VALIDATION_TYPE_AS_NUMBER:
4704 odf_validation_general (state, val, sheet, sr,
4705 "cell-content-is-decimal-number() and ", &pp);
4706 break;
4707 case GNM_VALIDATION_TYPE_AS_DATE:
4708 odf_validation_general (state, val, sheet, sr,
4709 "cell-content-is-date() and ", &pp);
4710 break;
4711 case GNM_VALIDATION_TYPE_AS_TIME:
4712 odf_validation_general (state, val, sheet, sr,
4713 "cell-content-is-time() and ", &pp);
4714 break;
4715 case GNM_VALIDATION_TYPE_IN_LIST:
4716 odf_validation_in_list (state, val, sheet, sr, &pp);
4717 break;
4718 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
4719 odf_validation_length (state, val, sheet, sr, &pp);
4720 break;
4721 case GNM_VALIDATION_TYPE_CUSTOM:
4722 odf_validation_custom (state, val, sheet, sr, &pp);
4723 break;
4727 /* writing help message */
4728 if (msg) {
4729 char const * msg_content = gnm_input_msg_get_msg (msg);
4730 char const * msg_title = gnm_input_msg_get_title (msg);
4732 if (msg_content != NULL || msg_title != NULL) {
4733 gsf_xml_out_start_element (state->xml,
4734 TABLE "help-message");
4735 odf_add_bool (state->xml, TABLE "display", TRUE);
4736 if (msg_title != NULL)
4737 gsf_xml_out_add_cstr (state->xml, TABLE "title", msg_title);
4739 if (msg_content != NULL && strlen (msg_content) > 0) {
4740 gboolean white_written = TRUE;
4741 gboolean pp = TRUE;
4742 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
4743 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
4744 gsf_xml_out_start_element (state->xml, TEXT "p");
4745 odf_add_chars (state, msg_content, strlen (msg_content),
4746 &white_written);
4747 gsf_xml_out_end_element (state->xml); /* p */
4748 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
4751 gsf_xml_out_end_element (state->xml);
4752 /* help message written */
4756 if (val) {
4757 /* writing error message */
4758 gsf_xml_out_start_element (state->xml,
4759 TABLE "error-message");
4760 odf_add_bool (state->xml, TABLE "display", TRUE);
4761 switch (val->style) {
4762 case GNM_VALIDATION_STYLE_NONE:
4763 case GNM_VALIDATION_STYLE_INFO:
4764 case GNM_VALIDATION_STYLE_PARSE_ERROR:
4765 message_type = "information";
4766 break;
4767 case GNM_VALIDATION_STYLE_STOP:
4768 message_type = "stop";
4769 break;
4770 case GNM_VALIDATION_STYLE_WARNING:
4771 message_type = "warning";
4772 break;
4774 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "message-type", message_type);
4775 if (val->title != NULL)
4776 gsf_xml_out_add_cstr (state->xml, TABLE "title", val->title->str);
4778 if (val->msg != NULL && go_string_get_len (val->msg) > 0) {
4779 gboolean white_written = TRUE;
4780 gboolean pp = TRUE;
4781 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
4782 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
4783 gsf_xml_out_start_element (state->xml, TEXT "p");
4784 odf_add_chars (state, val->msg->str, go_string_get_len (val->msg), &white_written);
4785 gsf_xml_out_end_element (state->xml); /* p */
4786 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
4789 gsf_xml_out_end_element (state->xml);
4790 /* error message written */
4793 gsf_xml_out_end_element (state->xml);
4794 /* </table:content-validation> */
4797 style_list_free (list);
4800 if (element_written)
4801 gsf_xml_out_end_element (state->xml); /* </table:content-validations> */
4805 static void
4806 odf_print_spreadsheet_content_prelude (GnmOOExport *state)
4808 gsf_xml_out_start_element (state->xml, TABLE "calculation-settings");
4809 gsf_xml_out_add_int (state->xml, TABLE "null-year", 1930);
4810 odf_add_bool (state->xml, TABLE "automatic-find-labels", FALSE);
4811 odf_add_bool (state->xml, TABLE "case-sensitive", FALSE);
4812 odf_add_bool (state->xml, TABLE "precision-as-shown", FALSE);
4813 odf_add_bool (state->xml, TABLE "search-criteria-must-apply-to-whole-cell", TRUE);
4814 odf_add_bool (state->xml, TABLE "use-regular-expressions", FALSE);
4815 if (state->odf_version > 101)
4816 odf_add_bool (state->xml, TABLE "use-wildcards", FALSE);
4817 gsf_xml_out_start_element (state->xml, TABLE "null-date");
4818 if (go_date_convention_base (workbook_date_conv (state->wb)) == 1900)
4819 /* As encouraged by the OpenFormula definition we "compensate" here. */
4820 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "date-value", "1899-12-30");
4821 else
4822 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "date-value", "1904-1-1");
4823 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "value-type", "date");
4824 gsf_xml_out_end_element (state->xml); /* </table:null-date> */
4825 gsf_xml_out_start_element (state->xml, TABLE "iteration");
4826 go_xml_out_add_double (state->xml, TABLE "maximum-difference",
4827 state->wb->iteration.tolerance);
4828 gsf_xml_out_add_cstr_unchecked (state->xml, TABLE "status",
4829 state->wb->iteration.enabled ? "enable" : "disable");
4830 gsf_xml_out_add_int (state->xml, TABLE "steps", state->wb->iteration.max_number);
4831 gsf_xml_out_end_element (state->xml); /* </table:iteration> */
4832 gsf_xml_out_end_element (state->xml); /* </table:calculation-settings> */
4834 odf_print_spreadsheet_content_validations (state);
4837 static void
4838 odf_write_named_expression (G_GNUC_UNUSED gpointer key, GnmNamedExpr *nexpr,
4839 GnmOOExport *state)
4841 char const *name;
4842 gboolean is_range;
4843 char *formula;
4844 GnmCellRef ref;
4845 GnmExprTop const *texpr;
4846 Sheet *sheet;
4848 g_return_if_fail (nexpr != NULL);
4850 if (!expr_name_is_active (nexpr))
4851 return;
4853 sheet = nexpr->pos.sheet;
4854 if (sheet == NULL)
4855 sheet = workbook_sheet_by_index (state->wb, 0);
4857 name = expr_name_name (nexpr);
4858 is_range = nexpr->texpr && !expr_name_is_placeholder (nexpr)
4859 && gnm_expr_top_is_rangeref (nexpr->texpr);
4861 if (is_range) {
4862 gsf_xml_out_start_element (state->xml, TABLE "named-range");
4863 gsf_xml_out_add_cstr (state->xml, TABLE "name", name);
4865 formula = gnm_expr_top_as_string (nexpr->texpr,
4866 &nexpr->pos,
4867 state->conv);
4868 gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address",
4869 odf_strip_brackets (formula));
4870 g_free (formula);
4872 gnm_cellref_init (&ref, sheet, nexpr->pos.eval.col,
4873 nexpr->pos.eval.row, FALSE);
4874 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
4875 formula = gnm_expr_top_as_string (texpr, &nexpr->pos, state->conv);
4876 gsf_xml_out_add_cstr (state->xml,
4877 TABLE "base-cell-address",
4878 odf_strip_brackets (formula));
4879 g_free (formula);
4880 gnm_expr_top_unref (texpr);
4882 #if 0
4883 // This would be the right thing to do per the spec, but
4884 // Excel ignores any name that has the attribute. LO does
4885 // not seem to write this.
4886 gsf_xml_out_add_cstr_unchecked
4887 (state->xml, TABLE "range-usable-as",
4888 "print-range filter repeat-row repeat-column");
4889 #endif
4891 if (nexpr->pos.sheet != NULL && state->with_extension
4892 && (state->odf_version < 102))
4893 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "scope",
4894 nexpr->pos.sheet->name_unquoted);
4896 gsf_xml_out_end_element (state->xml); /* </table:named-range> */
4897 } else if (!expr_name_is_placeholder (nexpr) && nexpr->texpr != NULL) {
4898 gsf_xml_out_start_element
4899 (state->xml, TABLE "named-expression");
4900 gsf_xml_out_add_cstr (state->xml, TABLE "name", name);
4902 formula = gnm_expr_top_as_string (nexpr->texpr,
4903 &nexpr->pos,
4904 state->conv);
4905 if (state->odf_version > 101) {
4906 char *eq_formula = g_strdup_printf ("of:=%s", formula);
4907 gsf_xml_out_add_cstr (state->xml, TABLE "expression", eq_formula);
4908 g_free (eq_formula);
4909 } else
4910 gsf_xml_out_add_cstr (state->xml, TABLE "expression", formula);
4911 g_free (formula);
4913 gnm_cellref_init (&ref, sheet, nexpr->pos.eval.col,
4914 nexpr->pos.eval.row, FALSE);
4915 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&ref));
4916 formula = gnm_expr_top_as_string (texpr, &nexpr->pos, state->conv);
4917 gsf_xml_out_add_cstr (state->xml,
4918 TABLE "base-cell-address",
4919 odf_strip_brackets (formula));
4920 g_free (formula);
4921 gnm_expr_top_unref (texpr);
4923 if (nexpr->pos.sheet != NULL && state->with_extension
4924 && (state->odf_version < 102))
4925 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "scope",
4926 nexpr->pos.sheet->name_unquoted);
4928 gsf_xml_out_end_element (state->xml); /* </table:named-expression> */
4932 static GsfXMLOut *
4933 create_new_xml_child (G_GNUC_UNUSED GnmOOExport *state, GsfOutput *child)
4935 return g_object_new (GSF_ODF_OUT_TYPE,
4936 "sink", child,
4937 "odf-version", state->odf_version,
4938 NULL);
4941 static void
4942 odf_write_content (GnmOOExport *state, GsfOutput *child)
4944 int i;
4945 int graph_n = 1;
4946 int image_n = 1;
4947 gboolean has_autofilters = FALSE;
4948 GSList *objects;
4950 state->xml = create_new_xml_child (state, child);
4951 gsf_xml_out_set_doc_type (state->xml, "\n");
4952 gsf_xml_out_start_element (state->xml, OFFICE "document-content");
4954 for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
4955 gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
4956 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version",
4957 state->odf_version_string);
4959 gsf_xml_out_simple_element (state->xml, OFFICE "scripts", NULL);
4961 gsf_xml_out_start_element (state->xml, OFFICE "font-face-decls");
4962 gsf_xml_out_end_element (state->xml); /* </office:font-face-decls> */
4964 gsf_xml_out_start_element (state->xml, OFFICE "automatic-styles");
4965 odf_write_table_styles (state);
4966 odf_write_character_styles (state);
4967 odf_write_cell_styles (state);
4968 odf_write_column_styles (state);
4969 odf_write_row_styles (state);
4970 odf_write_sheet_object_styles (state);
4971 gsf_xml_out_end_element (state->xml); /* </office:automatic-styles> */
4973 gsf_xml_out_start_element (state->xml, OFFICE "body");
4974 gsf_xml_out_start_element (state->xml, OFFICE "spreadsheet");
4976 odf_print_spreadsheet_content_prelude (state);
4978 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
4979 Sheet *sheet = workbook_sheet_by_index (state->wb, i);
4980 char *style_name;
4981 GnmRange *p_area;
4982 GSList *l, *graphs, *images;
4984 state->sheet = sheet;
4986 graphs = sheet_objects_get (sheet, NULL, GNM_SO_GRAPH_TYPE);
4987 for (l = graphs; l != NULL; l = l->next)
4988 g_hash_table_insert (state->graphs, l->data,
4989 g_strdup_printf ("Graph%i", graph_n++));
4990 g_slist_free (graphs);
4992 images = sheet_objects_get (sheet, NULL, GNM_SO_IMAGE_TYPE);
4993 for (l = images; l != NULL; l = l->next)
4994 g_hash_table_insert (state->images, l->data,
4995 g_strdup_printf ("Image%i", image_n++));
4996 g_slist_free (images);
4998 gsf_xml_out_start_element (state->xml, TABLE "table");
4999 gsf_xml_out_add_cstr (state->xml, TABLE "name", sheet->name_unquoted);
5001 style_name = table_style_name (state, sheet);
5002 gsf_xml_out_add_cstr (state->xml, TABLE "style-name", style_name);
5003 g_free (style_name);
5005 odf_add_bool (state->xml, TABLE "print", !sheet->print_info->do_not_print);
5007 p_area = sheet_get_nominal_printarea (sheet);
5008 if (p_area != NULL) {
5009 GnmValue *v = value_new_cellrange_r (sheet, p_area);
5010 GnmExprTop const *texpr;
5011 char *formula;
5012 GnmParsePos pp;
5013 GnmCellRef *a, *b;
5015 a = &v->v_range.cell.a;
5016 b = &v->v_range.cell.b;
5017 a->col_relative = b->col_relative = TRUE;
5018 a->row_relative = b->row_relative = TRUE;
5020 texpr = gnm_expr_top_new_constant (v);
5022 g_free (p_area);
5023 parse_pos_init_sheet (&pp, sheet);
5024 formula = gnm_expr_top_as_string (texpr,
5025 &pp,
5026 state->conv);
5027 gnm_expr_top_unref (texpr);
5028 gsf_xml_out_add_cstr (state->xml, TABLE "print-ranges",
5029 odf_strip_brackets (formula));
5030 g_free (formula);
5033 /* writing shapes with absolute anchors */
5034 objects = odf_sheet_objects_get (sheet, NULL);
5035 if (objects != NULL) {
5036 gsf_xml_out_start_element (state->xml, TABLE "shapes");
5037 odf_write_objects (state, objects);
5038 gsf_xml_out_end_element (state->xml);
5039 g_slist_free (objects);
5042 odf_write_sheet_controls (state);
5043 odf_write_sheet (state);
5044 if (state->odf_version > 101 && sheet->names) {
5045 gsf_xml_out_start_element (state->xml, TABLE "named-expressions");
5046 gnm_sheet_foreach_name (sheet,
5047 (GHFunc)&odf_write_named_expression, state);
5048 gsf_xml_out_end_element (state->xml); /* </table:named-expressions> */
5050 if (state->with_extension) {
5051 GSList *ptr, *copy;
5052 SheetView const *sv = sheet_get_view (sheet, state->wbv);
5053 if (sv) {
5054 gsf_xml_out_start_element (state->xml, GNMSTYLE "selections");
5055 gsf_xml_out_add_int (state->xml, GNMSTYLE "cursor-col", sv->edit_pos_real.col);
5056 gsf_xml_out_add_int (state->xml, GNMSTYLE "cursor-row", sv->edit_pos_real.row);
5058 /* Insert the selections in REVERSE order */
5059 copy = g_slist_copy (sv->selections);
5060 ptr = copy = g_slist_reverse (copy);
5061 for (; ptr != NULL ; ptr = ptr->next) {
5062 GnmRange const *r = ptr->data;
5063 gsf_xml_out_start_element (state->xml, GNMSTYLE "selection");
5064 odf_add_range (state, r);
5065 gsf_xml_out_end_element (state->xml); /* </gnm:selection> */
5067 g_slist_free (copy);
5069 gsf_xml_out_end_element (state->xml); /* </gnm:selections> */
5072 gsf_xml_out_end_element (state->xml); /* </table:table> */
5074 has_autofilters |= (sheet->filters != NULL);
5075 odf_update_progress (state, state->sheet_progress);
5078 gsf_xml_out_start_element (state->xml, TABLE "named-expressions");
5079 workbook_foreach_name
5080 (state->wb, (state->odf_version > 101),
5081 (GHFunc)&odf_write_named_expression, state);
5082 gsf_xml_out_end_element (state->xml); /* </table:named-expressions> */
5084 if (has_autofilters) {
5085 gsf_xml_out_start_element (state->xml, TABLE "database-ranges");
5086 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
5087 Sheet *sheet = workbook_sheet_by_index (state->wb, i);
5088 GSList *ptr;
5089 for (ptr = sheet->filters ; ptr != NULL ; ptr = ptr->next)
5090 odf_write_autofilter (state, ptr->data);
5093 gsf_xml_out_end_element (state->xml); /* </table:database-ranges> */
5096 gsf_xml_out_end_element (state->xml); /* </office:spreadsheet> */
5097 gsf_xml_out_end_element (state->xml); /* </office:body> */
5099 gsf_xml_out_end_element (state->xml); /* </office:document-content> */
5100 g_object_unref (state->xml);
5101 state->xml = NULL;
5104 /*****************************************************************************/
5106 static void
5107 odf_write_xl_style (char const *xl, char const *name, GnmOOExport *state)
5109 GOFormat *format;
5110 if (xl == NULL)
5111 xl = "General";
5112 format = go_format_new_from_XL (xl);
5113 go_format_output_to_odf (state->xml, format, 0, name,
5114 state->with_extension);
5115 go_format_unref (format);
5118 static void
5119 odf_render_tab (GnmOOExport *state, G_GNUC_UNUSED char const *args)
5121 gsf_xml_out_simple_element (state->xml, TEXT "sheet-name", NULL);
5124 static void
5125 odf_render_page (GnmOOExport *state, G_GNUC_UNUSED char const *args)
5127 gsf_xml_out_start_element (state->xml, TEXT "page-number");
5128 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "num-format", "1");
5129 /* odf_add_bool (state->xml, STYLE "num-letter-sync", TRUE); */
5130 gsf_xml_out_end_element (state->xml);
5133 static void
5134 odf_render_pages (GnmOOExport *state, G_GNUC_UNUSED char const *args)
5136 gsf_xml_out_start_element (state->xml, TEXT "page-count");
5137 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "num-format", "1");
5138 /* odf_add_bool (state->xml, STYLE "num-letter-sync", TRUE); */
5139 gsf_xml_out_end_element (state->xml);
5142 static void
5143 odf_render_date (GnmOOExport *state, char const *args)
5145 const char *style_name = NULL;
5147 if (args != NULL)
5148 style_name = xl_find_format_xl (state, args);
5150 gsf_xml_out_start_element (state->xml, TEXT "date");
5151 if (style_name)
5152 gsf_xml_out_add_cstr_unchecked
5153 (state->xml, STYLE "data-style-name", style_name);
5154 gsf_xml_out_end_element (state->xml);
5157 static void
5158 odf_render_date_to_xl (GnmOOExport *state, char const *args)
5160 if (args != NULL)
5161 xl_find_format_xl (state, args);
5164 static void
5165 odf_render_time (GnmOOExport *state, char const *args)
5167 const char *style_name = NULL;
5169 if (args != NULL)
5170 style_name = xl_find_format_xl (state, args);
5172 gsf_xml_out_start_element (state->xml, TEXT "time");
5173 if (style_name)
5174 gsf_xml_out_add_cstr_unchecked
5175 (state->xml, STYLE "data-style-name", style_name);
5176 gsf_xml_out_end_element (state->xml);
5178 static void
5179 odf_render_time_to_xl (GnmOOExport *state, char const *args)
5181 if (args != NULL)
5182 xl_find_format_xl (state, args);
5185 static void
5186 odf_render_file (GnmOOExport *state, G_GNUC_UNUSED char const *args)
5188 gsf_xml_out_start_element (state->xml, TEXT "file-name");
5189 gsf_xml_out_add_cstr_unchecked (state->xml, TEXT "display", "name-and-extension");
5190 gsf_xml_out_end_element (state->xml);
5193 static void
5194 odf_render_path (GnmOOExport *state, G_GNUC_UNUSED char const *args)
5196 gsf_xml_out_start_element (state->xml, TEXT "file-name");
5197 gsf_xml_out_add_cstr_unchecked (state->xml, TEXT "display", "path");
5198 gsf_xml_out_end_element (state->xml);
5201 static void
5202 odf_render_cell (GnmOOExport *state, char const *args)
5204 GnmExprTop const *texpr = NULL;
5205 GnmParsePos pp;
5206 char *formula, *full_formula;
5207 GnmConventions *convs;
5209 if (args) {
5210 convs = gnm_xml_io_conventions ();
5211 parse_pos_init_sheet (&pp, state->sheet);
5212 if (args && (g_str_has_prefix (args, "rep|")))
5213 args += 4;
5214 texpr = gnm_expr_parse_str (args, &pp, GNM_EXPR_PARSE_DEFAULT,
5215 convs, NULL);
5216 gnm_conventions_unref (convs);
5217 if (texpr) {
5218 formula = gnm_expr_top_as_string (texpr, &pp, state->conv);
5219 gnm_expr_top_unref (texpr);
5220 full_formula = g_strdup_printf ("of:=%s", formula);
5221 g_free (formula);
5224 gsf_xml_out_start_element (state->xml, TEXT "expression");
5225 gsf_xml_out_add_cstr_unchecked (state->xml, TEXT "display", "value");
5226 if (texpr) {
5227 gsf_xml_out_add_cstr (state->xml, TEXT "formula",
5228 full_formula);
5229 g_free (full_formula);
5232 gsf_xml_out_end_element (state->xml);
5235 typedef struct {
5236 char const *name;
5237 void (*render)(GnmOOExport *state, char const *args);
5238 char *name_trans;
5239 } render_ops_t;
5241 static render_ops_t odf_render_ops [] = {
5242 { N_("tab"), odf_render_tab, NULL},
5243 { N_("page"), odf_render_page, NULL},
5244 { N_("pages"), odf_render_pages, NULL},
5245 { N_("date"), odf_render_date, NULL},
5246 { N_("time"), odf_render_time, NULL},
5247 { N_("file"), odf_render_file, NULL},
5248 { N_("path"), odf_render_path, NULL},
5249 { N_("cell"), odf_render_cell, NULL},
5250 { NULL, NULL, NULL },
5253 static render_ops_t odf_render_ops_to_xl [] = {
5254 { N_("tab"), NULL, NULL},
5255 { N_("page"), NULL, NULL},
5256 { N_("pages"), NULL, NULL},
5257 { N_("date"), odf_render_date_to_xl, NULL},
5258 { N_("time"), odf_render_time_to_xl, NULL},
5259 { N_("file"), NULL, NULL},
5260 { N_("path"), NULL, NULL},
5261 { N_("cell"), NULL, NULL},
5262 { NULL, NULL, NULL },
5265 static void
5266 ods_render_ops_clear (render_ops_t *render_ops)
5268 int i;
5270 for (i = 0; render_ops [i].name; i++) {
5271 g_free (render_ops[i].name_trans);
5272 render_ops[i].name_trans = NULL;
5277 * Renders an opcode. The opcodes can take an argument by adding trailing ':'
5278 * to the opcode and then a number format code
5280 static void
5281 odf_render_opcode (GnmOOExport *state, char /* non-const */ *opcode,
5282 render_ops_t *render_ops)
5284 char *args;
5285 char *opcode_trans;
5286 int i;
5288 args = g_utf8_strchr (opcode, -1, ':');
5289 if (args) {
5290 *args = 0;
5291 args++;
5293 opcode_trans = g_utf8_casefold (opcode, -1);
5295 for (i = 0; render_ops [i].name; i++) {
5296 if (render_ops [i].name_trans == NULL) {
5297 render_ops [i].name_trans
5298 = g_utf8_casefold (_(render_ops [i].name), -1);
5301 if (((g_ascii_strcasecmp (render_ops [i].name, opcode) == 0) ||
5302 (g_utf8_collate (render_ops [i].name_trans, opcode_trans) == 0))
5303 && (render_ops [i].render != NULL)){
5304 (*render_ops [i].render)(state, args);
5307 g_free (opcode_trans);
5310 static void
5311 odf_hf_region_to_xl_styles (GnmOOExport *state, char const *format)
5313 char const *p;
5315 if (format == NULL)
5316 return;
5318 for (p = format; *p; p = g_utf8_next_char(p)) {
5319 if (*p == '&' && p[1] == '[') {
5320 char const *start;
5322 p += 2;
5323 start = p;
5324 while (*p && (*p != ']'))
5325 p++;
5327 if (*p == ']') {
5328 char *operation = g_strndup (start, p - start);
5329 odf_render_opcode (state, operation, odf_render_ops_to_xl);
5330 g_free (operation);
5331 } else
5332 break;
5338 * When we write the master styles we need certain data style. Here we are making
5339 * sure that those data styles were in fact written.
5341 static void
5342 odf_master_styles_to_xl_styles (GnmOOExport *state)
5344 int i;
5346 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
5347 Sheet const *sheet = workbook_sheet_by_index (state->wb, i);
5349 if (sheet->print_info->page_setup == NULL)
5350 gnm_print_info_load_defaults (sheet->print_info);
5352 if (sheet->print_info->header != NULL) {
5353 odf_hf_region_to_xl_styles
5354 (state, sheet->print_info->header->left_format);
5355 odf_hf_region_to_xl_styles
5356 (state, sheet->print_info->header->middle_format);
5357 odf_hf_region_to_xl_styles
5358 (state, sheet->print_info->header->right_format);
5360 if (sheet->print_info->footer != NULL) {
5361 odf_hf_region_to_xl_styles
5362 (state, sheet->print_info->footer->left_format);
5363 odf_hf_region_to_xl_styles
5364 (state, sheet->print_info->footer->middle_format);
5365 odf_hf_region_to_xl_styles
5366 (state, sheet->print_info->footer->right_format);
5371 static void
5372 odf_write_hf_region (GnmOOExport *state, char const *format, char const *id)
5374 gboolean pp = TRUE;
5375 char const *p;
5376 GString *text;
5378 if (format == NULL)
5379 return;
5381 gsf_xml_out_start_element (state->xml, id);
5382 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
5383 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
5384 gsf_xml_out_start_element (state->xml, TEXT "p");
5386 text = g_string_new (NULL);
5387 for (p = format; *p; p = g_utf8_next_char(p)) {
5388 if (*p == '&' && p[1] == '[') {
5389 char const *start;
5391 p += 2;
5392 start = p;
5393 while (*p && (*p != ']'))
5394 p++;
5396 if (*p == ']') {
5397 char *operation = g_strndup (start, p - start);
5398 if (text->len > 0) {
5399 gsf_xml_out_simple_element
5400 (state->xml, TEXT "span", text->str);
5401 g_string_truncate (text, 0);
5403 odf_render_opcode (state, operation, odf_render_ops);
5404 g_free (operation);
5405 } else
5406 break;
5407 } else
5408 g_string_append_len (text, p, g_utf8_next_char(p) - p);
5410 if (text->len > 0)
5411 gsf_xml_out_simple_element (state->xml, TEXT "span", text->str);
5412 g_string_free (text, TRUE);
5414 gsf_xml_out_end_element (state->xml); /* </text:p> */
5415 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
5416 gsf_xml_out_end_element (state->xml); /* id */
5419 static void
5420 odf_write_hf (GnmOOExport *state, GnmPrintInformation *pi, char const *id, gboolean header)
5422 GnmPrintHF *hf = header ? pi->header : pi->footer;
5423 double page_margin;
5424 double hf_height;
5425 GtkPageSetup *gps = gnm_print_info_get_page_setup (pi);
5427 if (hf == NULL)
5428 return;
5430 if (header) {
5431 page_margin = gtk_page_setup_get_top_margin (gps, GTK_UNIT_POINTS);
5432 hf_height = pi->edge_to_below_header - page_margin;
5433 } else {
5434 page_margin = gtk_page_setup_get_bottom_margin (gps, GTK_UNIT_POINTS);
5435 hf_height = pi->edge_to_above_footer - page_margin;
5438 gsf_xml_out_start_element (state->xml, id);
5439 odf_add_bool (state->xml, STYLE "display", hf_height > 0.);
5441 odf_write_hf_region (state, hf->left_format, STYLE "region-left");
5442 odf_write_hf_region (state, hf->middle_format, STYLE "region-center");
5443 odf_write_hf_region (state, hf->right_format, STYLE "region-right");
5444 gsf_xml_out_end_element (state->xml); /* id */
5447 static void
5448 odf_store_data_style_for_style_with_name (GnmStyleRegion *sr, G_GNUC_UNUSED char const *name, GnmOOExport *state)
5450 GnmStyle const *style = sr->style;
5452 if (gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
5453 GOFormat const *format = gnm_style_get_format(style);
5454 if (format != NULL && !go_format_is_markup (format) && !go_format_is_general (format)) {
5455 xl_find_format (state, format);
5460 static int
5461 by_key_str (gpointer key_a, G_GNUC_UNUSED gpointer val_a,
5462 gpointer key_b, G_GNUC_UNUSED gpointer val_b,
5463 G_GNUC_UNUSED gpointer user)
5465 return strcmp (key_a, key_b);
5468 static int
5469 by_value_str (G_GNUC_UNUSED gpointer key_a, gpointer val_a,
5470 G_GNUC_UNUSED gpointer key_b, gpointer val_b,
5471 G_GNUC_UNUSED gpointer user)
5473 return strcmp (val_a, val_b);
5476 static void
5477 odf_write_office_styles (GnmOOExport *state)
5479 gsf_xml_out_start_element (state->xml, OFFICE "styles");
5481 /* We need to make sure all the data styles for the named styles are included */
5482 g_hash_table_foreach (state->named_cell_style_regions, (GHFunc) odf_store_data_style_for_style_with_name, state);
5484 gnm_hash_table_foreach_ordered
5485 (state->xl_styles,
5486 (GHFunc) odf_write_xl_style,
5487 by_value_str,
5488 state);
5490 gnm_hash_table_foreach_ordered
5491 (state->named_cell_style_regions,
5492 (GHFunc) odf_save_this_style_with_name,
5493 by_value_str,
5494 state);
5496 gnm_hash_table_foreach_ordered
5497 (state->font_sizes,
5498 (GHFunc) odf_write_font_sizes,
5499 by_key_str,
5500 state);
5502 gnm_hash_table_foreach_ordered
5503 (state->text_colours,
5504 (GHFunc) odf_write_text_colours,
5505 by_key_str,
5506 state);
5508 if (state->default_style_region->style != NULL) {
5509 gsf_xml_out_start_element (state->xml, STYLE "default-style");
5510 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "family", "table-cell");
5511 odf_write_style (state, state->default_style_region->style,
5512 &state->default_style_region->range, TRUE);
5513 gsf_xml_out_end_element (state->xml); /* </style:default-style */
5515 if (state->column_default != NULL) {
5516 gsf_xml_out_start_element (state->xml, STYLE "default-style");
5517 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "family", "table-column");
5518 odf_write_col_style (state, state->column_default);
5519 gsf_xml_out_end_element (state->xml); /* </style:default-style */
5521 if (state->row_default != NULL) {
5522 gsf_xml_out_start_element (state->xml, STYLE "default-style");
5523 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "family", "table-row");
5524 odf_write_row_style (state, state->row_default);
5525 gsf_xml_out_end_element (state->xml); /* </style:default-style */
5528 gnm_hash_table_foreach_ordered
5529 (state->graph_dashes,
5530 (GHFunc) odf_write_dash_info,
5531 by_key_str,
5532 state);
5534 gnm_hash_table_foreach_ordered
5535 (state->graph_hatches,
5536 (GHFunc) odf_write_hatch_info,
5537 by_value_str,
5538 state);
5540 gnm_hash_table_foreach_ordered
5541 (state->graph_gradients,
5542 (GHFunc) odf_write_gradient_info,
5543 by_value_str,
5544 state);
5546 gnm_hash_table_foreach_ordered
5547 (state->graph_fill_images,
5548 (GHFunc) odf_write_fill_images_info,
5549 by_value_str,
5550 state);
5552 gnm_hash_table_foreach_ordered
5553 (state->arrow_markers,
5554 (GHFunc) odf_write_arrow_marker_info,
5555 by_value_str,
5556 state);
5558 g_hash_table_remove_all (state->graph_dashes);
5559 g_hash_table_remove_all (state->graph_hatches);
5560 g_hash_table_remove_all (state->graph_gradients);
5561 g_hash_table_remove_all (state->graph_fill_images);
5562 g_hash_table_remove_all (state->arrow_markers);
5564 gsf_xml_out_end_element (state->xml); /* </office:styles> */
5567 static void
5568 odf_write_hf_style (GnmOOExport *state, GnmPrintInformation *pi, char const *id, gboolean header)
5570 GnmPrintHF *hf = header ? pi->header : pi->footer;
5571 double page_margin;
5572 double hf_height;
5573 GtkPageSetup *gps = gnm_print_info_get_page_setup (pi);
5575 if (hf == NULL)
5576 return;
5578 if (header) {
5579 page_margin = gtk_page_setup_get_top_margin (gps, GTK_UNIT_POINTS);
5580 hf_height = pi->edge_to_below_header - page_margin;
5581 } else {
5582 page_margin = gtk_page_setup_get_bottom_margin (gps, GTK_UNIT_POINTS);
5583 hf_height = pi->edge_to_above_footer - page_margin;
5586 gsf_xml_out_start_element (state->xml, id);
5587 gsf_xml_out_start_element (state->xml, STYLE "header-footer-properties");
5589 gsf_xml_out_add_cstr_unchecked (state->xml, FOSTYLE "border", "none");
5590 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "shadow", "none");
5591 odf_add_pt (state->xml, FOSTYLE "padding", 0.0);
5592 odf_add_pt (state->xml, FOSTYLE "margin", 0.0);
5593 odf_add_pt (state->xml, FOSTYLE "min-height", hf_height);
5594 odf_add_pt (state->xml, SVG "height", hf_height);
5595 odf_add_bool (state->xml, STYLE "dynamic-spacing", TRUE);
5597 gsf_xml_out_end_element (state->xml); /* header-footer-properties */
5598 gsf_xml_out_end_element (state->xml); /* id */
5602 static void
5603 odf_write_page_layout (GnmOOExport *state, GnmPrintInformation *pi,
5604 Sheet const *sheet)
5606 static char const *centre_type [] = {
5607 "none" ,
5608 "horizontal" ,
5609 "vertical" ,
5610 "both" ,
5611 NULL };
5613 char *name = page_layout_name (state, pi);
5614 GtkPageSetup *gps = gnm_print_info_get_page_setup (pi);
5615 int i;
5616 GtkPageOrientation orient = gtk_page_setup_get_orientation (gps);
5617 gboolean landscape = !(orient == GTK_PAGE_ORIENTATION_PORTRAIT ||
5618 orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT);
5619 GString *gstr = g_string_new ("charts drawings objects");
5621 gsf_xml_out_start_element (state->xml, STYLE "page-layout");
5622 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "name", name);
5623 g_free (name);
5624 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "page-usage", "all");
5626 gsf_xml_out_start_element (state->xml, STYLE "page-layout-properties");
5627 odf_add_pt (state->xml, FOSTYLE "margin-top",
5628 gtk_page_setup_get_top_margin (gps, GTK_UNIT_POINTS));
5629 odf_add_pt (state->xml, FOSTYLE "margin-bottom",
5630 gtk_page_setup_get_bottom_margin (gps, GTK_UNIT_POINTS));
5631 odf_add_pt (state->xml, FOSTYLE "margin-left",
5632 gtk_page_setup_get_left_margin (gps, GTK_UNIT_POINTS));
5633 odf_add_pt (state->xml, FOSTYLE "margin-right",
5634 gtk_page_setup_get_right_margin (gps, GTK_UNIT_POINTS));
5635 odf_add_pt (state->xml, FOSTYLE "page-width",
5636 gtk_page_setup_get_paper_width (gps, GTK_UNIT_POINTS));
5637 odf_add_pt (state->xml, FOSTYLE "page-height",
5638 gtk_page_setup_get_paper_height (gps, GTK_UNIT_POINTS));
5639 i = (pi->center_horizontally ? 1 : 0) | (pi->center_vertically ? 2 : 0);
5640 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "table-centering",
5641 centre_type [i]);
5642 gsf_xml_out_add_cstr_unchecked
5643 (state->xml, STYLE "print-page-order",
5644 pi->print_across_then_down ? "ltr" : "ttb");
5645 gsf_xml_out_add_cstr_unchecked
5646 (state->xml, STYLE "writing-mode",
5647 sheet->text_is_rtl ? "rl-tb" : "lr-tb");
5648 gsf_xml_out_add_cstr_unchecked
5649 (state->xml, STYLE "print-orientation",
5650 landscape ? "landscape" : "portrait");
5652 if (pi->print_grid_lines)
5653 g_string_append (gstr, " grid");
5654 if (pi->print_titles)
5655 g_string_append (gstr, " headers");
5656 if (pi->comment_placement != GNM_PRINT_COMMENTS_NONE)
5657 g_string_append (gstr, " annotations");
5658 gsf_xml_out_add_cstr_unchecked
5659 (state->xml, STYLE "print", gstr->str);
5661 switch (pi->scaling.type) {
5662 case PRINT_SCALE_FIT_PAGES: {
5663 int x = pi->scaling.dim.cols;
5664 int y = pi->scaling.dim.rows;
5665 if (state->with_extension) {
5666 /* LO uses style:scale-to-X and style:scale-to-Y but */
5667 /* these are not valid in the style: namespace */
5668 /* So to be understood by LO we would need to write */
5669 /* invalid ODF. They should be using one of their */
5670 /* extension namespace, but are not! */
5671 if (x > 0)
5672 gsf_xml_out_add_int (state->xml, GNMSTYLE "scale-to-X", x);
5673 if (y > 0)
5674 gsf_xml_out_add_int (state->xml, GNMSTYLE "scale-to-Y", y);
5675 } else {
5676 /* ODF 1.2 only allows us to specify the total number of pages. */
5677 int x = pi->scaling.dim.cols;
5678 int y = pi->scaling.dim.rows;
5679 if (x > 0 && y > 0)
5680 gsf_xml_out_add_int (state->xml, STYLE "scale-to-pages", x*y);
5682 break;
5684 case PRINT_SCALE_PERCENTAGE:
5685 odf_add_percent (state->xml, STYLE "scale-to", pi->scaling.percentage.x/100);
5686 break;
5687 default:
5688 odf_add_percent (state->xml, STYLE "scale-to", 1.);
5691 if (state->with_extension) {
5692 g_string_truncate (gstr, 0);
5693 if (pi->comment_placement == GNM_PRINT_COMMENTS_AT_END)
5694 g_string_append (gstr, " annotations_at_end");
5695 if (pi->print_black_and_white)
5696 g_string_append (gstr, " black_n_white");
5697 if (pi->print_as_draft)
5698 g_string_append (gstr, " draft");
5699 if (pi->print_even_if_only_styles)
5700 g_string_append (gstr, " print_even_if_only_styles");
5701 switch (pi->error_display) {
5702 case GNM_PRINT_ERRORS_AS_BLANK:
5703 g_string_append (gstr, " errors_as_blank");
5704 break;
5705 case GNM_PRINT_ERRORS_AS_DASHES:
5706 g_string_append (gstr, " errors_as_dashes");
5707 break;
5708 case GNM_PRINT_ERRORS_AS_NA:
5709 g_string_append (gstr, " errors_as_na");
5710 break;
5711 default:
5712 case GNM_PRINT_ERRORS_AS_DISPLAYED:
5713 break;
5715 gsf_xml_out_add_cstr_unchecked
5716 (state->xml, GNMSTYLE "style-print", gstr->str);
5719 g_string_free (gstr, TRUE);
5721 gsf_xml_out_end_element (state->xml); /* </style:page-layout-properties> */
5723 odf_write_hf_style (state, pi, STYLE "header-style", TRUE);
5724 odf_write_hf_style (state, pi, STYLE "footer-style", FALSE);
5727 gsf_xml_out_end_element (state->xml); /* </style:page-layout> */
5730 static void
5731 odf_write_automatic_styles (GnmOOExport *state)
5733 int i;
5735 gsf_xml_out_start_element (state->xml, OFFICE "automatic-styles");
5737 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
5738 Sheet const *sheet = workbook_sheet_by_index (state->wb, i);
5739 odf_write_page_layout (state, sheet->print_info, sheet);
5742 gsf_xml_out_end_element (state->xml); /* </office:automatic-styles> */
5745 static void
5746 odf_write_master_styles (GnmOOExport *state)
5748 int i;
5750 gsf_xml_out_start_element (state->xml, OFFICE "master-styles");
5752 for (i = 0; i < workbook_sheet_count (state->wb); i++) {
5753 Sheet const *sheet = workbook_sheet_by_index (state->wb, i);
5754 char *mp_name = table_master_page_style_name (state, sheet);
5755 char *name = page_layout_name (state, sheet->print_info);
5757 gsf_xml_out_start_element (state->xml, STYLE "master-page");
5758 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "name", mp_name);
5759 gsf_xml_out_add_cstr (state->xml, STYLE "display-name", sheet->name_unquoted);
5760 gsf_xml_out_add_cstr_unchecked (state->xml, STYLE "page-layout-name",
5761 name);
5763 odf_write_hf (state, sheet->print_info, STYLE "header", TRUE);
5764 odf_write_hf (state, sheet->print_info, STYLE "footer", FALSE);
5766 gsf_xml_out_end_element (state->xml); /* </master-page> */
5767 g_free (mp_name);
5768 g_free (name);
5771 gsf_xml_out_end_element (state->xml); /* </master-styles> */
5774 static void
5775 odf_write_styles (GnmOOExport *state, GsfOutput *child)
5777 int i;
5779 state->xml = create_new_xml_child (state, child);
5780 gsf_xml_out_start_element (state->xml, OFFICE "document-styles");
5781 for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
5782 gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
5783 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version",
5784 state->odf_version_string);
5786 odf_master_styles_to_xl_styles (state);
5788 odf_write_office_styles (state);
5789 odf_write_automatic_styles (state);
5790 odf_write_master_styles (state);
5792 gsf_xml_out_end_element (state->xml); /* </office:document-styles> */
5794 g_object_unref (state->xml);
5795 state->xml = NULL;
5798 /*****************************************************************************/
5800 static void
5801 odf_write_meta (GnmOOExport *state, GsfOutput *child)
5803 GsfXMLOut *xml = create_new_xml_child (state, child);
5804 GsfDocMetaData *meta = go_doc_get_meta_data (GO_DOC (state->wb));
5805 GValue *val = g_new0 (GValue, 1);
5806 GsfDocProp *prop = gsf_doc_meta_data_steal (meta, GSF_META_NAME_GENERATOR);
5808 g_value_init (val, G_TYPE_STRING);
5809 g_value_set_string (val, PACKAGE_NAME "/" VERSION);
5811 gsf_doc_meta_data_insert (meta, g_strdup (GSF_META_NAME_GENERATOR), val);
5812 gsf_doc_meta_data_write_to_odf (meta, xml);
5813 gsf_doc_meta_data_remove (meta,GSF_META_NAME_GENERATOR);
5814 if (prop != NULL)
5815 gsf_doc_meta_data_store (meta, prop);
5816 g_object_unref (xml);
5819 static void
5820 odf_write_meta_graph (G_GNUC_UNUSED GnmOOExport *state, GsfOutput *child)
5822 GsfXMLOut *xml = create_new_xml_child (state, child);
5823 GsfDocMetaData *meta = gsf_doc_meta_data_new ();
5824 GValue *val = g_new0 (GValue, 1);
5826 g_value_init (val, G_TYPE_STRING);
5827 g_value_set_string (val, PACKAGE_NAME "/" VERSION);
5829 gsf_doc_meta_data_insert (meta, g_strdup (GSF_META_NAME_GENERATOR), val);
5830 gsf_doc_meta_data_write_to_odf (meta, xml);
5832 g_object_unref (meta);
5833 g_object_unref (xml);
5835 /*****************************************************************************/
5837 static void
5838 odf_write_fill_images_info (GOImage *image, char const *name, GnmOOExport *state)
5840 char const *display_name = go_image_get_name (image);
5841 char *href = g_strdup_printf ("Pictures/%s.png", name);
5843 gsf_xml_out_start_element (state->xml, DRAW "fill-image");
5844 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "name", name);
5845 gsf_xml_out_add_cstr (state->xml, DRAW "display-name", display_name);
5846 gsf_xml_out_add_cstr_unchecked (state->xml, XLINK "type", "simple");
5847 gsf_xml_out_add_cstr_unchecked (state->xml, XLINK "show", "embed");
5848 gsf_xml_out_add_cstr_unchecked (state->xml, XLINK "actuate", "onLoad");
5849 gsf_xml_out_add_cstr (state->xml, XLINK "href", href);
5850 gsf_xml_out_end_element (state->xml); /* </draw:fill-image> */
5852 g_free (href);
5855 static void
5856 odf_write_arrow_marker_info (GOArrow const *arrow, char const *name, GnmOOExport *state)
5858 gsf_xml_out_start_element (state->xml, DRAW "marker");
5859 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "name", name);
5861 if (state->with_extension) {
5862 gsf_xml_out_add_int (state->xml, GNMSTYLE "arrow-type", arrow->typ);
5863 go_xml_out_add_double (state->xml, GNMSTYLE "arrow-a", arrow->a);
5864 go_xml_out_add_double (state->xml, GNMSTYLE "arrow-b", arrow->b);
5865 go_xml_out_add_double (state->xml, GNMSTYLE "arrow-c", arrow->c);
5868 gsf_xml_out_add_cstr (state->xml, SVG "viewBox", "0 0 20 30");
5869 gsf_xml_out_add_cstr (state->xml, SVG "d", "m10 0-10 30h20z");
5871 gsf_xml_out_end_element (state->xml); /* </draw:marker> */
5874 static void
5875 odf_write_gradient_info (GOStyle const *style, char const *name, GnmOOExport *state)
5877 char *color;
5878 char const *type = "linear";
5879 int angle = 0;
5880 struct {
5881 unsigned int dir;
5882 char const *type;
5883 int angle;
5884 } gradients[] = {
5885 {GO_GRADIENT_N_TO_S,"linear", 180},
5886 {GO_GRADIENT_S_TO_N, "linear", 0},
5887 {GO_GRADIENT_N_TO_S_MIRRORED, "axial", 180},
5888 {GO_GRADIENT_S_TO_N_MIRRORED, "axial", 0},
5889 {GO_GRADIENT_W_TO_E, "linear", 270},
5890 {GO_GRADIENT_E_TO_W, "linear", 90},
5891 {GO_GRADIENT_W_TO_E_MIRRORED, "axial", 270},
5892 {GO_GRADIENT_E_TO_W_MIRRORED, "axial", 90},
5893 {GO_GRADIENT_NW_TO_SE, "linear", 225},
5894 {GO_GRADIENT_SE_TO_NW, "linear", 45},
5895 {GO_GRADIENT_NW_TO_SE_MIRRORED, "axial", 225},
5896 {GO_GRADIENT_SE_TO_NW_MIRRORED, "axial", 45},
5897 {GO_GRADIENT_NE_TO_SW, "linear", 135},
5898 {GO_GRADIENT_SW_TO_NE, "linear", 315},
5899 {GO_GRADIENT_SW_TO_NE_MIRRORED, "axial", 315},
5900 {GO_GRADIENT_NE_TO_SW_MIRRORED, "axial", 135},
5902 int i;
5904 gsf_xml_out_start_element (state->xml, DRAW "gradient");
5905 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "name", name);
5907 color = odf_go_color_to_string (style->fill.pattern.back);
5908 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "start-color", color);
5909 g_free (color);
5911 if (style->fill.gradient.brightness >= 0.0 && state->with_extension)
5912 go_xml_out_add_double (state->xml, GNMSTYLE "brightness",
5913 style->fill.gradient.brightness);
5915 color = odf_go_color_to_string (style->fill.pattern.fore);
5916 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "end-color", color);
5917 g_free (color);
5919 for (i = 0; i < (int)G_N_ELEMENTS (gradients); i++) {
5920 if (gradients[i].dir == style->fill.gradient.dir) {
5921 type = gradients[i].type;
5922 angle = gradients[i].angle;
5923 break;
5926 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "style", type);
5927 gsf_xml_out_add_int (state->xml, DRAW "angle", angle);
5929 gsf_xml_out_end_element (state->xml); /* </draw:gradient> */
5932 static void
5933 odf_write_hatch_info (GOPattern *pattern, char const *name, GnmOOExport *state)
5935 struct {
5936 unsigned int type;
5937 char const *style;
5938 int angle;
5939 double distance;
5940 } info[] = {
5941 {GO_PATTERN_GREY75, "double", 0, 1.0},
5942 {GO_PATTERN_GREY50, "double", 0, 2.0},
5943 {GO_PATTERN_GREY25, "double", 0, 3.0},
5944 {GO_PATTERN_GREY125, "double", 0, 4.0},
5945 {GO_PATTERN_GREY625, "double", 0, 5.0},
5946 {GO_PATTERN_HORIZ, "single", 0, 2.0},
5947 {GO_PATTERN_VERT, "single", 90, 2.0},
5948 {GO_PATTERN_REV_DIAG, "single", -45, 2.0},
5949 {GO_PATTERN_DIAG, "single", 45, 2.0},
5950 {GO_PATTERN_DIAG_CROSS, "double", 45, 2.0},
5951 {GO_PATTERN_THICK_DIAG_CROSS, "double", 45, 1.0},
5952 {GO_PATTERN_THIN_HORIZ, "single", 0, 3.0},
5953 {GO_PATTERN_THIN_VERT, "single", 90, 3.0},
5954 {GO_PATTERN_THIN_REV_DIAG, "single", -45, 3.0},
5955 {GO_PATTERN_THIN_DIAG, "single", 45, 3.0},
5956 {GO_PATTERN_THIN_HORIZ_CROSS, "double", 0, 3.0},
5957 {GO_PATTERN_THIN_DIAG_CROSS, "double", 45, 3.0},
5958 {GO_PATTERN_SMALL_CIRCLES, "triple", 0, 2.0},
5959 {GO_PATTERN_SEMI_CIRCLES, "triple", 45, 2.0},
5960 {GO_PATTERN_THATCH, "triple", 90, 2.0},
5961 {GO_PATTERN_LARGE_CIRCLES, "triple", 0, 3.0},
5962 {GO_PATTERN_BRICKS, "triple", 45, 3.0},
5963 {GO_PATTERN_MAX, "single", 0, 2.0}
5965 char *color = odf_go_color_to_string (pattern->fore);
5966 int i;
5968 gsf_xml_out_start_element (state->xml, DRAW "hatch");
5969 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "name", name);
5970 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "display-name", name);
5971 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "color", color);
5972 g_free (color);
5974 for (i = 0; info[i].type != GO_PATTERN_MAX; i++)
5975 if (info[i].type == pattern->pattern)
5976 break;
5978 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "style",
5979 info[i].style);
5980 odf_add_angle (state->xml, DRAW "rotation", info[i].angle);
5981 odf_add_pt (state->xml, DRAW "distance", info[i].distance);
5983 gsf_xml_out_end_element (state->xml); /* </draw:hatch> */
5986 static void
5987 odf_write_dash_info (char const *name, gpointer data, GnmOOExport *state)
5989 GOLineDashType type = GPOINTER_TO_INT (data);
5990 GOLineDashSequence *lds;
5991 double scale;
5992 gboolean new = (state->odf_version > 101);
5994 gsf_xml_out_start_element (state->xml, DRAW "stroke-dash");
5995 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "name", name);
5996 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "display-name",
5997 go_line_dash_as_label (type));
5998 gsf_xml_out_add_cstr_unchecked (state->xml, DRAW "style", "rect");
6000 scale = new ? 1 : 0.5;
6002 lds = go_line_dash_get_sequence (type, scale);
6003 if (lds != NULL) {
6004 double dot_1 = lds->dash [0];
6005 guint n_1 = 1;
6006 guint i = 2;
6008 if (new)
6009 odf_add_percent (state->xml, DRAW "distance",
6010 (lds->n_dash > 1) ? lds->dash[1] : 1.);
6011 else
6012 odf_add_pt (state->xml, DRAW "distance",
6013 (lds->n_dash > 1) ? lds->dash[1] : 1.);
6015 for (; lds->n_dash > i && lds->dash[i] == dot_1; i += 2);
6016 gsf_xml_out_add_int (state->xml, DRAW "dots1", n_1);
6017 if (dot_1 == 0.)
6018 dot_1 = scale * 0.2;
6019 if (new)
6020 odf_add_percent (state->xml, DRAW "dots1-length", dot_1);
6021 else
6022 odf_add_pt (state->xml, DRAW "dots1-length", dot_1);
6023 if (lds->n_dash > i) {
6024 dot_1 = lds->dash [i];
6025 n_1 = 1;
6026 for (i += 2; lds->n_dash > i
6027 && lds->dash[i] == dot_1; i += 2);
6028 gsf_xml_out_add_int (state->xml, DRAW "dots2", n_1);
6029 if (dot_1 == 0.)
6030 dot_1 = scale * 0.2;
6031 if (new)
6032 odf_add_percent (state->xml, DRAW "dots2-length",
6033 dot_1);
6034 else
6035 odf_add_pt (state->xml, DRAW "dots2-length",
6036 dot_1);
6040 gsf_xml_out_end_element (state->xml); /* </draw:stroke-dash> */
6042 go_line_dash_sequence_free (lds);
6045 static void
6046 odf_write_graph_styles (GnmOOExport *state, GsfOutput *child)
6048 int i;
6050 state->xml = create_new_xml_child (state, child);
6051 gsf_xml_out_start_element (state->xml, OFFICE "document-styles");
6052 for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
6053 gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
6054 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version",
6055 state->odf_version_string);
6056 gsf_xml_out_start_element (state->xml, OFFICE "styles");
6058 gnm_hash_table_foreach_ordered
6059 (state->graph_dashes,
6060 (GHFunc) odf_write_dash_info,
6061 by_key_str,
6062 state);
6064 gnm_hash_table_foreach_ordered
6065 (state->graph_hatches,
6066 (GHFunc) odf_write_hatch_info,
6067 by_value_str,
6068 state);
6070 gnm_hash_table_foreach_ordered
6071 (state->graph_gradients,
6072 (GHFunc) odf_write_gradient_info,
6073 by_value_str,
6074 state);
6076 gnm_hash_table_foreach_ordered
6077 (state->graph_fill_images,
6078 (GHFunc) odf_write_fill_images_info,
6079 by_value_str,
6080 state);
6082 gnm_hash_table_foreach_ordered
6083 (state->xl_styles,
6084 (GHFunc) odf_write_xl_style,
6085 by_value_str,
6086 state);
6088 gsf_xml_out_end_element (state->xml); /* </office:styles> */
6089 gsf_xml_out_end_element (state->xml); /* </office:document-styles> */
6091 g_object_unref (state->xml);
6092 state->xml = NULL;
6095 /*****************************************************************************/
6097 static void
6098 odf_write_gnm_settings (GnmOOExport *state)
6100 gsf_xml_out_start_element (state->xml, CONFIG "config-item-set");
6101 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", GNMSTYLE "settings");
6102 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6103 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", GNMSTYLE "has_foreign");
6104 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "boolean");
6105 odf_add_bool (state->xml, NULL, state->with_extension);
6106 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6108 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6109 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", GNMSTYLE "active-sheet");
6110 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "string");
6111 gsf_xml_out_add_cstr (state->xml, NULL,
6112 (wb_view_cur_sheet (state->wbv))->name_unquoted);
6113 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6115 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6116 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", GNMSTYLE "geometry-width");
6117 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6118 gsf_xml_out_add_int (state->xml, NULL,
6119 state->wbv->preferred_width);
6120 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6122 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6123 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", GNMSTYLE "geometry-height");
6124 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6125 gsf_xml_out_add_int (state->xml, NULL,
6126 state->wbv->preferred_height);
6127 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6129 gsf_xml_out_end_element (state->xml); /* </config:config-item-set> */
6132 static void
6133 odf_write_ooo_settings (GnmOOExport *state)
6135 GSList *l, *sheets;
6137 gsf_xml_out_start_element (state->xml, CONFIG "config-item-set");
6138 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", OOO "view-settings");
6139 gsf_xml_out_start_element (state->xml, CONFIG "config-item-map-indexed");
6140 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "Views");
6141 gsf_xml_out_start_element (state->xml, CONFIG "config-item-map-entry");
6142 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6143 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "ViewId");
6144 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "string");
6145 gsf_xml_out_add_cstr (state->xml, NULL, "View1");
6146 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6147 gsf_xml_out_start_element (state->xml,
6148 CONFIG "config-item-map-named");
6149 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name",
6150 "Tables");
6152 sheets = workbook_sheets (state->wb);
6153 for (l = sheets; l != NULL; l = l->next) {
6154 Sheet *sheet = l->data;
6155 SheetView *sv = sheet_get_view (sheet, state->wbv);
6156 gsf_xml_out_start_element (state->xml, CONFIG "config-item-map-entry");
6157 gsf_xml_out_add_cstr (state->xml, CONFIG "name", sheet->name_unquoted);
6158 if (state->odf_version < 103 && sheet->tab_color != NULL
6159 && !sheet->tab_color->is_auto) {
6160 /* Not used by LO 3.3.3 and later */
6161 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6162 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "TabColor");
6163 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6164 gsf_xml_out_add_int (state->xml, NULL, sheet->tab_color->go_color >> 8);
6165 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6167 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6168 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "CursorPositionX");
6169 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6170 gsf_xml_out_add_int (state->xml, NULL, sv->edit_pos.col);
6171 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6172 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6173 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "CursorPositionY");
6174 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6175 gsf_xml_out_add_int (state->xml, NULL, sv->edit_pos.row);
6176 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6178 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6179 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "ZoomValue");
6180 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6181 gsf_xml_out_add_int (state->xml, NULL, (int) gnm_floor (sheet->last_zoom_factor_used * 100. + 0.5));
6182 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6184 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6185 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "ShowGrid");
6186 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "boolean");
6187 odf_add_bool (state->xml, NULL, !sheet->hide_grid);
6188 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6190 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6191 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "HasColumnRowHeaders");
6192 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "boolean");
6193 odf_add_bool (state->xml, NULL,
6194 (!sheet->hide_col_header) || !sheet->hide_row_header);
6195 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6197 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6198 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "ShowZeroValues");
6199 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "boolean");
6200 odf_add_bool (state->xml, NULL, !sheet->hide_zero);
6201 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6203 if (sv_is_frozen (sv)) {
6204 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6205 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "HorizontalSplitMode");
6206 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "short");
6207 gsf_xml_out_add_int (state->xml, NULL, 2);
6208 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6209 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6210 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "VerticalSplitMode");
6211 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "short");
6212 gsf_xml_out_add_int (state->xml, NULL, 2);
6213 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6214 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6215 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "HorizontalSplitPosition");
6216 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6217 gsf_xml_out_add_int (state->xml, NULL, sv->unfrozen_top_left.col);
6218 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6219 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6220 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "VerticalSplitPosition");
6221 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6222 gsf_xml_out_add_int (state->xml, NULL, sv->unfrozen_top_left.row);
6223 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6224 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6225 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionLeft");
6226 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6227 gsf_xml_out_add_int (state->xml, NULL, 0);
6228 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6229 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6230 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionRight");
6231 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6232 gsf_xml_out_add_int (state->xml, NULL, sv->initial_top_left.col);
6233 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6234 } else {
6235 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6236 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionLeft");
6237 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6238 gsf_xml_out_add_int (state->xml, NULL, sv->initial_top_left.col);
6239 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6240 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6241 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionRight");
6242 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6243 gsf_xml_out_add_int (state->xml, NULL, 0);
6244 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6246 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6247 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionTop");
6248 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6249 gsf_xml_out_add_int (state->xml, NULL, 0);
6250 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6251 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6252 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "PositionBottom");
6253 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "int");
6254 gsf_xml_out_add_int (state->xml, NULL, sv->initial_top_left.row);
6255 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6257 gsf_xml_out_end_element (state->xml); /* </config:config-item-map-entry> */
6259 g_slist_free (sheets);
6261 gsf_xml_out_end_element (state->xml); /* </config:config-item-map-named> */
6263 gsf_xml_out_start_element (state->xml, CONFIG "config-item");
6264 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "name", "ActiveTable");
6265 gsf_xml_out_add_cstr_unchecked (state->xml, CONFIG "type", "string");
6266 gsf_xml_out_add_cstr (state->xml, NULL,
6267 (wb_view_cur_sheet (state->wbv))->name_unquoted);
6268 gsf_xml_out_end_element (state->xml); /* </config:config-item> */
6270 gsf_xml_out_end_element (state->xml); /* </config:config-item-map-entry> */
6271 gsf_xml_out_end_element (state->xml); /* </config:config-item-map-indexed> */
6272 gsf_xml_out_end_element (state->xml); /* </config:config-item-set> */
6275 static void
6276 odf_write_settings (GnmOOExport *state, GsfOutput *child)
6278 int i;
6280 state->xml = create_new_xml_child (state, child);
6281 gsf_xml_out_start_element (state->xml, OFFICE "document-settings");
6282 for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
6283 gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
6284 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version",
6285 state->odf_version_string);
6287 gsf_xml_out_start_element (state->xml, OFFICE "settings");
6289 odf_write_gnm_settings (state);
6290 odf_write_ooo_settings (state);
6292 gsf_xml_out_end_element (state->xml); /* </office:settings> */
6293 gsf_xml_out_end_element (state->xml); /* </office:document-settings> */
6294 g_object_unref (state->xml);
6295 state->xml = NULL;
6298 /**********************************************************************************/
6300 static void
6301 odf_file_entry (GsfXMLOut *out, char const *type, char const *name)
6303 gsf_xml_out_start_element (out, MANIFEST "file-entry");
6304 gsf_xml_out_add_cstr (out, MANIFEST "media-type", type);
6305 gsf_xml_out_add_cstr (out, MANIFEST "full-path", name);
6306 gsf_xml_out_end_element (out); /* </manifest:file-entry> */
6309 static void
6310 odf_write_graph_manifest (SheetObject *graph, char const *name, GnmOOExport *state)
6312 guint i, n = odf_n_charts (state, graph);
6314 for (i = 0; i < n; i++) {
6315 char *realname = g_strdup_printf ("%s-%i", name, i);
6316 char *fullname = g_strdup_printf ("%s/", realname);
6317 odf_file_entry (state->xml, "application/vnd.oasis.opendocument.chart", fullname);
6318 g_free(fullname);
6319 fullname = g_strdup_printf ("%s/content.xml", realname);
6320 odf_file_entry (state->xml, "text/xml", fullname);
6321 g_free(fullname);
6322 fullname = g_strdup_printf ("%s/meta.xml", realname);
6323 odf_file_entry (state->xml, "text/xml", fullname);
6324 g_free(fullname);
6325 fullname = g_strdup_printf ("%s/styles.xml", realname);
6326 odf_file_entry (state->xml, "text/xml", fullname);
6327 g_free(fullname);
6328 fullname = g_strdup_printf ("Pictures/%s", realname);
6329 odf_file_entry (state->xml, "image/svg+xml", fullname);
6330 g_free(fullname);
6331 fullname = g_strdup_printf ("Pictures/%s.png", realname);
6332 odf_file_entry (state->xml, "image/png", fullname);
6333 g_free(fullname);
6334 g_free(realname);
6338 static void
6339 odf_write_image_manifest (SheetObject *image, char const *name, GnmOOExport *state)
6341 char *image_type;
6342 char *fullname;
6343 char *mime;
6345 g_object_get (G_OBJECT (image), "image-type", &image_type, NULL);
6346 mime = g_strdup_printf ("image/%s", image_type);
6347 fullname = g_strdup_printf ("Pictures/%s.%s", name, image_type);
6348 odf_file_entry (state->xml, mime, fullname);
6350 g_free (mime);
6351 g_free(fullname);
6352 g_free (image_type);
6356 static void
6357 odf_write_manifest (GnmOOExport *state, GsfOutput *child)
6359 GsfXMLOut *xml = create_new_xml_child (state, child);
6360 GSList *l;
6362 gsf_xml_out_set_doc_type (xml, "\n");
6363 gsf_xml_out_start_element (xml, MANIFEST "manifest");
6364 gsf_xml_out_add_cstr_unchecked (xml, "xmlns:manifest",
6365 "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
6366 if (state->odf_version > 101)
6367 gsf_xml_out_add_cstr_unchecked (xml, MANIFEST "version",
6368 state->odf_version_string);
6369 odf_file_entry (xml, "application/vnd.oasis.opendocument.spreadsheet" ,"/");
6370 odf_file_entry (xml, "text/xml", "content.xml");
6371 odf_file_entry (xml, "text/xml", "styles.xml");
6372 odf_file_entry (xml, "text/xml", "meta.xml");
6373 odf_file_entry (xml, "text/xml", "settings.xml");
6375 state->xml = xml;
6376 gnm_hash_table_foreach_ordered
6377 (state->graphs,
6378 (GHFunc) odf_write_graph_manifest,
6379 by_value_str,
6380 state);
6381 gnm_hash_table_foreach_ordered
6382 (state->images,
6383 (GHFunc) odf_write_image_manifest,
6384 by_value_str,
6385 state);
6387 for (l = state->fill_image_files; l != NULL; l = l->next)
6388 odf_file_entry (xml, "image/png", l->data);
6389 g_slist_free_full (state->fill_image_files, g_free);
6390 state->fill_image_files = NULL;
6392 state->xml = NULL;
6394 gsf_xml_out_end_element (xml); /* </manifest:manifest> */
6395 g_object_unref (xml);
6398 /**********************************************************************************/
6399 typedef enum {
6400 ODF_BARCOL,
6401 ODF_LINE,
6402 ODF_AREA,
6403 ODF_DROPBAR,
6404 ODF_MINMAX,
6405 ODF_CIRCLE,
6406 ODF_RADAR,
6407 ODF_RADARAREA,
6408 ODF_RING,
6409 ODF_SCATTER,
6410 ODF_SURF,
6411 ODF_GNM_SURF,
6412 ODF_XYZ_SURF,
6413 ODF_XYZ_GNM_SURF,
6414 ODF_BUBBLE,
6415 ODF_SCATTER_COLOUR,
6416 ODF_POLAR,
6417 ODF_GNM_BOX
6418 } odf_chart_type_t;
6420 static void
6421 odf_write_label_cell_address (GnmOOExport *state, GOData const *dat)
6423 GnmExprTop const *texpr;
6425 if (dat == NULL)
6426 return;
6428 texpr = gnm_go_data_get_expr (dat);
6429 if (texpr != NULL) {
6430 char *str;
6431 GnmParsePos pp;
6432 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6433 str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6434 if (gnm_expr_top_is_rangeref (texpr))
6435 gsf_xml_out_add_cstr (state->xml, CHART "label-cell-address",
6436 odf_strip_brackets (str));
6437 else if (state->with_extension)
6438 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "label-cell-expression",
6439 odf_strip_brackets (str));
6440 g_free (str);
6444 static void
6445 odf_write_series_lines (GnmOOExport *state, GogObject const *series)
6447 GogObjectRole const *role = gog_object_find_role_by_name (series, "Series lines");
6449 if (role != NULL) {
6450 GSList *serieslines = gog_object_get_children (series, role);
6451 if (serieslines != NULL && serieslines->data != NULL) {
6452 GogObject *obj = GOG_OBJECT (serieslines->data);
6453 char *style = odf_get_gog_style_name_from_obj (state, obj);
6455 gsf_xml_out_start_element (state->xml, GNMSTYLE "serieslines");
6456 gsf_xml_out_add_cstr (state->xml, CHART "style-name", style);
6457 gsf_xml_out_end_element (state->xml); /* </gnm:serieslines> */
6459 g_free (style);
6461 g_slist_free (serieslines);
6465 static void
6466 odf_write_drop_line (GnmOOExport *state, GogObject const *series, char const *drop)
6468 GogObjectRole const *role = gog_object_find_role_by_name (series, drop);
6470 if (role != NULL) {
6471 GSList *drops = gog_object_get_children
6472 (series, role);
6473 if (drops != NULL && drops->data != NULL) {
6474 GogObject *obj = GOG_OBJECT (drops->data);
6475 char *style = odf_get_gog_style_name_from_obj (state, obj);
6477 gsf_xml_out_start_element (state->xml, GNMSTYLE "droplines");
6478 gsf_xml_out_add_cstr (state->xml, CHART "style-name", style);
6479 gsf_xml_out_end_element (state->xml); /* </gnm:droplines> */
6481 g_free (style);
6483 g_slist_free (drops);
6487 static void
6488 odf_write_data_element_range (GnmOOExport *state, GnmParsePos *pp, GnmExprTop const *texpr,
6489 char const *attribute, char const *gnm_attribute)
6491 char *str;
6493 switch (GNM_EXPR_GET_OPER (texpr->expr)) {
6494 case GNM_EXPR_OP_CONSTANT:
6495 if (VALUE_IS_CELLRANGE (texpr->expr->constant.value)) {
6496 str = gnm_expr_top_as_string (texpr, pp, state->conv);
6497 gsf_xml_out_add_cstr (state->xml, attribute,
6498 odf_strip_brackets (str));
6499 g_free (str);
6500 return;
6502 break;
6503 case GNM_EXPR_OP_SET: {
6504 int i;
6505 gboolean success = TRUE;
6506 GnmExpr const *expr = texpr->expr;
6507 GString *gstr = g_string_new (NULL);
6508 for (i = 0; i < expr->set.argc; i++) {
6509 GnmExpr const *expr_arg = expr->set.argv[i];
6510 if ((GNM_EXPR_GET_OPER (expr_arg) == GNM_EXPR_OP_CONSTANT &&
6511 VALUE_IS_CELLRANGE (expr_arg->constant.value)) ||
6512 (GNM_EXPR_GET_OPER (expr_arg) == GNM_EXPR_OP_CELLREF)) {
6513 char *str = gnm_expr_as_string (expr_arg, pp, state->conv);
6514 if (gstr->len > 0)
6515 g_string_append_c (gstr, ' ');
6516 g_string_append (gstr, odf_strip_brackets (str));
6517 g_free (str);
6518 } else
6519 success = FALSE;
6521 if (success) {
6522 gsf_xml_out_add_cstr (state->xml, attribute, gstr->str);
6523 g_string_free (gstr, TRUE);
6524 return;
6526 g_string_free (gstr, TRUE);
6527 break;
6529 case GNM_EXPR_OP_CELLREF:
6530 str = gnm_expr_top_as_string (texpr, pp, state->conv);
6531 gsf_xml_out_add_cstr (state->xml, attribute,
6532 odf_strip_brackets (str));
6533 g_free (str);
6534 return;
6535 default:
6536 break;
6539 /* ODF does not support anything but Gnumeric does */
6540 if (NULL != gnm_attribute) {
6541 str = gnm_expr_top_as_string (texpr, pp, state->conv);
6542 gsf_xml_out_add_cstr (state->xml, gnm_attribute, str);
6543 g_free (str);
6547 static gboolean
6548 odf_write_data_element (GnmOOExport *state, GOData const *data, GnmParsePos *pp,
6549 char const *element, char const *attribute, char const *gnm_attribute)
6551 GnmExprTop const *texpr = gnm_go_data_get_expr (data);
6553 if (NULL != texpr) {
6554 char *str = gnm_expr_top_as_string (texpr, pp, state->conv);
6555 gsf_xml_out_start_element (state->xml, element);
6556 odf_write_data_element_range (state, pp, texpr, attribute, gnm_attribute);
6557 g_free (str);
6558 return TRUE;
6560 return FALSE;
6563 static void
6564 odf_write_data_attribute (GnmOOExport *state, GOData const *data, GnmParsePos *pp,
6565 char const *attribute, char const *c_attribute)
6567 GnmExprTop const *texpr = gnm_go_data_get_expr (data);
6569 if (NULL != texpr) {
6570 if (state->with_extension) {
6571 char *str = gnm_expr_top_as_string (texpr, pp,
6572 state->conv);
6573 gsf_xml_out_add_cstr (state->xml, attribute,
6574 odf_strip_brackets (str));
6575 g_free (str);
6577 if (NULL != c_attribute) {
6578 GnmValue const *v = gnm_expr_top_get_constant (texpr);
6579 if (NULL != v && VALUE_IS_STRING (v))
6580 gsf_xml_out_add_cstr (state->xml, c_attribute,
6581 value_peek_string (v));
6582 if (NULL != v && VALUE_IS_FLOAT (v))
6583 go_xml_out_add_double (state->xml, c_attribute,
6584 value_get_as_float (v));
6589 static gint
6590 cmp_data_points (GObject *a, GObject *b)
6592 int ind_a = 0, ind_b = 0;
6594 g_object_get (a, "index", &ind_a, NULL);
6595 g_object_get (b, "index", &ind_b, NULL);
6597 if (ind_a < ind_b)
6598 return -1;
6599 else if (ind_a > ind_b)
6600 return 1;
6601 else return 0;
6604 static void
6605 odf_write_regression_curve (GnmOOExport *state, GogObjectRole const *role, GogObject const *series, GnmParsePos *pp)
6607 GSList *l, *regressions = gog_object_get_children
6608 (series, role);
6609 char *str;
6611 for (l = regressions; l != NULL && l->data != NULL; l = l->next) {
6612 GOData const *bd;
6613 GogObject const *regression = l->data;
6614 gboolean is_reg_curve = GOG_IS_REG_CURVE (regression);
6615 GogObject const *equation
6616 = is_reg_curve?
6617 gog_object_get_child_by_name (regression, "Equation"):
6618 NULL;
6619 str = odf_get_gog_style_name_from_obj
6620 (state, GOG_OBJECT (regression));
6621 gsf_xml_out_start_element
6622 (state->xml, CHART "regression-curve");
6623 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6624 g_free (str);
6626 if (is_reg_curve && state->with_extension) {
6627 /* Upper and lower bounds */
6628 bd = gog_dataset_get_dim (GOG_DATASET (regression), 0);
6629 if (bd != NULL)
6630 odf_write_data_attribute
6631 (state, bd, pp, GNMSTYLE "lower-bound", NULL);
6632 bd = gog_dataset_get_dim (GOG_DATASET (regression), 1);
6633 if (bd != NULL)
6634 odf_write_data_attribute
6635 (state, bd, pp, GNMSTYLE "upper-bound", NULL);
6637 if (equation != NULL) {
6638 char const *eq_element, *eq_automatic, *eq_display, *eq_r;
6639 if (state->odf_version > 101) {
6640 eq_element = CHART "equation";
6641 eq_automatic = CHART "automatic-content";
6642 eq_display = CHART "display-equation";
6643 eq_r = CHART "display-r-square";
6644 } else {
6645 eq_element = GNMSTYLE "equation";
6646 eq_automatic = GNMSTYLE "automatic-content";
6647 eq_display = GNMSTYLE "display-equation";
6648 eq_r = GNMSTYLE "display-r-square";
6650 gsf_xml_out_start_element
6651 (state->xml, eq_element);
6652 odf_add_bool (state->xml, eq_automatic, TRUE);
6653 odf_write_plot_style_bool (state->xml, equation,
6654 "show-eq", eq_display);
6655 odf_write_plot_style_bool (state->xml, equation,
6656 "show-r2", eq_r);
6657 str = odf_get_gog_style_name_from_obj
6658 (state, GOG_OBJECT (equation));
6659 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6660 g_free (str);
6661 odf_write_gog_position (state, equation);
6662 odf_write_gog_position_pts (state, equation);
6663 gsf_xml_out_end_element (state->xml); /* </chart:equation> */
6666 gsf_xml_out_end_element (state->xml); /* </chart:regression-curve> */
6668 g_slist_free (regressions);
6671 static void
6672 odf_write_attached_axis (GnmOOExport *state, char const * const axis_role, int id)
6674 GString *str = g_string_new (NULL);
6675 g_string_append_printf (str, "%s-%i", axis_role, id);
6676 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "attached-axis", str->str);
6677 g_string_free (str, TRUE);
6680 static void
6681 odf_write_attached_axes (GnmOOExport *state, GogObject *series)
6683 GogPlot *plot = gog_series_get_plot (GOG_SERIES (series));
6684 GogAxis *axis = gog_plot_get_axis (plot, GOG_AXIS_X);
6685 int id;
6687 id = (NULL != axis) ? gog_object_get_id (GOG_OBJECT (axis)) : 0;
6688 if (id > 1)
6689 odf_write_attached_axis (state, "X-Axis", id);
6690 else {
6691 axis = gog_plot_get_axis (plot, GOG_AXIS_Z);
6692 id = (NULL != axis) ? gog_object_get_id (GOG_OBJECT (axis)) : 0;
6693 if (id > 1)
6694 odf_write_attached_axis (state, "Z-Axis", id);
6695 else {
6696 axis = gog_plot_get_axis (plot, GOG_AXIS_Y);
6697 if (NULL != axis) {
6698 id = gog_object_get_id (GOG_OBJECT (axis));
6699 odf_write_attached_axis (state, "Y-Axis", id);
6706 static void
6707 odf_write_standard_series (GnmOOExport *state, GSList const *series, char const* class)
6709 GnmParsePos pp;
6710 int i;
6711 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6713 for (i = 1; NULL != series ; series = series->next, i++) {
6714 GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_VALUES);
6715 if (NULL != dat && odf_write_data_element (state, dat, &pp, CHART "series",
6716 CHART "values-cell-range-address",
6717 GNMSTYLE "values-cell-range-expression")) {
6718 GogObjectRole const *role;
6719 GSList *points;
6720 GOData const *cat = gog_dataset_get_dim (GOG_DATASET (series->data),
6721 GOG_MS_DIM_LABELS);
6722 char *str = odf_get_gog_style_name_from_obj (state, series->data);
6724 odf_write_attached_axes (state, series->data);
6726 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6727 g_free (str);
6729 odf_write_label_cell_address
6730 (state, gog_series_get_name (GOG_SERIES (series->data)));
6732 if (NULL != class)
6733 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "class", class);
6735 if (NULL != cat && odf_write_data_element (state, cat, &pp, CHART "domain",
6736 TABLE "cell-range-address",
6737 GNMSTYLE "cell-range-expression"))
6738 gsf_xml_out_end_element (state->xml); /* </chart:domain> */
6740 role = gog_object_find_role_by_name
6741 (GOG_OBJECT (series->data), "Regression curve");
6742 if (role != NULL)
6743 odf_write_regression_curve (state, role, GOG_OBJECT (series->data), &pp);
6745 role = gog_object_find_role_by_name
6746 (GOG_OBJECT (series->data), "Trend line");
6747 if (role != NULL)
6748 odf_write_regression_curve (state, role, GOG_OBJECT (series->data), &pp);
6750 /* Write data points if any */
6752 role = gog_object_find_role_by_name
6753 (GOG_OBJECT (series->data), "Point");
6754 if (role != NULL && NULL != (points = gog_object_get_children
6755 (GOG_OBJECT (series->data), role))) {
6756 int index = 0, next_index = 0;
6757 GSList *l;
6758 points = g_slist_sort (points, (GCompareFunc) cmp_data_points);
6760 for (l = points; l != NULL; l = l->next) {
6761 char *style = odf_get_gog_style_name_from_obj
6762 (state, GOG_OBJECT (l->data));
6763 g_object_get (G_OBJECT (l->data), "index", &index, NULL);
6764 if (index > next_index) {
6765 gsf_xml_out_start_element (state->xml,
6766 CHART "data-point");
6767 gsf_xml_out_add_int (state->xml, CHART "repeated",
6768 index - next_index);
6769 gsf_xml_out_end_element (state->xml);
6770 /* CHART "data-point" */
6772 gsf_xml_out_start_element (state->xml,
6773 CHART "data-point");
6774 gsf_xml_out_add_cstr (state->xml, CHART "style-name", style);
6775 gsf_xml_out_end_element (state->xml);
6776 /* CHART "data-point" */
6777 g_free (style);
6778 next_index = index + 1;
6780 g_slist_free (points);
6783 if (state->with_extension) {
6784 odf_write_drop_line (state, GOG_OBJECT (series->data),
6785 "Horizontal drop lines");
6786 odf_write_drop_line (state, GOG_OBJECT (series->data),
6787 "Vertical drop lines");
6788 odf_write_drop_line (state, GOG_OBJECT (series->data),
6789 "Drop lines");
6790 odf_write_series_lines (state, GOG_OBJECT (series->data));
6792 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6797 static void
6798 odf_write_box_series (GnmOOExport *state, GSList const *series, char const* class)
6800 GnmParsePos pp;
6801 int i;
6802 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6804 for (i = 1; NULL != series ; series = series->next, i++) {
6805 GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), 0);
6807 if (NULL != dat) {
6808 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
6809 if (NULL != texpr) {
6810 char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6812 gsf_xml_out_start_element (state->xml, CHART "series");
6813 gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
6814 odf_strip_brackets (str));
6815 g_free (str);
6816 str = odf_get_gog_style_name_from_obj (state, series->data);
6817 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6818 g_free (str);
6819 odf_write_label_cell_address
6820 (state, gog_series_get_name (GOG_SERIES (series->data)));
6821 if (NULL != class)
6822 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "class", class);
6823 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6829 static void
6830 odf_write_gantt_series (GnmOOExport *state, GSList const *series, char const* class)
6832 GnmParsePos pp;
6833 int i;
6834 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6836 for (i = 1; NULL != series ; series = series->next, i++) {
6837 GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_VALUES);
6838 if (NULL != dat) {
6839 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
6840 if (NULL != texpr) {
6841 char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6842 GOData const *cat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_LABELS);
6843 gsf_xml_out_start_element (state->xml, CHART "series");
6844 gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
6845 odf_strip_brackets (str));
6846 g_free (str);
6847 str = odf_get_gog_style_name_from_obj (state, series->data);
6848 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6849 g_free (str);
6851 if (NULL != class)
6852 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "class", class);
6854 if (NULL != cat) {
6855 texpr = gnm_go_data_get_expr (cat);
6856 if (NULL != texpr) {
6857 str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6858 gsf_xml_out_start_element (state->xml, CHART "domain");
6859 gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address",
6860 odf_strip_brackets (str));
6861 gsf_xml_out_end_element (state->xml); /* </chart:domain> */
6862 g_free (str);
6865 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6868 dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_CATEGORIES);
6869 if (NULL != dat) {
6870 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
6871 if (NULL != texpr) {
6872 char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6873 gsf_xml_out_start_element (state->xml, CHART "series");
6874 gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
6875 odf_strip_brackets (str));
6876 g_free (str);
6877 str = odf_get_gog_style_name_from_obj (state, series->data);
6878 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6879 g_free (str);
6880 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6886 static void
6887 odf_write_bubble_series (GnmOOExport *state, GSList const *orig_series, char const* class)
6889 GnmParsePos pp;
6890 int i, j;
6891 GSList const *series;
6892 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6894 for (series = orig_series, i = 1; NULL != series; series = series->next, i++) {
6895 GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), 2);
6897 if (NULL != dat) {
6898 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
6899 if (NULL != texpr) {
6900 char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6901 gsf_xml_out_start_element (state->xml, CHART "series");
6902 gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
6903 odf_strip_brackets (str));
6904 g_free (str);
6905 str = odf_get_gog_style_name_from_obj (state, series->data);
6906 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6907 g_free (str);
6909 if (NULL != class)
6910 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "class", class);
6912 for (j = 1; j >= 0; j--) {
6913 dat = gog_dataset_get_dim (GOG_DATASET (series->data), j);
6914 if (NULL != dat) {
6915 texpr = gnm_go_data_get_expr (dat);
6916 if (NULL != texpr) {
6917 str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6918 gsf_xml_out_start_element (state->xml, CHART "domain");
6919 gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address",
6920 odf_strip_brackets (str));
6921 gsf_xml_out_end_element (state->xml); /* </chart:domain> */
6922 g_free (str);
6927 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6932 static void
6933 odf_write_min_max_series (GnmOOExport *state, GSList const *orig_series, char const* class)
6935 GnmParsePos pp;
6936 int i, j;
6937 GSList const *series;
6938 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
6940 for (j = 1; j < 3; j++) {
6941 gsf_xml_out_start_element (state->xml, CHART "series");
6942 for (series = orig_series, i = 1; NULL != series; series = series->next, i++) {
6943 GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), j);
6945 if (NULL != dat) {
6946 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
6947 if (NULL != texpr) {
6948 char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
6949 gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
6950 odf_strip_brackets (str));
6951 g_free (str);
6952 str = odf_get_gog_style_name_from_obj (state, series->data);
6953 gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
6954 g_free (str);
6955 break;
6958 if (NULL != class)
6959 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "class", class);
6961 gsf_xml_out_end_element (state->xml); /* </chart:series> */
6966 static void
6967 odf_write_fill_type (GnmOOExport *state,
6968 GogObject const *series)
6970 gchar *type_str = NULL;
6972 if (state->with_extension && gnm_object_has_readable_prop (series, "fill-type", G_TYPE_STRING, &type_str)) {
6973 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "fill-type", type_str);
6974 g_free (type_str);
6978 static void
6979 odf_write_interpolation_attribute (GnmOOExport *state,
6980 G_GNUC_UNUSED GOStyle const *style,
6981 GogObject const *series)
6983 gchar *interpolation = NULL;
6985 g_object_get (G_OBJECT (series),
6986 "interpolation", &interpolation,
6987 NULL);
6989 if (interpolation != NULL) {
6990 if (0 == strcmp (interpolation, "linear"))
6991 gsf_xml_out_add_cstr
6992 (state->xml, CHART "interpolation", "none");
6993 else if (0 == strcmp (interpolation, "spline"))
6994 gsf_xml_out_add_cstr
6995 (state->xml, CHART "interpolation",
6996 "cubic-spline");
6997 else if (0 == strcmp (interpolation, "odf-spline"))
6998 /* this one is really compatible with ODF */
6999 gsf_xml_out_add_cstr
7000 (state->xml, CHART "interpolation",
7001 "cubic-spline");
7002 else if (state->with_extension) {
7003 char *tag = g_strdup_printf ("gnm:%s", interpolation);
7004 gsf_xml_out_add_cstr
7005 (state->xml, GNMSTYLE "interpolation", tag);
7006 g_free (tag);
7007 } else
7008 gsf_xml_out_add_cstr
7009 (state->xml, CHART "interpolation", "none");
7012 if (state->with_extension) {
7013 gboolean skip_invalid = TRUE;
7015 if (!gnm_object_has_readable_prop (series,
7016 "interpolation-skip-invalid",
7017 G_TYPE_BOOLEAN,
7018 &skip_invalid) ||
7019 !skip_invalid)
7020 odf_add_bool (state->xml,
7021 GNMSTYLE "interpolation-skip-invalid",
7022 FALSE);
7025 g_free (interpolation);
7028 static int
7029 odf_scale_initial_angle (gnm_float angle)
7031 angle = 90 - angle;
7032 while (angle < 0)
7033 angle += 360.;
7034 angle = gnm_fake_round (angle);
7036 return (((int) angle) % 360);
7039 static void
7040 odf_write_plot_style (GnmOOExport *state, GogObject const *plot)
7042 gchar const *plot_type = G_OBJECT_TYPE_NAME (plot);
7043 gchar *type_str = NULL;
7044 double default_separation = 0.;
7045 double d;
7047 odf_add_bool (state->xml, CHART "auto-size", TRUE);
7049 if (gnm_object_has_readable_prop (plot, "type",
7050 G_TYPE_STRING, &type_str)) {
7051 if (type_str != NULL) {
7052 odf_add_bool (state->xml, CHART "stacked",
7053 (0== strcmp (type_str, "stacked")));
7054 odf_add_bool (state->xml, CHART "percentage",
7055 (0== strcmp (type_str, "as_percentage")));
7056 g_free (type_str);
7060 if (gnm_object_has_readable_prop (plot, "default-separation",
7061 G_TYPE_DOUBLE, &default_separation)) {
7062 if (0 == strcmp ("GogRingPlot", plot_type)) {
7063 if (state->with_extension)
7064 odf_add_percent (state->xml,
7065 GNMSTYLE "default-separation",
7066 default_separation);
7067 } else
7068 gsf_xml_out_add_int (state->xml,
7069 CHART "pie-offset",
7070 (default_separation * 100. + 0.5));
7073 /* Note: horizontal refers to the bars and vertical to the x-axis */
7074 odf_write_plot_style_bool (state->xml, plot,
7075 "horizontal", CHART "vertical");
7077 odf_write_plot_style_bool (state->xml, plot,
7078 "vertical", CHART "vertical");
7080 odf_write_plot_style_from_bool
7081 (state->xml, plot,
7082 "default-style-has-markers", CHART "symbol-type",
7083 "automatic", "none");
7085 odf_write_plot_style_int (state->xml, plot,
7086 "gap-percentage", CHART "gap-width");
7088 odf_write_plot_style_int (state->xml, plot,
7089 "overlap-percentage", CHART "overlap");
7091 odf_write_plot_style_double_percent (state->xml, plot,
7092 "center-size",
7093 CHART "hole-size");
7095 if (gnm_object_has_readable_prop (plot, "initial-angle", G_TYPE_DOUBLE, &d))
7096 gsf_xml_out_add_int (state->xml, CHART "angle-offset", odf_scale_initial_angle (d));
7098 if (gnm_object_has_readable_prop (plot, "interpolation",
7099 G_TYPE_NONE, NULL))
7100 odf_write_interpolation_attribute (state, NULL, plot);
7102 if (0 == strcmp ( "GogXYZSurfacePlot", plot_type) ||
7103 0 == strcmp ( "GogSurfacePlot", plot_type) ||
7104 0 == strcmp ( "XLSurfacePlot", plot_type))
7105 odf_add_bool (state->xml, CHART "three-dimensional", TRUE);
7106 else
7107 odf_add_bool (state->xml, CHART "three-dimensional", FALSE);
7109 odf_write_plot_style_bool (state->xml, plot, "default-style-has-lines", CHART "lines");
7111 if (state->with_extension) {
7112 if (0 == strcmp ( "XLSurfacePlot", plot_type))
7113 odf_add_bool (state->xml, GNMSTYLE "multi-series",
7114 TRUE);
7115 odf_write_plot_style_bool (state->xml, plot,
7116 "outliers", GNMSTYLE "outliers");
7118 odf_write_plot_style_double (state->xml, plot,
7119 "radius-ratio", GNMSTYLE
7120 "radius-ratio");
7122 odf_write_plot_style_bool (state->xml, plot,
7123 "vary-style-by-element",
7124 GNMSTYLE "vary-style-by-element");
7126 odf_write_plot_style_bool (state->xml, plot,
7127 "show-negatives",
7128 GNMSTYLE "show-negatives");
7134 static char const *
7135 odf_get_marker (GOMarkerShape m)
7137 static struct {
7138 guint m;
7139 char const *str;
7140 } marks [] =
7141 {{GO_MARKER_NONE, "none"},
7142 {GO_MARKER_SQUARE, "square"},
7143 {GO_MARKER_DIAMOND,"diamond"},
7144 {GO_MARKER_TRIANGLE_DOWN,"arrow-down"},
7145 {GO_MARKER_TRIANGLE_UP,"arrow-up"},
7146 {GO_MARKER_TRIANGLE_RIGHT,"arrow-right"},
7147 {GO_MARKER_TRIANGLE_LEFT,"arrow-left"},
7148 {GO_MARKER_CIRCLE,"circle"},
7149 {GO_MARKER_X,"x"},
7150 {GO_MARKER_CROSS,"plus"},
7151 {GO_MARKER_ASTERISK,"asterisk"},
7152 {GO_MARKER_BAR,"horizontal-bar"},
7153 {GO_MARKER_HALF_BAR,"vertical-bar"}, /* Not ODF */
7154 {GO_MARKER_BUTTERFLY,"bow-tie"},
7155 {GO_MARKER_HOURGLASS,"hourglass"},
7156 {GO_MARKER_LEFT_HALF_BAR,"star"},/* Not ODF */
7157 {GO_MARKER_MAX, "star"}, /* not used by us */
7158 {GO_MARKER_MAX + 1, "vertical-bar"},/* not used by us */
7159 {0, NULL}
7161 int i;
7162 for (i = 0; marks[i].str != NULL; i++)
7163 if (marks[i].m == m)
7164 return marks[i].str;
7165 return "diamond";
7168 static void
7169 odf_add_expr (GnmOOExport *state, GogObject const *obj, gint dim,
7170 char const *attribute, char const *c_attribute)
7172 GnmParsePos pp;
7173 GOData const *bd;
7174 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
7175 bd = gog_dataset_get_dim (GOG_DATASET (obj), dim);
7176 if (bd != NULL)
7177 odf_write_data_attribute
7178 (state, bd, &pp, attribute, c_attribute);
7181 static void
7182 odf_write_axis_position (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
7183 GogObject const *axis)
7185 char *pos_str = NULL;
7186 if (gnm_object_has_readable_prop (axis, "pos-str",
7187 G_TYPE_STRING, &pos_str)) {
7188 if (0 == strcmp (pos_str, "low"))
7189 gsf_xml_out_add_cstr (state->xml, CHART "axis-position", "start");
7190 else if (0 == strcmp (pos_str, "high"))
7191 gsf_xml_out_add_cstr (state->xml, CHART "axis-position", "end");
7192 else if (0 == strcmp (pos_str, "cross")) {
7193 GnmParsePos pp;
7194 GOData const *bd;
7195 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0, 0);
7196 bd = gog_dataset_get_dim (GOG_DATASET (axis), 4);
7197 if (bd != NULL)
7198 odf_write_data_attribute (state, bd, &pp,
7199 GNMSTYLE "axis-position-expression",
7200 CHART "axis-position");
7201 else
7202 gsf_xml_out_add_cstr (state->xml, CHART "axis-position", "0");
7204 g_free (pos_str);
7208 static void
7209 odf_write_axisline_style (GnmOOExport *state, GOStyle const *style,
7210 GogObject const *axis)
7212 odf_write_axis_position (state, style, axis);
7214 odf_write_plot_style_bool
7215 (state->xml, axis, "major-tick-in", CHART "tick-marks-major-inner");
7216 odf_write_plot_style_bool
7217 (state->xml, axis, "major-tick-out", CHART "tick-marks-major-outer");
7218 odf_write_plot_style_bool
7219 (state->xml, axis, "minor-tick-in", CHART "tick-marks-minor-inner");
7220 odf_write_plot_style_bool
7221 (state->xml, axis, "minor-tick-out", CHART "tick-marks-minor-outer");
7222 odf_write_plot_style_bool
7223 (state->xml, axis, "major-tick-labeled", CHART "display-label");
7226 static void
7227 odf_write_axis_style (GnmOOExport *state, GOStyle const *style,
7228 GogObject const *axis)
7230 double tmp;
7231 GOData const *interval;
7232 gboolean user_defined;
7233 gboolean logarithmic = FALSE;
7234 char *map_name_str = NULL;
7236 if (gnm_object_has_readable_prop (axis, "map-name",
7237 G_TYPE_STRING, &map_name_str)) {
7238 logarithmic = (0 != strcmp (map_name_str, "Linear"));
7239 odf_add_bool (state->xml, CHART "logarithmic", logarithmic);
7240 g_free (map_name_str);
7243 tmp = gog_axis_get_entry
7244 (GOG_AXIS (axis), GOG_AXIS_ELEM_MIN, &user_defined);
7245 if (user_defined) {
7246 go_xml_out_add_double (state->xml, CHART "minimum", tmp);
7247 if (state->with_extension)
7248 odf_add_expr (state, GOG_OBJECT (axis), 0,
7249 GNMSTYLE "chart-minimum-expression", NULL);
7251 tmp = gog_axis_get_entry
7252 (GOG_AXIS (axis), GOG_AXIS_ELEM_MAX, &user_defined);
7253 if (user_defined) {
7254 go_xml_out_add_double (state->xml, CHART "maximum", tmp);
7255 if (state->with_extension)
7256 odf_add_expr (state, GOG_OBJECT (axis), 1,
7257 GNMSTYLE "chart-maximum-expression", NULL);
7260 interval = gog_dataset_get_dim (GOG_DATASET(axis),2);
7261 if (interval != NULL) {
7262 GnmExprTop const *texpr
7263 = gnm_go_data_get_expr (interval);
7264 if (texpr != NULL &&
7265 GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_CONSTANT) {
7266 double val = value_get_as_float (texpr->expr->constant.value);
7267 go_xml_out_add_double (state->xml, CHART "interval-major", val);
7269 interval = gog_dataset_get_dim (GOG_DATASET(axis),3);
7270 if (interval != NULL) {
7271 texpr = gnm_go_data_get_expr (interval);
7272 if (texpr != NULL &&
7273 GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_CONSTANT) {
7274 double val_minor = value_get_as_float
7275 (texpr->expr->constant.value);
7276 if (val_minor > 0) {
7277 if (logarithmic)
7278 val_minor = gnm_floor(val_minor + 1.5);
7279 else
7280 val_minor = gnm_floor(val/val_minor + 0.5);
7281 gsf_xml_out_add_float
7282 (state->xml, CHART "interval-minor-divisor",
7283 val_minor, 0);
7289 if (state->odf_version > 101)
7290 odf_write_plot_style_bool
7291 (state->xml, axis,
7292 "invert-axis", CHART "reverse-direction");
7293 else if (state->with_extension)
7294 odf_write_plot_style_bool
7295 (state->xml, axis,
7296 "invert-axis", GNMSTYLE "reverse-direction");
7298 odf_write_axisline_style (state, style, axis);
7301 static void
7302 odf_write_generic_axis_style (GnmOOExport *state, char const *style_label)
7304 odf_start_style (state->xml, style_label, "chart");
7305 gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
7307 gsf_xml_out_add_cstr (state->xml, CHART "axis-position", "start");
7308 odf_add_bool (state->xml, CHART "display-label", TRUE);
7310 if (state->odf_version > 101)
7311 odf_add_bool (state->xml, CHART "reverse-direction", TRUE);
7312 gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
7313 gsf_xml_out_end_element (state->xml); /* </style:style> */
7316 static void
7317 odf_write_one_axis_grid (GnmOOExport *state, GogObject const *axis,
7318 char const *role, char const *class)
7320 GogObject const *grid;
7322 grid = gog_object_get_child_by_name (axis, role);
7323 if (grid) {
7324 char *style = odf_get_gog_style_name_from_obj (state, GOG_OBJECT (grid));
7326 gsf_xml_out_start_element (state->xml, CHART "grid");
7327 gsf_xml_out_add_cstr (state->xml, CHART "style-name", style);
7328 gsf_xml_out_add_cstr (state->xml, CHART "class", class);
7329 gsf_xml_out_end_element (state->xml); /* </chart:grid> */
7331 g_free (style);
7335 static void
7336 odf_write_axis_grid (GnmOOExport *state, GogObject const *axis)
7338 g_return_if_fail (axis != NULL);
7339 odf_write_one_axis_grid (state, axis, "MajorGrid", "major");
7340 odf_write_one_axis_grid (state, axis, "MinorGrid", "minor");
7343 static void
7344 odf_write_axislines (GnmOOExport *state, GogObject const *axis)
7346 g_return_if_fail (axis != NULL);
7348 if (state->with_extension) {
7349 GogObjectRole const *role;
7350 role = gog_object_find_role_by_name (axis, "AxisLine");
7351 if (role != NULL) {
7352 GSList *l, *lines = gog_object_get_children (axis, role);
7353 l = lines;
7354 while (l != NULL && l->data != NULL) {
7355 char *name = odf_get_gog_style_name_from_obj (state, GOG_OBJECT (l->data));
7356 gsf_xml_out_start_element (state->xml, GNMSTYLE "axisline");
7357 if (name != NULL)
7358 gsf_xml_out_add_cstr (state->xml, CHART "style-name", name);
7359 gsf_xml_out_end_element (state->xml); /* </gnm:axisline> */
7360 g_free (name);
7361 l = l->next;
7363 g_slist_free (lines);
7369 static void
7370 odf_write_title (GnmOOExport *state, GogObject const *title,
7371 char const *id, gboolean allow_content)
7373 if (title != NULL && id != NULL) {
7374 GOData const *dat = gog_dataset_get_dim (GOG_DATASET(title),0);
7376 if (dat != NULL) {
7377 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
7378 if (texpr != NULL) {
7379 GnmParsePos ppos;
7380 char *formula;
7381 char *name;
7382 gboolean pp = TRUE;
7383 GnmValue const *v;
7385 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
7387 gsf_xml_out_start_element (state->xml, id);
7389 odf_write_gog_position (state, title);
7390 odf_write_gog_position_pts (state, title);
7392 name = odf_get_gog_style_name_from_obj (state, title);
7394 if (name != NULL) {
7395 gsf_xml_out_add_cstr (state->xml, CHART "style-name",
7396 name);
7397 g_free (name);
7400 parse_pos_init (&ppos, WORKBOOK (state->wb), NULL, 0,0 );
7401 formula = gnm_expr_top_as_string (texpr, &ppos, state->conv);
7403 if (gnm_expr_top_is_rangeref (texpr)) {
7404 char *f = odf_strip_brackets (formula);
7405 gsf_xml_out_add_cstr (state->xml,
7406 TABLE "cell-range", f);
7407 } else if (allow_content &&
7408 (v = gnm_expr_top_get_constant (texpr)) &&
7409 VALUE_IS_STRING (v)) {
7410 gboolean white_written = TRUE;
7411 char const *str;
7412 GogText *text;
7413 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
7414 gsf_xml_out_start_element (state->xml, TEXT "p");
7415 str = value_peek_string (v);
7416 if (GOG_IS_TEXT (title) &&
7417 (text = GOG_TEXT (title))->allow_markup) {
7418 PangoAttrList *attr_list = NULL;
7419 char *text_clean = NULL;
7420 if (pango_parse_markup (str, -1, 0,
7421 &attr_list,
7422 &text_clean, NULL, NULL)) {
7423 odf_new_markup (state, attr_list, text_clean);
7424 g_free (text_clean);
7425 pango_attr_list_unref (attr_list);
7426 } else
7427 odf_add_chars (state, str,strlen (str),
7428 &white_written);
7429 } else
7430 odf_add_chars (state, str,strlen (str),
7431 &white_written);
7432 gsf_xml_out_end_element (state->xml); /* </text:p> */
7433 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
7434 } else {
7435 gboolean white_written = TRUE;
7436 if (state->with_extension)
7437 gsf_xml_out_add_cstr (state->xml,
7438 GNMSTYLE "expression",
7439 formula);
7440 if (allow_content) {
7441 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
7442 gsf_xml_out_start_element
7443 (state->xml, TEXT "p");
7444 odf_add_chars (state, formula,
7445 strlen (formula),
7446 &white_written);
7447 gsf_xml_out_end_element (state->xml);
7448 /* </text:p> */
7449 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
7452 gsf_xml_out_end_element (state->xml); /* </chart:title> */
7453 g_free (formula);
7459 static void
7460 odf_write_label (GnmOOExport *state, GogObject const *axis)
7462 GSList *labels = gog_object_get_children
7463 (axis, gog_object_find_role_by_name (axis, "Label"));
7465 if (labels != NULL) {
7466 GogObject const *label = NULL;
7468 label = labels->data;
7469 odf_write_title (state, label, CHART "title", TRUE);
7470 g_slist_free (labels);
7476 static gboolean
7477 odf_match_gradient (GOStyle const *old, GOStyle const *new)
7479 gboolean result;
7481 if (old->fill.gradient.brightness != new->fill.gradient.brightness)
7482 return FALSE;
7484 if (old->fill.gradient.brightness >= 0.)
7485 result = (old->fill.gradient.brightness == new->fill.gradient.brightness);
7486 else
7487 result = (old->fill.pattern.fore == new->fill.pattern.fore);
7489 return (result && (old->fill.gradient.dir == new->fill.gradient.dir) &&
7490 (old->fill.pattern.back == new->fill.pattern.back));
7493 static gchar *
7494 odf_get_gradient_name (GnmOOExport *state, GOStyle const* style)
7496 gchar const *grad = g_hash_table_lookup (state->graph_gradients,
7497 (gpointer) style);
7498 gchar *new_name;
7499 if (grad != NULL)
7500 return g_strdup (grad);
7502 new_name = g_strdup_printf ("Gradient-%i", g_hash_table_size (state->graph_gradients));
7503 g_hash_table_insert (state->graph_gradients,
7504 (gpointer) style, g_strdup (new_name));
7505 return new_name;
7508 static gboolean
7509 odf_match_image (GOImage *old, GOImage *new)
7511 return !go_image_differ (old, new);
7515 static gchar *
7516 odf_get_image_name (GnmOOExport *state, GOStyle const* style)
7518 gchar const *image = g_hash_table_lookup (state->graph_fill_images,
7519 (gpointer) style->fill.image.image);
7520 gchar *new_name;
7521 if (image != NULL)
7522 return g_strdup (image);
7524 new_name = g_strdup_printf ("Fill-Image-%i",
7525 g_hash_table_size (state->graph_fill_images));
7526 g_hash_table_insert (state->graph_fill_images,
7527 (gpointer) style->fill.image.image, g_strdup (new_name));
7528 return new_name;
7531 static gboolean
7532 odf_match_pattern (GOPattern const *old, GOPattern const *new)
7534 return (old->pattern == new->pattern &&
7535 old->back == new->back &&
7536 old->fore == new->fore);
7539 static gchar *
7540 odf_get_pattern_name (GnmOOExport *state, GOStyle const* style)
7542 gchar const *hatch = g_hash_table_lookup (state->graph_hatches,
7543 (gpointer) &style->fill.pattern);
7544 gchar *new_name;
7545 if (hatch != NULL)
7546 return g_strdup (hatch);
7548 new_name = g_strdup_printf ("Pattern-%i-%i", style->fill.pattern.pattern,
7549 g_hash_table_size (state->graph_hatches));
7550 g_hash_table_insert (state->graph_hatches,
7551 (gpointer) &style->fill.pattern, g_strdup (new_name));
7552 return new_name;
7555 static char *
7556 odf_get_border_info (G_GNUC_UNUSED GnmOOExport *state, GOStyle const *style)
7558 if (style->line.width <= 0)
7559 return g_strdup ("thin");
7560 if (style->line.width == 1.5)
7561 return g_strdup ("medium");
7562 if (style->line.width == 3)
7563 return g_strdup ("thick");
7564 return g_strdup_printf ("%.6fpt", style->line.width);
7567 static void
7568 odf_write_gog_style_graphic (GnmOOExport *state, GOStyle const *style, gboolean with_border)
7570 char const *image_types[] =
7571 {"stretch", "repeat", "no-repeat"};
7573 if (!style)
7574 return;
7576 if (style->interesting_fields & (GO_STYLE_FILL)) {
7577 if (state->with_extension && style->fill.auto_type) {
7578 odf_add_bool (state->xml, GNMSTYLE "auto-type", TRUE);
7580 /* We need to write our colours even for auto_type == TRUE since nobody else understands this */
7581 switch (style->fill.type) {
7582 case GO_STYLE_FILL_NONE:
7583 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "none");
7584 break;
7585 case GO_STYLE_FILL_PATTERN:
7586 if (style->fill.pattern.pattern == GO_PATTERN_SOLID) {
7587 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "solid");
7588 if (!style->fill.auto_back) {
7589 char *color = odf_go_color_to_string (style->fill.pattern.back);
7590 gsf_xml_out_add_cstr (state->xml, DRAW "fill-color", color);
7591 odf_add_percent (state->xml, DRAW "opacity",
7592 odf_go_color_opacity (style->fill.pattern.back));
7593 g_free (color);
7595 } else if (style->fill.pattern.pattern == GO_PATTERN_FOREGROUND_SOLID) {
7596 if (state->with_extension)
7597 odf_add_bool (state->xml, GNMSTYLE "foreground-solid", TRUE);
7598 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "solid");
7599 if (!style->fill.auto_fore) {
7600 char *color = odf_go_color_to_string (style->fill.pattern.fore);
7601 gsf_xml_out_add_cstr (state->xml, DRAW "fill-color", color);
7602 odf_add_percent (state->xml, DRAW "opacity",
7603 odf_go_color_opacity (style->fill.pattern.fore));
7604 g_free (color);
7606 } else {
7607 gchar *hatch = odf_get_pattern_name (state, style);
7608 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "hatch");
7609 gsf_xml_out_add_cstr (state->xml, DRAW "fill-hatch-name",
7610 hatch);
7611 if (!style->fill.auto_back) {
7612 char *color = odf_go_color_to_string (style->fill.pattern.back);
7613 gsf_xml_out_add_cstr (state->xml, DRAW "fill-color", color);
7614 odf_add_percent (state->xml, DRAW "opacity",
7615 odf_go_color_opacity (style->fill.pattern.back));
7616 g_free (color);
7618 g_free (hatch);
7619 odf_add_bool (state->xml, DRAW "fill-hatch-solid", TRUE);
7620 if (state->with_extension)
7621 gsf_xml_out_add_int
7622 (state->xml,
7623 GNMSTYLE "pattern",
7624 style->fill.pattern.pattern);
7626 break;
7627 case GO_STYLE_FILL_GRADIENT: {
7628 gchar *grad = odf_get_gradient_name (state, style);
7629 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "gradient");
7630 gsf_xml_out_add_cstr (state->xml, DRAW "fill-gradient-name", grad);
7631 g_free (grad);
7632 break;
7634 case GO_STYLE_FILL_IMAGE: {
7635 gchar *image = odf_get_image_name (state, style);
7636 gsf_xml_out_add_cstr (state->xml, DRAW "fill", "bitmap");
7637 gsf_xml_out_add_cstr (state->xml, DRAW "fill-image-name", image);
7638 g_free (image);
7639 if (style->fill.image.type < G_N_ELEMENTS (image_types))
7640 gsf_xml_out_add_cstr (state->xml, STYLE "repeat",
7641 image_types [style->fill.image.type]);
7642 else g_warning ("Unexpected GOImageType value");
7643 break;
7648 if (style->interesting_fields & (GO_STYLE_LINE | GO_STYLE_OUTLINE | GO_STYLE_MARKER)) {
7649 GOLineDashType dash_type = style->line.dash_type;
7650 gboolean has_line = go_style_is_line_visible (style);
7651 gboolean is_auto;
7652 GOColor color;
7654 if (!has_line)
7655 gsf_xml_out_add_cstr (state->xml,
7656 DRAW "stroke", "none");
7657 else if (dash_type == GO_LINE_SOLID)
7658 gsf_xml_out_add_cstr (state->xml,
7659 DRAW "stroke", "solid");
7660 else {
7661 char const *dash = go_line_dash_as_str (dash_type);
7662 gsf_xml_out_add_cstr (state->xml,
7663 DRAW "stroke", "dash");
7664 gsf_xml_out_add_cstr
7665 (state->xml,
7666 DRAW "stroke-dash", dash);
7667 g_hash_table_insert (state->graph_dashes, g_strdup (dash),
7668 GINT_TO_POINTER (dash_type));
7670 if (style->line.auto_dash && state->with_extension)
7671 odf_add_bool (state->xml, GNMSTYLE "auto-dash", TRUE);
7673 if (style->line.auto_width && state->with_extension)
7674 odf_add_bool (state->xml, GNMSTYLE "auto-width", TRUE);
7675 else if (style->line.width == 0.0) {
7676 odf_add_pt (state->xml, SVG "stroke-width", 1.);
7677 if (state->with_extension)
7678 odf_add_pt (state->xml, GNMSTYLE "stroke-width", 0.);
7679 } else if (style->line.width > 0.0)
7680 odf_add_pt (state->xml, SVG "stroke-width",
7681 style->line.width);
7684 * ods doesn't have separate colours for the marker, so use
7685 * the marker colour if we don't have a line.
7687 is_auto = style->line.auto_color;
7688 color = style->line.color;
7689 if (!has_line && (style->interesting_fields & GO_STYLE_MARKER)) {
7690 is_auto = style->marker.auto_fill_color;
7691 color = go_marker_get_fill_color (style->marker.mark);
7694 if (!is_auto) {
7695 char *s = odf_go_color_to_string (color);
7696 gsf_xml_out_add_cstr (state->xml, SVG "stroke-color", s);
7697 g_free (s);
7698 if (state->with_extension) {
7699 s = odf_go_color_to_string (go_marker_get_outline_color (style->marker.mark));
7700 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "marker-outline-colour", s);
7701 g_free (s);
7702 s = odf_go_color_to_string (go_marker_get_fill_color (style->marker.mark));
7703 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "marker-fill-colour", s);
7704 g_free (s);
7706 } else if (state->with_extension)
7707 odf_add_bool (state->xml, GNMSTYLE "auto-color", style->fill.auto_fore);
7708 if (state->with_extension && (style->interesting_fields & GO_STYLE_MARKER)) {
7709 odf_add_bool (state->xml, GNMSTYLE "auto-marker-outline-colour",
7710 style->marker.auto_outline_color);
7711 odf_add_bool (state->xml, GNMSTYLE "auto-marker-fill-colour",
7712 style->marker.auto_fill_color);
7714 } else {
7715 gsf_xml_out_add_cstr (state->xml, DRAW "stroke", "none");
7718 if (with_border && go_style_is_outline_visible (style)) {
7719 char *border = odf_get_border_info (state, style);
7720 if (strlen (border) > 0)
7721 gsf_xml_out_add_cstr (state->xml, FOSTYLE "border", border);
7722 g_free (border);
7726 static void
7727 odf_write_gog_style_text (GnmOOExport *state, GOStyle const *style)
7729 if (style != NULL) {
7730 PangoFontDescription const *desc = style->font.font->desc;
7731 PangoFontMask mask = pango_font_description_get_set_fields (desc);
7733 if (!style->text_layout.auto_angle) {
7734 int val = style->text_layout.angle;
7735 odf_add_angle (state->xml, STYLE "text-rotation-angle", val);
7738 if (!style->font.auto_color) {
7739 char *color = odf_go_color_to_string (style->font.color);
7740 gsf_xml_out_add_cstr (state->xml, FOSTYLE "color", color);
7741 g_free (color);
7744 if (mask & PANGO_FONT_MASK_SIZE)
7745 odf_add_pt (state->xml, FOSTYLE "font-size",
7746 pango_font_description_get_size
7747 (style->font.font->desc)
7748 / (double)PANGO_SCALE);
7750 if (mask & PANGO_FONT_MASK_VARIANT) {
7751 PangoVariant var = pango_font_description_get_variant (desc);
7752 switch (var) {
7753 case PANGO_VARIANT_NORMAL:
7754 gsf_xml_out_add_cstr (state->xml,
7755 FOSTYLE "font-variant", "normal");
7756 break;
7757 case PANGO_VARIANT_SMALL_CAPS:
7758 gsf_xml_out_add_cstr (state->xml,
7759 FOSTYLE "font-variant",
7760 "small-caps");
7761 break;
7762 default:
7763 break;
7766 /*Note that we should be using style:font-name instead of fo:font-family*/
7767 if (mask & PANGO_FONT_MASK_FAMILY)
7768 gsf_xml_out_add_cstr
7769 (state->xml,
7770 FOSTYLE "font-family",
7771 pango_font_description_get_family (desc));
7772 if (mask & PANGO_FONT_MASK_STYLE) {
7773 PangoStyle s = pango_font_description_get_style (desc);
7774 switch (s) {
7775 case PANGO_STYLE_NORMAL:
7776 gsf_xml_out_add_cstr (state->xml,
7777 FOSTYLE "font-style", "normal");
7778 break;
7779 case PANGO_STYLE_OBLIQUE:
7780 gsf_xml_out_add_cstr (state->xml,
7781 FOSTYLE "font-style", "oblique");
7782 break;
7783 case PANGO_STYLE_ITALIC:
7784 gsf_xml_out_add_cstr (state->xml,
7785 FOSTYLE "font-style", "italic");
7786 break;
7787 default:
7788 break;
7791 if (mask & PANGO_FONT_MASK_WEIGHT)
7792 odf_add_font_weight (state,
7793 pango_font_description_get_weight (desc));
7795 if ((mask & PANGO_FONT_MASK_STRETCH) && state->with_extension)
7796 gsf_xml_out_add_int (state->xml, GNMSTYLE "font-stretch-pango",
7797 pango_font_description_get_stretch (desc));
7798 if ((mask & PANGO_FONT_MASK_GRAVITY) && state->with_extension)
7799 gsf_xml_out_add_int (state->xml, GNMSTYLE "font-gravity-pango",
7800 pango_font_description_get_gravity (desc));
7802 if (state->with_extension)
7803 odf_add_bool (state->xml, GNMSTYLE "auto-font",
7804 style->font.auto_font);
7808 static void
7809 odf_write_gog_style_chart (GnmOOExport *state, GOStyle const *style, GogObject const *obj)
7811 gchar const *type = G_OBJECT_TYPE_NAME (G_OBJECT (obj));
7812 void (*func) (GnmOOExport *state, GOStyle const *style, GogObject const *obj);
7814 if (GOG_IS_PLOT (obj))
7815 odf_write_plot_style (state, obj);
7817 if (GOG_IS_AXIS (obj)) {
7818 GOFormat *fmt = gog_axis_get_format (GOG_AXIS (obj));
7819 odf_add_bool (state->xml, CHART "link-data-style-to-source", fmt == NULL);
7822 odf_write_fill_type (state, obj);
7824 func = g_hash_table_lookup (state->chart_props_hash, type);
7825 if (func != NULL)
7826 func (state, style, obj);
7828 if (!style)
7829 return;
7831 if (style->interesting_fields & (GO_STYLE_LINE | GO_STYLE_OUTLINE)) {
7832 odf_add_bool (state->xml,
7833 CHART "lines",
7834 go_style_is_line_visible (style));
7837 if (style->interesting_fields & GO_STYLE_MARKER) {
7838 GOMarker const *marker = go_style_get_marker (style);
7839 const char *symbol_type = NULL;
7841 if (style->marker.auto_shape) {
7842 if (GOG_IS_SERIES (obj)) {
7843 GogPlot *plot = gog_series_get_plot (GOG_SERIES (obj));
7844 gboolean has_marker = TRUE;
7846 if (gnm_object_has_readable_prop
7847 (plot, "default-style-has-markers",
7848 G_TYPE_BOOLEAN, &has_marker)) {
7849 if (has_marker)
7850 symbol_type = "automatic";
7851 } else
7852 symbol_type = "automatic";
7853 } else
7854 symbol_type = "automatic";
7855 } else {
7856 GOMarkerShape m = go_marker_get_shape (marker);
7858 if (m != GO_MARKER_NONE) {
7859 symbol_type = "named-symbol";
7861 gsf_xml_out_add_cstr
7862 (state->xml, CHART "symbol-name", odf_get_marker (m));
7866 if (symbol_type) {
7867 int size = go_marker_get_size (marker);
7868 odf_add_pt (state->xml, CHART "symbol-width", size);
7869 odf_add_pt (state->xml, CHART "symbol-height", size);
7870 } else
7871 symbol_type = "none";
7873 gsf_xml_out_add_cstr (state->xml, CHART "symbol-type", symbol_type);
7877 static void
7878 odf_write_gog_style (GnmOOExport *state, GOStyle const *style,
7879 GogObject const *obj)
7881 char *name = odf_get_gog_style_name (state, style, obj);
7882 if (name != NULL) {
7883 odf_start_style (state->xml, name, "chart");
7885 if (GOG_IS_AXIS (obj)) {
7886 GOFormat *fmt = gog_axis_get_format (GOG_AXIS (obj));
7887 if (fmt) {
7888 char const *name = xl_find_format (state, fmt);
7889 gsf_xml_out_add_cstr (state->xml, STYLE "data-style-name", name);
7893 gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
7894 odf_write_gog_style_chart (state, style, obj);
7895 gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
7897 gsf_xml_out_start_element (state->xml, STYLE "graphic-properties");
7898 odf_write_gog_style_graphic (state, style, FALSE);
7899 gsf_xml_out_end_element (state->xml); /* </style:graphic-properties> */
7901 gsf_xml_out_start_element (state->xml, STYLE "paragraph-properties");
7902 gsf_xml_out_end_element (state->xml); /* </style:paragraph-properties> */
7904 gsf_xml_out_start_element (state->xml, STYLE "text-properties");
7905 odf_write_gog_style_text (state, style);
7906 gsf_xml_out_end_element (state->xml); /* </style:text-properties> */
7908 gsf_xml_out_end_element (state->xml); /* </style:style> */
7910 g_free (name);
7914 static void
7915 odf_write_gog_styles (GogObject const *obj, GnmOOExport *state)
7917 GSList *children;
7918 GOStyle *style = NULL;
7920 if (gnm_object_has_readable_prop (obj, "style", G_TYPE_NONE, &style)) {
7921 odf_write_gog_style (state, style, obj);
7922 if (style != NULL)
7923 g_object_unref (style);
7924 } else
7925 odf_write_gog_style (state, NULL, obj);
7927 children = gog_object_get_children (obj, NULL);
7928 g_slist_foreach (children, (GFunc) odf_write_gog_styles, state);
7929 g_slist_free (children);
7932 static void
7933 odf_write_axis_categories (GnmOOExport *state, GSList const *series, GogMSDimType dim)
7935 if (series != NULL && series->data != NULL) {
7936 GOData const *cat = gog_dataset_get_dim (GOG_DATASET (series->data), dim);
7937 if (NULL != cat) {
7938 GnmExprTop const *texpr = gnm_go_data_get_expr (cat);
7939 if (NULL != texpr) {
7940 char *cra;
7941 GnmParsePos pp;
7942 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
7943 cra = gnm_expr_top_as_string (texpr, &pp, state->conv);
7945 gsf_xml_out_start_element (state->xml, CHART "categories");
7946 gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address",
7947 odf_strip_brackets (cra));
7948 gsf_xml_out_end_element (state->xml); /* </chart:categories> */
7950 g_free (cra);
7956 static void
7957 odf_write_axis_full (GnmOOExport *state,
7958 GogObject const *chart,
7959 char const *axis_role,
7960 char const *dimension,
7961 G_GNUC_UNUSED odf_chart_type_t gtype,
7962 GSList const *series,
7963 gboolean include_cats,
7964 GogMSDimType dim)
7966 GSList *children = NULL, *l;
7967 GString *str;
7969 if (axis_role == NULL)
7970 return;
7972 str = g_string_new (NULL);
7973 children = gog_object_get_children (chart, gog_object_find_role_by_name (chart, axis_role));
7975 for (l = children; l != NULL; l = l->next) {
7976 GogObject const *axis = l->data;
7977 if (axis != NULL) {
7978 int id = gog_object_get_id (GOG_OBJECT (axis));
7979 char *name;
7981 gsf_xml_out_start_element (state->xml, CHART "axis");
7982 gsf_xml_out_add_cstr (state->xml, CHART "dimension", dimension);
7983 if (state->with_extension)
7984 gsf_xml_out_add_int (state->xml, GNMSTYLE "id", id);
7985 g_string_truncate (str, 0);
7986 g_string_append_printf (str, "%s-%i", axis_role, id);
7987 gsf_xml_out_add_cstr_unchecked (state->xml, CHART "name", str->str);
7988 name = odf_get_gog_style_name_from_obj (state, GOG_OBJECT (axis));
7989 if (name != NULL)
7990 gsf_xml_out_add_cstr (state->xml, CHART "style-name", name);
7991 g_free (name);
7992 if (state->with_extension && 0 == strcmp (axis_role,"Pseudo-3D-Axis")) {
7993 char *color_map_name = NULL;
7994 g_object_get (G_OBJECT (axis), "color-map-name", &color_map_name, NULL);
7995 if (color_map_name) {
7996 gsf_xml_out_add_cstr (state->xml, GNMSTYLE "color-map-name", color_map_name);
7997 g_free (color_map_name);
8000 odf_write_label (state, axis);
8001 if (include_cats)
8002 odf_write_axis_categories (state, series, dim);
8003 odf_write_axis_grid (state, axis);
8004 odf_write_axislines (state, axis);
8005 gsf_xml_out_end_element (state->xml); /* </chart:axis> */
8008 g_slist_free (children);
8009 g_string_free (str, TRUE);
8012 static void
8013 odf_write_axis (GnmOOExport *state,
8014 GogObject const *chart,
8015 char const *axis_role,
8016 char const *dimension,
8017 odf_chart_type_t gtype,
8018 GogMSDimType dim,
8019 GSList const *series)
8021 odf_write_axis_full (state, chart, axis_role, dimension, gtype, series, TRUE, dim);
8024 static void
8025 odf_write_axis_no_cats (GnmOOExport *state,
8026 GogObject const *chart,
8027 char const *axis_role,
8028 char const *dimension,
8029 odf_chart_type_t gtype,
8030 GogMSDimType dim,
8031 GSList const *series)
8033 odf_write_axis_full (state, chart, axis_role, dimension, gtype, series, FALSE, dim);
8036 static void
8037 odf_write_pie_axis (GnmOOExport *state,
8038 G_GNUC_UNUSED GogObject const *chart,
8039 G_GNUC_UNUSED char const *axis_role,
8040 char const *dimension,
8041 G_GNUC_UNUSED odf_chart_type_t gtype,
8042 GogMSDimType dim,
8043 GSList const *series)
8045 gsf_xml_out_start_element (state->xml, CHART "axis");
8046 gsf_xml_out_add_cstr (state->xml, CHART "dimension", dimension);
8047 gsf_xml_out_add_cstr (state->xml, CHART "style-name", "pie-axis");
8048 odf_write_axis_categories (state, series, dim);
8049 gsf_xml_out_end_element (state->xml); /* </chart:axis> */
8053 static void
8054 odf_write_plot (GnmOOExport *state, SheetObject *so, GogObject const *graph,
8055 GogObject const *chart, GogObject const *plot, GSList *other_plots)
8057 char const *plot_type = G_OBJECT_TYPE_NAME (plot);
8058 SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
8059 double res_pts[4] = {0.,0.,0.,0.};
8060 GSList const *series, *l;
8061 GogObject const *wall = gog_object_get_child_by_name (chart, "Backplane");
8062 GogObject const *legend = gog_object_get_child_by_name (chart, "Legend");
8063 GogObject const *color_scale = gog_object_get_child_by_name (chart, "Color-Scale");
8064 GogObjectRole const *trole = gog_object_find_role_by_name (graph, "Title");
8065 GSList *titles = gog_object_get_children (graph, trole);
8066 GogObjectRole const *trole2 = gog_object_find_role_by_name (chart, "Title");
8067 GSList *subtitles = gog_object_get_children (chart, trole2);
8068 char *name;
8069 GOStyle *style = NULL;
8071 static struct {
8072 char const * type;
8073 char const *odf_plot_type;
8074 odf_chart_type_t gtype;
8075 double pad;
8076 char const * x_axis_name;
8077 char const * y_axis_name;
8078 char const * z_axis_name;
8079 GogMSDimType x_dim;
8080 GogMSDimType y_dim;
8081 GogMSDimType z_dim;
8082 void (*odf_write_series) (GnmOOExport *state,
8083 GSList const *series,
8084 char const* class);
8085 void (*odf_write_x_axis) (GnmOOExport *state,
8086 GogObject const *chart,
8087 char const *axis_role,
8088 char const *dimension,
8089 odf_chart_type_t gtype,
8090 GogMSDimType dim,
8091 GSList const *series);
8092 void (*odf_write_y_axis) (GnmOOExport *state,
8093 GogObject const *chart,
8094 char const *axis_role,
8095 char const *dimension,
8096 odf_chart_type_t gtype,
8097 GogMSDimType dim,
8098 GSList const *series);
8099 void (*odf_write_z_axis) (GnmOOExport *state,
8100 GogObject const *chart,
8101 char const *axis_role,
8102 char const *dimension,
8103 odf_chart_type_t gtype,
8104 GogMSDimType dim,
8105 GSList const *series);
8106 } *this_plot, *this_second_plot, plots[] = {
8107 { "GogColPlot", CHART "bar", ODF_BARCOL,
8108 20., "X-Axis", "Y-Axis", NULL,
8109 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8110 odf_write_standard_series,
8111 odf_write_axis, odf_write_axis_no_cats, odf_write_axis},
8112 { "GogBarPlot", CHART "bar", ODF_BARCOL,
8113 20., "Y-Axis", "X-Axis", NULL,
8114 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8115 odf_write_standard_series,
8116 odf_write_axis, odf_write_axis_no_cats, odf_write_axis},
8117 { "GogLinePlot", CHART "line", ODF_LINE,
8118 20., "X-Axis", "Y-Axis", NULL,
8119 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8120 odf_write_standard_series,
8121 odf_write_axis, odf_write_axis, odf_write_axis},
8122 { "GogPolarPlot", GNMSTYLE "polar", ODF_POLAR,
8123 20., "Circular-Axis", "Radial-Axis", NULL,
8124 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8125 odf_write_standard_series,
8126 odf_write_axis, odf_write_axis, odf_write_axis},
8127 { "GogAreaPlot", CHART "area", ODF_AREA,
8128 20., "X-Axis", "Y-Axis", NULL,
8129 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8130 odf_write_standard_series,
8131 odf_write_axis, odf_write_axis, odf_write_axis},
8132 { "GogDropBarPlot", CHART "gantt", ODF_DROPBAR,
8133 20., "X-Axis", "Y-Axis", NULL,
8134 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8135 odf_write_gantt_series,
8136 odf_write_axis, odf_write_axis, odf_write_axis},
8137 { "GogMinMaxPlot", CHART "stock", ODF_MINMAX,
8138 10., "X-Axis", "Y-Axis", NULL,
8139 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8140 odf_write_min_max_series,
8141 odf_write_axis, odf_write_axis, odf_write_axis},
8142 { "GogPiePlot", CHART "circle", ODF_CIRCLE,
8143 5., NULL, "Y-Axis", NULL,
8144 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8145 odf_write_standard_series,
8146 NULL, odf_write_pie_axis, NULL},
8147 { "GogRadarPlot", CHART "radar", ODF_RADAR,
8148 10., "Circular-Axis", "Radial-Axis", NULL,
8149 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8150 odf_write_standard_series,
8151 odf_write_axis, odf_write_axis, odf_write_axis},
8152 { "GogRadarAreaPlot", CHART "filled-radar", ODF_RADARAREA,
8153 10., "X-Axis", "Y-Axis", NULL,
8154 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8155 odf_write_standard_series,
8156 odf_write_axis, odf_write_axis, odf_write_axis},
8157 { "GogRingPlot", CHART "ring", ODF_RING,
8158 10., NULL, "Y-Axis", NULL,
8159 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8160 odf_write_standard_series,
8161 NULL, odf_write_pie_axis, NULL},
8162 { "GogXYPlot", CHART "scatter", ODF_SCATTER,
8163 20., "X-Axis", "Y-Axis", NULL,
8164 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8165 odf_write_standard_series,
8166 odf_write_axis, odf_write_axis, odf_write_axis},
8167 { "GogContourPlot", CHART "surface", ODF_SURF,
8168 20., "X-Axis", "Y-Axis", "Pseudo-3D-Axis",
8169 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8170 odf_write_bubble_series,
8171 odf_write_axis, odf_write_axis, odf_write_axis_no_cats},
8172 { "GogXYZContourPlot", GNMSTYLE "xyz-surface", ODF_XYZ_SURF,
8173 20., "X-Axis", "Y-Axis", NULL,
8174 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8175 odf_write_bubble_series,
8176 odf_write_axis, odf_write_axis, odf_write_axis},
8177 { "GogXYZSurfacePlot", GNMSTYLE "xyz-surface", ODF_XYZ_GNM_SURF,
8178 20., "X-Axis", "Y-Axis", "Z-Axis",
8179 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8180 odf_write_bubble_series,
8181 odf_write_axis, odf_write_axis, odf_write_axis},
8182 { "GogSurfacePlot", CHART "surface", ODF_GNM_SURF,
8183 20., "X-Axis", "Y-Axis", "Z-Axis",
8184 GOG_MS_DIM_LABELS, GOG_MS_DIM_VALUES, GOG_MS_DIM_LABELS,
8185 odf_write_bubble_series,
8186 odf_write_axis, odf_write_axis, odf_write_axis_no_cats},
8187 { "GogBubblePlot", CHART "bubble", ODF_BUBBLE,
8188 20., "X-Axis", "Y-Axis", NULL,
8189 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8190 odf_write_bubble_series,
8191 odf_write_axis_no_cats, odf_write_axis_no_cats, odf_write_axis_no_cats},
8192 { "GogXYColorPlot", GNMSTYLE "scatter-color", ODF_SCATTER_COLOUR,
8193 20., "X-Axis", "Y-Axis", NULL,
8194 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8195 odf_write_bubble_series,
8196 odf_write_axis, odf_write_axis, odf_write_axis},
8197 { "XLSurfacePlot", CHART "surface", ODF_GNM_SURF,
8198 20., "X-Axis", "Y-Axis", "Z-Axis",
8199 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8200 odf_write_standard_series,
8201 odf_write_axis, odf_write_axis, odf_write_axis},
8202 { "GogBoxPlot", GNMSTYLE "box", ODF_GNM_BOX,
8203 20., "X-Axis", "Y-Axis", NULL,
8204 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8205 odf_write_box_series,
8206 odf_write_axis, odf_write_axis, odf_write_axis},
8207 { NULL, NULL, 0,
8208 20., "X-Axis", "Y-Axis", NULL,
8209 GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS, GOG_MS_DIM_LABELS,
8210 odf_write_standard_series,
8211 odf_write_axis, odf_write_axis, odf_write_axis}
8214 if (0 == strcmp ("GogBarColPlot", plot_type)) {
8215 gboolean b;
8217 if (gnm_object_has_readable_prop (plot, "horizontal",
8218 G_TYPE_BOOLEAN, &b) &&
8220 plot_type = "GogBarPlot";
8221 else
8222 plot_type = "GogColPlot";
8225 for (this_plot = &plots[0]; this_plot->type != NULL; this_plot++)
8226 if (0 == strcmp (plot_type, this_plot->type))
8227 break;
8229 if (this_plot->type == NULL) {
8230 g_printerr ("Encountered unknown chart type %s\n", plot_type);
8231 this_plot = &plots[0];
8234 series = gog_plot_get_series (GOG_PLOT (plot));
8236 gsf_xml_out_start_element (state->xml, OFFICE "automatic-styles");
8237 odf_write_character_styles (state);
8239 odf_write_generic_axis_style (state, "pie-axis");
8241 odf_start_style (state->xml, "plotstyle", "chart");
8242 gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
8243 odf_add_bool (state->xml, CHART "auto-size", TRUE);
8244 gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
8245 g_object_get (G_OBJECT (chart), "style", &style, NULL);
8246 if (style) {
8247 gsf_xml_out_start_element (state->xml, STYLE "graphic-properties");
8248 odf_write_gog_style_graphic (state, style, TRUE);
8249 gsf_xml_out_end_element (state->xml); /* </style:graphic-properties> */
8250 g_object_unref (style);
8252 gsf_xml_out_end_element (state->xml); /* </style:style> */
8254 odf_write_gog_styles (chart, state);
8256 gsf_xml_out_end_element (state->xml); /* </office:automatic-styles> */
8258 gsf_xml_out_start_element (state->xml, OFFICE "body");
8259 gsf_xml_out_start_element (state->xml, OFFICE "chart");
8260 gsf_xml_out_start_element (state->xml, CHART "chart");
8262 sheet_object_anchor_to_pts (anchor, state->sheet, res_pts);
8263 odf_add_pt (state->xml, SVG "width", res_pts[2] - res_pts[0] - 2 * this_plot->pad);
8264 odf_add_pt (state->xml, SVG "height", res_pts[3] - res_pts[1] - 2 * this_plot->pad);
8266 if (state->odf_version > 101) {
8267 gsf_xml_out_add_cstr (state->xml, XLINK "type", "simple");
8268 gsf_xml_out_add_cstr (state->xml, XLINK "href", "..");
8270 gsf_xml_out_add_cstr (state->xml, CHART "class", this_plot->odf_plot_type);
8271 gsf_xml_out_add_cstr (state->xml, CHART "style-name", "plotstyle");
8273 /* Set up title */
8275 if (titles != NULL) {
8276 GogObject const *title = titles->data;
8277 odf_write_title (state, title, CHART "title", TRUE);
8278 g_slist_free (titles);
8280 if (subtitles != NULL) {
8281 GogObject const *title = subtitles->data;
8282 char *position;
8283 gboolean is_footer = FALSE;
8285 g_object_get (G_OBJECT (title),
8286 "compass", &position,
8287 NULL);
8288 is_footer = NULL != g_strstr_len (position, -1, "bottom");
8289 odf_write_title (state, title,
8290 is_footer ? CHART "footer" : CHART "subtitle",
8291 TRUE);
8292 g_slist_free (subtitles);
8293 g_free (position);
8297 /* Set up legend if appropriate*/
8299 if (legend != NULL) {
8300 GogObjectPosition flags;
8301 char *style_name = odf_get_gog_style_name_from_obj
8302 (state, legend);
8303 GSList *ltitles = gog_object_get_children
8304 (legend, gog_object_find_role_by_name
8305 (legend, "Title"));
8306 gboolean is_position_manual = FALSE;
8308 gsf_xml_out_start_element (state->xml, CHART "legend");
8309 gsf_xml_out_add_cstr (state->xml,
8310 CHART "style-name",
8311 style_name);
8312 g_free (style_name);
8314 odf_write_gog_position (state, legend); /* gnumeric extensions */
8316 g_object_get (G_OBJECT (legend),
8317 "is-position-manual", &is_position_manual,
8318 NULL);
8319 if (is_position_manual)
8320 odf_write_gog_position_pts (state, legend);
8321 else {
8322 flags = gog_object_get_position_flags
8323 (legend, GOG_POSITION_COMPASS);
8324 if (flags) {
8325 GString *compass = g_string_new (NULL);
8327 if (flags & GOG_POSITION_N)
8328 g_string_append (compass, "top");
8329 if (flags & GOG_POSITION_S)
8330 g_string_append (compass, "bottom");
8331 if ((flags & (GOG_POSITION_S | GOG_POSITION_N)) &&
8332 (flags & (GOG_POSITION_E | GOG_POSITION_W)))
8333 g_string_append (compass, "-");
8334 if (flags & GOG_POSITION_E)
8335 g_string_append (compass, "end");
8336 if (flags & GOG_POSITION_W)
8337 g_string_append (compass, "start");
8339 gsf_xml_out_add_cstr (state->xml,
8340 CHART "legend-position",
8341 compass->str);
8343 g_string_free (compass, TRUE);
8347 if (ltitles != NULL) {
8348 GogObject const *title = ltitles->data;
8350 if (state->with_extension)
8351 odf_write_title (state, title,
8352 GNMSTYLE "title", state->odf_version > 101);
8353 else if (state->odf_version > 101) {
8354 GOData const *dat =
8355 gog_dataset_get_dim (GOG_DATASET(title),0);
8357 if (dat != NULL) {
8358 GnmExprTop const *texpr
8359 = gnm_go_data_get_expr (dat);
8360 if (texpr != NULL &&
8361 GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_CONSTANT
8362 && VALUE_IS_STRING (texpr->expr->constant.value)) {
8363 gboolean white_written = TRUE;
8364 char const *str;
8365 gboolean pp = TRUE;
8366 g_object_get (G_OBJECT (state->xml), "pretty-print", &pp, NULL);
8367 g_object_set (G_OBJECT (state->xml), "pretty-print", FALSE, NULL);
8368 gsf_xml_out_start_element (state->xml, TEXT "p");
8369 str = value_peek_string (texpr->expr->constant.value);
8370 odf_add_chars (state, str, strlen (str),
8371 &white_written);
8372 gsf_xml_out_end_element (state->xml); /* </text:p> */
8373 g_object_set (G_OBJECT (state->xml), "pretty-print", pp, NULL);
8378 g_slist_free (ltitles);
8381 gsf_xml_out_end_element (state->xml); /* </chart:legend> */
8384 gsf_xml_out_start_element (state->xml, CHART "plot-area");
8386 name = odf_get_gog_style_name_from_obj (state, plot);
8387 if (name != NULL) {
8388 gsf_xml_out_add_cstr (state->xml, CHART "style-name", name);
8389 g_free (name);
8392 if (state->odf_version <= 101) {
8393 for ( l = series; NULL != l ; l = l->next) {
8394 GOData const *dat = gog_dataset_get_dim
8395 (GOG_DATASET (l->data), GOG_MS_DIM_VALUES);
8396 if (NULL != dat) {
8397 GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
8398 if (NULL != texpr) {
8399 GnmParsePos pp;
8400 char *str;
8401 parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
8402 str = gnm_expr_top_as_string (texpr, &pp, state->conv);
8403 gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address",
8404 odf_strip_brackets (str));
8405 g_free (str);
8406 break;
8412 odf_write_gog_plot_area_position (state, chart);
8414 if (this_plot->odf_write_z_axis)
8415 this_plot->odf_write_z_axis
8416 (state, chart, this_plot->z_axis_name, "z",
8417 this_plot->gtype, this_plot->z_dim, series);
8418 if (this_plot->odf_write_y_axis)
8419 this_plot->odf_write_y_axis
8420 (state, chart, this_plot->y_axis_name, "y",
8421 this_plot->gtype, this_plot->y_dim, series);
8422 if (this_plot->odf_write_x_axis)
8423 this_plot->odf_write_x_axis
8424 (state, chart, this_plot->x_axis_name, "x",
8425 this_plot->gtype, this_plot->x_dim, series);
8427 if (this_plot->odf_write_series != NULL)
8428 this_plot->odf_write_series (state, series, NULL);
8430 while (other_plots) {
8431 GogObject const *second_plot = GOG_OBJECT (other_plots->data);
8432 plot_type = G_OBJECT_TYPE_NAME (second_plot);
8434 if (0 == strcmp ("GogBarColPlot", plot_type)) {
8435 gboolean b;
8437 if (gnm_object_has_readable_prop
8438 (second_plot, "horizontal",
8439 G_TYPE_BOOLEAN, &b) && b)
8440 plot_type = "GogBarPlot";
8441 else
8442 plot_type = "GogColPlot";
8445 for (this_second_plot = &plots[0]; this_second_plot->type != NULL; this_second_plot++)
8446 if (0 == strcmp (plot_type, this_second_plot->type))
8447 break;
8449 if (this_second_plot->type == NULL) {
8450 g_printerr ("Encountered unknown chart type %s\n", plot_type);
8451 this_second_plot = &plots[0];
8454 series = gog_plot_get_series (GOG_PLOT (second_plot));
8456 this_second_plot->odf_write_series (state, series, this_second_plot->odf_plot_type);
8457 other_plots = other_plots->next;
8460 if (wall != NULL) {
8461 char *name = odf_get_gog_style_name_from_obj (state, wall);
8463 gsf_xml_out_start_element (state->xml, CHART "wall");
8464 odf_add_pt (state->xml, SVG "width", res_pts[2] - res_pts[0] - 2 * this_plot->pad);
8465 if (name != NULL)
8466 gsf_xml_out_add_cstr (state->xml, CHART "style-name", name);
8467 gsf_xml_out_end_element (state->xml); /* </chart:wall> */
8469 g_free (name);
8471 gsf_xml_out_end_element (state->xml); /* </chart:plot_area> */
8473 if (color_scale != NULL && state->with_extension)
8474 gsf_xml_out_simple_element (state->xml, GNMSTYLE "color-scale", NULL);
8476 gsf_xml_out_end_element (state->xml); /* </chart:chart> */
8477 gsf_xml_out_end_element (state->xml); /* </office:chart> */
8478 gsf_xml_out_end_element (state->xml); /* </office:body> */
8482 static void
8483 odf_write_graph_content (GnmOOExport *state, GsfOutput *child, SheetObject *so, GogObject const *chart)
8485 int i;
8486 GogGraph const *graph;
8487 gboolean plot_written = FALSE;
8489 state->xml = create_new_xml_child (state, child);
8490 gsf_xml_out_set_doc_type (state->xml, "\n");
8491 gsf_xml_out_start_element (state->xml, OFFICE "document-content");
8493 for (i = 0 ; i < (int)G_N_ELEMENTS (ns) ; i++)
8494 gsf_xml_out_add_cstr_unchecked (state->xml, ns[i].key, ns[i].url);
8495 gsf_xml_out_add_cstr_unchecked (state->xml, OFFICE "version",
8496 state->odf_version_string);
8498 graph = sheet_object_graph_get_gog (so);
8499 if (graph != NULL) {
8500 double pos[4];
8501 GogRenderer *renderer;
8502 GogObjectRole const *role;
8504 sheet_object_position_pts_get (so, pos);
8505 renderer = g_object_new (GOG_TYPE_RENDERER,
8506 "model", graph,
8507 NULL);
8508 gog_renderer_update (renderer, pos[2] - pos[0], pos[3] - pos[1]);
8509 g_object_get (G_OBJECT (renderer), "view", &state->root_view, NULL);
8511 role = gog_object_find_role_by_name (chart, "Plot");
8512 if (role != NULL) {
8513 GSList *plots = gog_object_get_children
8514 (chart, gog_object_find_role_by_name (chart, "Plot"));
8515 if (plots != NULL && plots->data != NULL) {
8516 odf_write_plot (state, so, GOG_OBJECT (graph),
8517 chart, plots->data, plots->next);
8518 plot_written = TRUE;
8520 g_slist_free (plots);
8522 g_object_unref (state->root_view);
8523 state->root_view = NULL;
8524 g_object_unref (renderer);
8526 if (!plot_written) {
8527 gsf_xml_out_start_element (state->xml, OFFICE "body");
8528 gsf_xml_out_start_element (state->xml, OFFICE "chart");
8529 gsf_xml_out_start_element (state->xml, CHART "chart");
8530 gsf_xml_out_add_cstr (state->xml, CHART "class", GNMSTYLE "none");
8531 gsf_xml_out_start_element (state->xml, CHART "plot-area");
8532 gsf_xml_out_end_element (state->xml); /* </chart:plotarea> */
8533 gsf_xml_out_end_element (state->xml); /* </chart:chart> */
8534 gsf_xml_out_end_element (state->xml); /* </office:chart> */
8535 gsf_xml_out_end_element (state->xml); /* </office:body> */
8538 gsf_xml_out_end_element (state->xml); /* </office:document-content> */
8539 g_object_unref (state->xml);
8540 state->xml = NULL;
8543 /**********************************************************************************/
8545 static void
8546 odf_write_images (SheetObjectImage *soi, char const *name, GnmOOExport *state)
8548 char *image_type;
8549 char *fullname;
8550 GsfOutput *child;
8551 GOImage *image;
8553 g_object_get (G_OBJECT (soi),
8554 "image-type", &image_type,
8555 "image", &image,
8556 NULL);
8557 fullname = g_strdup_printf ("Pictures/%s.%s", name, image_type);
8559 child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8560 "compression-level", GSF_ZIP_DEFLATED,
8561 NULL);
8562 if (NULL != child) {
8563 gsize length;
8564 guint8 const *data = go_image_get_data (image, &length);
8565 gsf_output_write (child, length, data);
8566 gsf_output_close (child);
8567 g_object_unref (child);
8570 g_free (fullname);
8571 g_free (image_type);
8572 g_object_unref (image);
8574 odf_update_progress (state, state->graph_progress);
8577 static void
8578 odf_write_drop (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8579 GogObject const *obj)
8581 GogObjectRole const *h_role = gog_object_find_role_by_name
8582 (obj->parent, "Horizontal drop lines");
8583 gboolean vertical = !(h_role == obj->role);
8585 odf_add_bool (state->xml, CHART "vertical", vertical);
8588 static void
8589 odf_write_reg_name (GnmOOExport *state, GogObject const *obj)
8591 if (state->with_extension)
8592 odf_add_expr (state, obj, -1, GNMSTYLE "regression-name",
8593 LOEXT "regression-name");
8596 static void
8597 odf_write_plot_style_affine (GsfXMLOut *xml, GogObject const *plot, float intercept)
8599 gboolean b;
8600 if (gnm_object_has_readable_prop (plot, "affine", G_TYPE_BOOLEAN, &b)) {
8601 odf_add_bool (xml, GNMSTYLE "regression-affine", b);
8602 odf_add_bool (xml, LOEXT "regression-force-intercept", !b);
8603 go_xml_out_add_double (xml, LOEXT "regression-intercept-value", intercept);
8608 static void
8609 odf_write_lin_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8610 GogObject const *obj)
8612 gsf_xml_out_add_cstr (state->xml, CHART "regression-type", "linear");
8613 if (state->with_extension) {
8614 odf_write_plot_style_uint (state->xml, obj,
8615 "dims", GNMSTYLE "regression-polynomial-dims");
8616 odf_write_plot_style_uint (state->xml, obj,
8617 "dims", LOEXT "regression-max-degree");
8618 odf_write_plot_style_affine (state->xml, obj, 0.);
8620 odf_write_reg_name (state, obj);
8623 static void
8624 odf_write_polynom_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8625 GogObject const *obj)
8627 if (state->with_extension) {
8628 gsf_xml_out_add_cstr (state->xml, CHART "regression-type",
8629 GNMSTYLE "polynomial");
8630 odf_write_plot_style_uint (state->xml, obj,
8631 "dims", GNMSTYLE "regression-polynomial-dims");
8632 odf_write_plot_style_uint (state->xml, obj,
8633 "dims", LOEXT "regression-max-degree");
8634 odf_write_plot_style_affine (state->xml, obj, 0.);
8636 odf_write_reg_name (state, obj);
8639 static void
8640 odf_write_exp_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8641 G_GNUC_UNUSED GogObject const *obj)
8643 gsf_xml_out_add_cstr (state->xml, CHART "regression-type", "exponential");
8644 if (state->with_extension)
8645 odf_write_plot_style_affine (state->xml, obj, 1.);
8646 odf_write_reg_name (state, obj);
8649 static void
8650 odf_write_power_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8651 G_GNUC_UNUSED GogObject const *obj)
8653 gsf_xml_out_add_cstr (state->xml, CHART "regression-type", "power");
8654 odf_write_reg_name (state, obj);
8657 static void
8658 odf_write_log_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8659 G_GNUC_UNUSED GogObject const *obj)
8661 gsf_xml_out_add_cstr (state->xml, CHART "regression-type", "logarithmic");
8662 odf_write_reg_name (state, obj);
8665 static void
8666 odf_write_log_fit_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8667 G_GNUC_UNUSED GogObject const *obj)
8669 if (state->with_extension)
8670 gsf_xml_out_add_cstr (state->xml, CHART "regression-type",
8671 GNMSTYLE "log-fit");
8672 odf_write_reg_name (state, obj);
8675 static void
8676 odf_write_movig_avg_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8677 G_GNUC_UNUSED GogObject const *obj)
8679 if (state->with_extension)
8680 gsf_xml_out_add_cstr (state->xml, CHART "regression-type",
8681 GNMSTYLE "moving-average");
8682 odf_write_reg_name (state, obj);
8685 static void
8686 odf_write_exp_smooth_reg (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8687 G_GNUC_UNUSED GogObject const *obj)
8689 if (state->with_extension)
8690 gsf_xml_out_add_cstr (state->xml, CHART "regression-type",
8691 GNMSTYLE "exponential-smoothed");
8692 odf_write_reg_name (state, obj);
8695 static void
8696 odf_write_pie_point (GnmOOExport *state, G_GNUC_UNUSED GOStyle const *style,
8697 GogObject const *obj)
8699 double separation = 0.;
8701 if (gnm_object_has_readable_prop (obj, "separation",
8702 G_TYPE_DOUBLE, &separation)) {
8703 gsf_xml_out_add_int (state->xml,
8704 CHART "pie-offset",
8705 (separation * 100. + 0.5));
8709 static void
8710 odf_fill_chart_props_hash (GnmOOExport *state)
8712 int i;
8713 static struct {
8714 gchar const *type;
8715 void (*odf_write_property) (GnmOOExport *state,
8716 GOStyle const *style,
8717 GogObject const *obj);
8718 } props[] = {
8719 {"GogSeriesLines", odf_write_drop},
8720 {"GogAxis", odf_write_axis_style},
8721 {"GogAxisLine", odf_write_axisline_style},
8722 {"GogLinRegCurve", odf_write_lin_reg},
8723 {"GogPolynomRegCurve", odf_write_polynom_reg},
8724 {"GogExpRegCurve", odf_write_exp_reg},
8725 {"GogPowerRegCurve", odf_write_power_reg},
8726 {"GogLogRegCurve", odf_write_log_reg},
8727 {"GogLogFitCurve", odf_write_log_fit_reg},
8728 {"GogMovingAvg", odf_write_movig_avg_reg},
8729 {"GogExpSmooth", odf_write_exp_smooth_reg},
8730 {"GogPieSeriesElement", odf_write_pie_point},
8731 {"GogXYSeries", odf_write_interpolation_attribute},
8732 {"GogLineSeries", odf_write_interpolation_attribute},
8735 for (i = 0 ; i < (int)G_N_ELEMENTS (props) ; i++)
8736 g_hash_table_insert (state->chart_props_hash, (gpointer) props[i].type,
8737 props[i].odf_write_property);
8740 static gboolean
8741 _gsf_gdk_pixbuf_save (const gchar *buf,
8742 gsize count,
8743 GError **error,
8744 gpointer data)
8746 GsfOutput *output = GSF_OUTPUT (data);
8747 gboolean ok = gsf_output_write (output, count, buf);
8749 if (!ok && error)
8750 *error = g_error_copy (gsf_output_error (output));
8752 return ok;
8755 static void
8756 odf_write_fill_images (GOImage *image, char const *name, GnmOOExport *state)
8758 GsfOutput *child;
8759 char *manifest_name = g_strdup_printf ("%s/Pictures/%s.png",
8760 state->object_name, name);
8762 child = gsf_outfile_new_child_full (state->outfile, manifest_name,
8763 FALSE,
8764 "compression-level", GSF_ZIP_DEFLATED,
8765 NULL);
8767 if (child != NULL) {
8768 GdkPixbuf *output_pixbuf;
8770 state->fill_image_files
8771 = g_slist_prepend (state->fill_image_files,
8772 manifest_name);
8773 output_pixbuf = go_image_get_pixbuf (image);
8775 gdk_pixbuf_save_to_callback (output_pixbuf,
8776 _gsf_gdk_pixbuf_save,
8777 child, "png",
8778 NULL, NULL);
8779 gsf_output_close (child);
8780 g_object_unref (child);
8781 } else
8782 g_free (manifest_name);
8788 static void
8789 odf_write_graphs (SheetObject *so, char const *name, GnmOOExport *state)
8791 GogGraph *graph = sheet_object_graph_get_gog (so);
8792 GogObjectRole const *role = gog_object_find_role_by_name (GOG_OBJECT (graph), "Chart");
8793 GSList *l, *chart_list = gog_object_get_children (GOG_OBJECT (graph), role);
8794 gint n = 0;
8795 guint num = g_slist_length (chart_list);
8796 gchar *chartname;
8797 float progress = state->graph_progress / num;
8799 l = chart_list;
8801 while (NULL != chart_list) {
8802 GsfOutput *child;
8803 GogObject const *chart = chart_list->data;
8804 chartname = g_strdup_printf ("%s-%i", name, n);
8805 g_hash_table_remove_all (state->xl_styles);
8807 state->object_name = chartname;
8809 child = gsf_outfile_new_child_full
8810 (state->outfile, chartname, TRUE,
8811 "compression-level", GSF_ZIP_DEFLATED,
8812 NULL);
8813 if (NULL != child) {
8814 char *fullname = g_strdup_printf ("%s/content.xml", chartname);
8815 GsfOutput *sec_child;
8817 state->chart_props_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
8818 NULL, NULL);
8819 odf_fill_chart_props_hash (state);
8821 sec_child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8822 "compression-level", GSF_ZIP_DEFLATED,
8823 NULL);
8824 if (NULL != sec_child) {
8825 odf_write_graph_content (state, sec_child, so, chart);
8826 gsf_output_close (sec_child);
8827 g_object_unref (sec_child);
8829 g_free (fullname);
8831 odf_update_progress (state, 4 * progress);
8833 fullname = g_strdup_printf ("%s/meta.xml", chartname);
8834 sec_child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8835 "compression-level", GSF_ZIP_DEFLATED,
8836 NULL);
8837 if (NULL != sec_child) {
8838 odf_write_meta_graph (state, sec_child);
8839 gsf_output_close (sec_child);
8840 g_object_unref (sec_child);
8842 g_free (fullname);
8843 odf_update_progress (state, progress / 2);
8845 fullname = g_strdup_printf ("%s/styles.xml", chartname);
8846 sec_child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8847 "compression-level", GSF_ZIP_DEFLATED,
8848 NULL);
8849 if (NULL != sec_child) {
8850 odf_write_graph_styles (state, sec_child);
8851 gsf_output_close (sec_child);
8852 g_object_unref (sec_child);
8854 g_free (fullname);
8856 gnm_hash_table_foreach_ordered
8857 (state->graph_fill_images,
8858 (GHFunc) odf_write_fill_images,
8859 by_value_str,
8860 state);
8862 g_hash_table_remove_all (state->graph_dashes);
8863 g_hash_table_remove_all (state->graph_hatches);
8864 g_hash_table_remove_all (state->graph_gradients);
8865 g_hash_table_remove_all (state->graph_fill_images);
8867 g_hash_table_unref (state->chart_props_hash);
8868 state->chart_props_hash = NULL;
8869 odf_update_progress (state, progress * (3./2.));
8871 gsf_output_close (child);
8872 g_object_unref (child);
8874 fullname = g_strdup_printf ("Pictures/%s", chartname);
8875 sec_child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8876 "compression-level", GSF_ZIP_DEFLATED,
8877 NULL);
8878 if (NULL != sec_child) {
8879 if (!gog_graph_export_image (graph, GO_IMAGE_FORMAT_SVG,
8880 sec_child, 100., 100.))
8881 g_print ("Failed to create svg image of graph.\n");
8882 gsf_output_close (sec_child);
8883 g_object_unref (sec_child);
8885 g_free (fullname);
8887 odf_update_progress (state, progress);
8889 fullname = g_strdup_printf ("Pictures/%s.png", chartname);
8890 sec_child = gsf_outfile_new_child_full (state->outfile, fullname, FALSE,
8891 "compression-level", GSF_ZIP_DEFLATED,
8892 NULL);
8893 if (NULL != sec_child) {
8894 if (!gog_graph_export_image (graph, GO_IMAGE_FORMAT_PNG,
8895 sec_child, 100., 100.))
8896 g_print ("Failed to create png image of graph.\n");
8897 gsf_output_close (sec_child);
8898 g_object_unref (sec_child);
8900 g_free (fullname);
8901 odf_update_progress (state, progress);
8904 chart_list = chart_list->next;
8905 n++;
8906 g_free (chartname);
8908 state->object_name = NULL;
8909 g_slist_free (l);
8913 /**********************************************************************************/
8915 static void
8916 openoffice_file_save_real (G_GNUC_UNUSED GOFileSaver const *fs, GOIOContext *ioc,
8917 WorkbookView const *wbv, GsfOutput *output,
8918 gboolean with_extension)
8920 static struct {
8921 void (*func) (GnmOOExport *state, GsfOutput *child);
8922 char const *name;
8923 gboolean inhibit_compression;
8924 } const streams[] = {
8925 /* Must be first */
8926 { odf_write_mimetype, "mimetype", TRUE },
8928 { odf_write_content, "content.xml", FALSE },
8929 { odf_write_styles, "styles.xml", FALSE }, /* must follow content */
8930 { odf_write_meta, "meta.xml", FALSE },
8931 { odf_write_settings, "settings.xml", FALSE },
8934 GnmOOExport state;
8935 GnmLocale *locale;
8936 GError *err;
8937 unsigned i, ui;
8938 Sheet *sheet;
8939 GsfOutput *pictures;
8940 GsfOutput *manifest;
8941 GnmStyle *style;
8943 locale = gnm_push_C_locale ();
8945 state.outfile = gsf_outfile_zip_new (output, &err);
8947 state.with_extension = with_extension;
8948 state.odf_version = gsf_odf_get_version ();
8949 state.odf_version_string = g_strdup (gsf_odf_get_version_string ());
8950 state.ioc = ioc;
8951 state.wbv = wbv;
8952 state.wb = wb_view_get_workbook (wbv);
8953 state.conv = odf_expr_conventions_new (&state);
8954 state.openformula_namemap = NULL;
8955 state.openformula_handlermap = NULL;
8956 state.graphs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8957 NULL, (GDestroyNotify) g_free);
8958 state.images = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8959 NULL, (GDestroyNotify) g_free);
8960 state.controls = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8961 NULL, (GDestroyNotify) g_free);
8962 state.named_cell_styles = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8963 NULL, (GDestroyNotify) g_free);
8964 state.named_cell_style_regions = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8965 (GDestroyNotify) gnm_style_region_free,
8966 (GDestroyNotify) g_free);
8967 state.cell_styles = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8968 NULL, (GDestroyNotify) g_free);
8969 state.so_styles = g_hash_table_new_full (g_direct_hash, g_direct_equal,
8970 NULL, (GDestroyNotify) g_free);
8971 state.xl_styles = g_hash_table_new_full (g_str_hash, g_str_equal,
8972 (GDestroyNotify) g_free, (GDestroyNotify) g_free);
8973 for (ui = 0; ui < G_N_ELEMENTS (state.style_names); ui++)
8974 state.style_names[ui] =
8975 g_hash_table_new_full (g_direct_hash, g_direct_equal,
8976 NULL, (GDestroyNotify) g_free);
8977 state.graph_dashes = g_hash_table_new_full (g_str_hash, g_str_equal,
8978 (GDestroyNotify) g_free,
8979 NULL);
8980 state.graph_hatches = g_hash_table_new_full (g_direct_hash,
8981 (GEqualFunc)odf_match_pattern,
8982 NULL,
8983 (GDestroyNotify) g_free);
8984 state.graph_gradients = g_hash_table_new_full (g_direct_hash,
8985 (GEqualFunc)odf_match_gradient,
8986 NULL,
8987 (GDestroyNotify) g_free);
8988 state.graph_fill_images = g_hash_table_new_full (g_direct_hash,
8989 (GEqualFunc)odf_match_image,
8990 NULL,
8991 (GDestroyNotify) g_free);
8992 state.arrow_markers = g_hash_table_new_full (g_direct_hash,
8993 (GEqualFunc)odf_match_arrow_markers,
8994 (GDestroyNotify) g_free,
8995 (GDestroyNotify) g_free);
8996 state.text_colours = g_hash_table_new_full (g_str_hash, g_str_equal,
8997 (GDestroyNotify) g_free,
8998 (GDestroyNotify) g_free);
8999 state.font_sizes = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
9000 state.col_styles = NULL;
9001 state.row_styles = NULL;
9003 state.date_long_fmt = go_format_new_from_XL ("yyyy-mm-ddThh:mm:ss");
9004 state.date_fmt = go_format_new_from_XL ("yyyy-mm-dd");
9005 state.time_fmt = go_format_new_from_XL ("\"PT0\"[h]\"H\"mm\"M\"ss\"S\"");
9007 state.fill_image_files = NULL;
9009 state.last_progress = 0;
9010 state.sheet_progress = ((float) PROGRESS_STEPS) / 2 /
9011 (workbook_sheet_count (state.wb) + G_N_ELEMENTS (streams));
9012 state.graph_progress = ((float) PROGRESS_STEPS) / 2;
9013 go_io_progress_message (state.ioc, _("Writing Sheets..."));
9014 go_io_value_progress_set (state.ioc, PROGRESS_STEPS, 0);
9018 /* ODF dos not have defaults per table, so we use our first table for defaults only.*/
9019 sheet = workbook_sheet_by_index (state.wb, 0);
9021 state.column_default = &sheet->cols.default_style;
9022 state.row_default = &sheet->rows.default_style;
9023 if (NULL != (style = sheet_style_default (sheet))) {
9024 GnmRange r = {{0,0},{0,0}};
9025 /* We need to make sure any referenced styles are added to the named hash */
9026 state.default_style_region = gnm_style_region_new (&r, style);
9027 odf_store_this_named_style (state.default_style_region->style, "Gnumeric-default",
9028 &state.default_style_region->range,
9029 &state);
9030 gnm_style_unref (style);
9031 } else {
9032 GnmRange r = {{0,0},{0,0}};
9033 state.default_style_region = gnm_style_region_new (&r, NULL);
9036 for (i = 0 ; i < G_N_ELEMENTS (streams); i++) {
9037 int comp_level = streams[i].inhibit_compression
9038 ? GSF_ZIP_STORED
9039 : GSF_ZIP_DEFLATED;
9040 GsfOutput *child = gsf_outfile_new_child_full
9041 (state.outfile, streams[i].name, FALSE,
9042 "compression-level", comp_level,
9043 NULL);
9044 if (NULL != child) {
9045 streams[i].func (&state, child);
9046 gsf_output_close (child);
9047 g_object_unref (child);
9049 odf_update_progress (&state, state.sheet_progress);
9052 state.graph_progress = ((float) PROGRESS_STEPS) / 2 /
9053 (8 * g_hash_table_size (state.graphs) + g_hash_table_size (state.images) + 1);
9054 go_io_progress_message (state.ioc, _("Writing Sheet Objects..."));
9056 pictures = gsf_outfile_new_child_full (state.outfile, "Pictures", TRUE,
9057 "compression-level", GSF_ZIP_DEFLATED,
9058 NULL);
9059 gnm_hash_table_foreach_ordered
9060 (state.graphs,
9061 (GHFunc) odf_write_graphs,
9062 by_value_str,
9063 &state);
9064 gnm_hash_table_foreach_ordered
9065 (state.images,
9066 (GHFunc) odf_write_images,
9067 by_value_str,
9068 &state);
9069 if (NULL != pictures) {
9070 gsf_output_close (pictures);
9071 g_object_unref (pictures);
9074 /* Need to write the manifest */
9075 manifest = gsf_outfile_new_child_full
9076 (state.outfile, "META-INF/manifest.xml", FALSE,
9077 "compression-level", GSF_ZIP_DEFLATED,
9078 NULL);
9079 if (manifest) {
9080 odf_write_manifest (&state, manifest);
9081 gsf_output_close (manifest);
9082 g_object_unref (manifest);
9083 } else {
9084 /* Complain fiercely? */
9087 g_free (state.conv);
9088 if (state.openformula_namemap)
9089 g_hash_table_destroy (state.openformula_namemap);
9090 if (state.openformula_handlermap)
9091 g_hash_table_destroy (state.openformula_handlermap);
9093 go_io_value_progress_update (state.ioc, PROGRESS_STEPS);
9094 go_io_progress_unset (state.ioc);
9095 gsf_output_close (GSF_OUTPUT (state.outfile));
9096 g_object_unref (state.outfile);
9098 g_free (state.odf_version_string);
9100 gnm_pop_C_locale (locale);
9101 g_hash_table_unref (state.graphs);
9102 g_hash_table_unref (state.images);
9103 g_hash_table_unref (state.controls);
9104 g_hash_table_unref (state.named_cell_styles);
9105 g_hash_table_unref (state.named_cell_style_regions);
9106 g_hash_table_unref (state.cell_styles);
9107 g_hash_table_unref (state.so_styles);
9108 g_hash_table_unref (state.xl_styles);
9109 for (ui = 0; ui < G_N_ELEMENTS (state.style_names); ui++)
9110 g_hash_table_unref (state.style_names[ui]);
9111 g_hash_table_unref (state.graph_dashes);
9112 g_hash_table_unref (state.graph_hatches);
9113 g_hash_table_unref (state.graph_gradients);
9114 g_hash_table_unref (state.graph_fill_images);
9115 g_hash_table_unref (state.arrow_markers);
9116 g_hash_table_unref (state.text_colours);
9117 g_hash_table_unref (state.font_sizes);
9118 g_slist_free_full (state.col_styles, col_row_styles_free);
9119 g_slist_free_full (state.row_styles, col_row_styles_free);
9120 if (state.default_style_region)
9121 gnm_style_region_free (state.default_style_region);
9122 go_format_unref (state.time_fmt);
9123 go_format_unref (state.date_fmt);
9124 go_format_unref (state.date_long_fmt);
9126 ods_render_ops_clear (odf_render_ops);
9127 ods_render_ops_clear (odf_render_ops_to_xl);
9132 void
9133 openoffice_file_save (GOFileSaver const *fs, GOIOContext *ioc,
9134 WorkbookView const *wbv, GsfOutput *output);
9136 G_MODULE_EXPORT void
9137 openoffice_file_save (GOFileSaver const *fs, GOIOContext *ioc,
9138 WorkbookView const *wbv, GsfOutput *output)
9140 openoffice_file_save_real (fs, ioc, wbv, output, FALSE);
9143 void
9144 odf_file_save (GOFileSaver const *fs, GOIOContext *ioc,
9145 WorkbookView const *wbv, GsfOutput *output);
9147 G_MODULE_EXPORT void
9148 odf_file_save (GOFileSaver const *fs, GOIOContext *ioc,
9149 WorkbookView const *wbv, GsfOutput *output)
9151 openoffice_file_save_real (fs, ioc, wbv, output, TRUE);