1 
2 /*
3  * xml-sax-write.c : export .gnumeric and the clipboard subset using the sax
4  *			like wrappers in libgsf
5  *
6  * Copyright (C) 2003-2007 Jody Goldberg (jody@gnome.org)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) version 3.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 
24 /*****************************************************************************/
25 
26 #include <gnumeric-config.h>
27 #include <gnumeric.h>
28 #include <glib/gi18n-lib.h>
29 #include <xml-sax.h>
30 #include <workbook-view.h>
31 #include <gnm-format.h>
32 #include <workbook.h>
33 #include <workbook-priv.h> /* Workbook::names */
34 #include <cell.h>
35 #include <sheet.h>
36 #include <sheet-view.h>
37 #include <sheet-style.h>
38 #include <style-color.h>
39 #include <style-conditions.h>
40 #include <expr.h>
41 #include <expr-name.h>
42 #include <value.h>
43 #include <ranges.h>
44 #include <mstyle.h>
45 #include <style-border.h>
46 #include <validation.h>
47 #include <hlink.h>
48 #include <input-msg.h>
49 #include <tools/gnm-solver.h>
50 #include <sheet-filter.h>
51 #include <sheet-object-impl.h>
52 #include <sheet-object-cell-comment.h>
53 #include <print-info.h>
54 #include <gutils.h>
55 #include <clipboard.h>
56 #include <tools/scenarios.h>
57 #include <gnumeric-conf.h>
58 
59 #include <goffice/goffice.h>
60 #include <gsf/gsf-libxml.h>
61 #include <gsf/gsf-output-gzip.h>
62 #include <gsf/gsf-doc-meta-data.h>
63 #include <gsf/gsf-opendoc-utils.h>
64 #include <gsf/gsf-utils.h>
65 
66 typedef struct {
67 	WorkbookView const *wb_view;	/* View for the new workbook */
68 	Workbook const	   *wb;		/* The new workbook */
69 	Sheet const	   *sheet;
70 	GnmConventions	   *convs;
71 	GHashTable	   *expr_map;
72 	GString		   *cell_str;   /* Scratch pad.  */
73 
74 	// Do we write result values?  For now this is clipboard only
75 	gboolean            write_value_result;
76 
77 	GsfXMLOut *output;
78 } GnmOutputXML;
79 
80 #define GNM "gnm:"
81 
82 /* Precision to use when saving point measures. */
83 #define POINT_SIZE_PRECISION 4
84 
85 void
gnm_xml_out_add_gocolor(GsfXMLOut * o,char const * id,GOColor c)86 gnm_xml_out_add_gocolor (GsfXMLOut *o, char const *id, GOColor c)
87 {
88 	/*
89 	 * This uses format "rrrr:gggg:bbbb" or "rrrr:gggg:bbbb:aaaa"
90 	 * using hex numbers, i.e., the numbers are in the range from
91 	 * 0 to FFFF.
92 	 *
93 	 * Note, that while go_xml_out_add_color exists, we cannot use
94 	 * it as it using a 0-255 scaling and always includes alpha.
95 	 */
96 	unsigned r, g, b, a;
97 	char buf[4 * 4 * sizeof (unsigned int) + 1];
98 
99 	GO_COLOR_TO_RGBA (c, &r, &g, &b, &a);
100 
101 	sprintf (buf, "%X:%X:%X%c%X",
102 		 r * 0x101, g * 0x101, b * 0x101,
103 		 (a == 0xff ? 0 : ':'),
104 		 a * 0x101);
105 	gsf_xml_out_add_cstr_unchecked (o, id, buf);
106 }
107 
108 static void
gnm_xml_out_add_color(GsfXMLOut * o,char const * id,GnmColor const * c)109 gnm_xml_out_add_color (GsfXMLOut *o, char const *id, GnmColor const *c)
110 {
111 	gnm_xml_out_add_gocolor (o, id, c->go_color);
112 }
113 
114 static void
gnm_xml_out_add_cellpos(GsfXMLOut * o,char const * id,GnmCellPos const * p)115 gnm_xml_out_add_cellpos (GsfXMLOut *o, char const *id, GnmCellPos const *p)
116 {
117 	g_return_if_fail (p != NULL);
118 	gsf_xml_out_add_cstr_unchecked (o, id, cellpos_as_string (p));
119 }
120 
121 static void
xml_out_add_range(GsfXMLOut * xml,GnmRange const * r)122 xml_out_add_range (GsfXMLOut *xml, GnmRange const *r)
123 {
124 	g_return_if_fail (range_is_sane (r));
125 
126 	gsf_xml_out_add_int (xml, "startCol", r->start.col);
127 	gsf_xml_out_add_int (xml, "startRow", r->start.row);
128 	gsf_xml_out_add_int (xml, "endCol",   r->end.col);
129 	gsf_xml_out_add_int (xml, "endRow",   r->end.row);
130 }
131 
132 static void
xml_out_add_points(GsfXMLOut * xml,char const * name,double val)133 xml_out_add_points (GsfXMLOut *xml, char const *name, double val)
134 {
135 	gsf_xml_out_add_float (xml, name, val, POINT_SIZE_PRECISION);
136 }
137 
138 static void
xml_write_boolean_attribute(GnmOutputXML * state,char const * name,gboolean value)139 xml_write_boolean_attribute (GnmOutputXML *state, char const *name, gboolean value)
140 {
141 	gsf_xml_out_start_element (state->output, GNM "Attribute");
142 	gsf_xml_out_simple_element (state->output, GNM "name", name);
143 	gsf_xml_out_simple_element (state->output, GNM "value", value ? "TRUE" : "FALSE");
144 	gsf_xml_out_end_element (state->output); /* </Attribute> */
145 }
146 
147 static void
xml_write_version(GnmOutputXML * state)148 xml_write_version (GnmOutputXML *state)
149 {
150 	gsf_xml_out_start_element (state->output, GNM "Version");
151 	gsf_xml_out_add_int (state->output, "Epoch", GNM_VERSION_EPOCH);
152 	gsf_xml_out_add_int (state->output, "Major", GNM_VERSION_MAJOR);
153 	gsf_xml_out_add_int (state->output, "Minor", GNM_VERSION_MINOR);
154 	gsf_xml_out_add_cstr_unchecked (state->output, "Full", GNM_VERSION_FULL);
155 	gsf_xml_out_end_element (state->output); /* </Version> */
156 }
157 
158 static void
xml_write_attributes(GnmOutputXML * state)159 xml_write_attributes (GnmOutputXML *state)
160 {
161 	gsf_xml_out_start_element (state->output, GNM "Attributes");
162 	xml_write_boolean_attribute
163 		(state, "WorkbookView::show_horizontal_scrollbar",
164 		 state->wb_view->show_horizontal_scrollbar);
165 	xml_write_boolean_attribute
166 		(state, "WorkbookView::show_vertical_scrollbar",
167 		 state->wb_view->show_vertical_scrollbar);
168 	xml_write_boolean_attribute
169 		(state, "WorkbookView::show_notebook_tabs",
170 		 state->wb_view->show_notebook_tabs);
171 	xml_write_boolean_attribute
172 		(state, "WorkbookView::do_auto_completion",
173 		 state->wb_view->do_auto_completion);
174 	xml_write_boolean_attribute
175 		(state, "WorkbookView::is_protected",
176 		 state->wb_view->is_protected);
177 	gsf_xml_out_end_element (state->output); /* </Attributes> */
178 }
179 
180 static void
xml_write_meta_data(GnmOutputXML * state)181 xml_write_meta_data (GnmOutputXML *state)
182 {
183 	gsf_doc_meta_data_write_to_odf (go_doc_get_meta_data (GO_DOC (state->wb)),
184 	                            state->output);
185 }
186 
187 /* DEPRECATED in 1.7.11 */
188 static void
xml_write_conventions(GnmOutputXML * state)189 xml_write_conventions (GnmOutputXML *state)
190 {
191 	GODateConventions const *conv = workbook_date_conv (state->wb);
192 	if (conv->use_1904)
193 		gsf_xml_out_simple_element (state->output, GNM "DateConvention", "1904");
194 }
195 
196 static void
xml_write_sheet_names(GnmOutputXML * state)197 xml_write_sheet_names (GnmOutputXML *state)
198 {
199 	int i, n = workbook_sheet_count (state->wb);
200 	Sheet *sheet;
201 
202 	gsf_xml_out_start_element (state->output, GNM "SheetNameIndex");
203 	for (i = 0 ; i < n ; i++) {
204 		sheet = workbook_sheet_by_index (state->wb, i);
205 		gsf_xml_out_start_element (state->output, GNM "SheetName");
206 
207 		/*
208 		 * Note, that we explicitly namespace these attributes.
209 		 * That is not wrong, per se, but note that Gnumeric until
210 		 * 1.12.22 will not read files without this explicit name-
211 		 * space and that the abbreviation must be "gnm".
212 		 */
213 		if (sheet->sheet_type == GNM_SHEET_OBJECT)
214 			gsf_xml_out_add_cstr (state->output, GNM "SheetType", "object");
215 		gsf_xml_out_add_int (state->output, GNM "Cols",
216 				     gnm_sheet_get_max_cols (sheet));
217 		gsf_xml_out_add_int (state->output, GNM "Rows",
218 				     gnm_sheet_get_max_rows (sheet));
219 		gsf_xml_out_add_cstr (state->output, NULL, sheet->name_unquoted);
220 		gsf_xml_out_end_element (state->output); /* </gnm:SheetName> */
221 	}
222 	gsf_xml_out_end_element (state->output); /* </gnm:SheetNameIndex> */
223 }
224 
225 static void
xml_write_name(GnmOutputXML * state,GnmNamedExpr * nexpr)226 xml_write_name (GnmOutputXML *state, GnmNamedExpr *nexpr)
227 {
228 	char *expr_str;
229 
230 	g_return_if_fail (nexpr != NULL);
231 
232 	gsf_xml_out_start_element (state->output, GNM "Name");
233 	gsf_xml_out_simple_element (state->output, GNM "name",
234 				    expr_name_name (nexpr));
235 	expr_str = expr_name_as_string (nexpr, NULL, state->convs);
236 	gsf_xml_out_simple_element (state->output, GNM "value", expr_str);
237 	g_free (expr_str);
238 	gsf_xml_out_simple_element (state->output, GNM "position",
239 		cellpos_as_string (&nexpr->pos.eval));
240 	gsf_xml_out_end_element (state->output); /* </gnm:Name> */
241 }
242 
243 static void
xml_write_named_expressions(GnmOutputXML * state,GnmNamedExprCollection * scope)244 xml_write_named_expressions (GnmOutputXML *state, GnmNamedExprCollection *scope)
245 {
246 	GSList *names =
247 		g_slist_sort (gnm_named_expr_collection_list (scope),
248 			      (GCompareFunc)expr_name_cmp_by_name);
249 	GSList *p;
250 
251 	if (!names)
252 		return;
253 
254 	gsf_xml_out_start_element (state->output, GNM "Names");
255 	for (p = names; p; p = p->next) {
256 		GnmNamedExpr *nexpr = p->data;
257 		xml_write_name (state, nexpr);
258 	}
259 	gsf_xml_out_end_element (state->output); /* </gnm:Names> */
260 	g_slist_free (names);
261 }
262 
263 static void
xml_write_geometry(GnmOutputXML * state)264 xml_write_geometry (GnmOutputXML *state)
265 {
266 	if (state->wb_view->preferred_width > 0 ||
267 	    state->wb_view->preferred_height > 0) {
268 		gsf_xml_out_start_element (state->output, GNM "Geometry");
269 		gsf_xml_out_add_int (state->output, "Width", state->wb_view->preferred_width);
270 		gsf_xml_out_add_int (state->output, "Height", state->wb_view->preferred_height);
271 		gsf_xml_out_end_element (state->output); /* </gnm:Geometry> */
272 	}
273 }
274 
275 static void
xml_write_print_unit(GnmOutputXML * state,char const * name,double points,GtkUnit unit)276 xml_write_print_unit (GnmOutputXML *state, char const *name,
277 		      double points, GtkUnit unit)
278 {
279 	gsf_xml_out_start_element (state->output, name);
280 	xml_out_add_points (state->output, "Points", points);
281 	gsf_xml_out_add_cstr_unchecked (state->output, "PrefUnit",
282 					unit_to_unit_name (unit));
283 	gsf_xml_out_end_element (state->output);
284 }
285 
286 static void
xml_write_print_repeat_range(GnmOutputXML * state,char const * name,const char * range)287 xml_write_print_repeat_range (GnmOutputXML *state,
288 			      char const *name,
289 			      const char *range)
290 {
291 	if (range && *range) {
292 		gsf_xml_out_start_element (state->output, name);
293 		gsf_xml_out_add_cstr_unchecked (state->output, "value", range);
294 		gsf_xml_out_end_element (state->output);
295 	}
296 }
297 
298 static void
xml_write_print_hf(GnmOutputXML * state,char const * name,GnmPrintHF const * hf)299 xml_write_print_hf (GnmOutputXML *state, char const *name,
300 		    GnmPrintHF const *hf)
301 {
302 	gsf_xml_out_start_element (state->output, name);
303 	gsf_xml_out_add_cstr (state->output, "Left", hf->left_format);
304 	gsf_xml_out_add_cstr (state->output, "Middle", hf->middle_format);
305 	gsf_xml_out_add_cstr (state->output, "Right", hf->right_format);
306 	gsf_xml_out_end_element (state->output);
307 
308 }
309 
310 static void
xml_write_breaks(GnmOutputXML * state,GnmPageBreaks * breaks)311 xml_write_breaks (GnmOutputXML *state, GnmPageBreaks *breaks)
312 {
313 	GArray const *details = breaks->details;
314 	GnmPageBreak const *binfo;
315 	unsigned i;
316 
317 	gsf_xml_out_start_element (state->output,
318 		(breaks->is_vert) ? GNM "vPageBreaks" : GNM "hPageBreaks");
319 	gsf_xml_out_add_int (state->output, "count", details->len);
320 
321 	for (i = 0 ; i < details->len ; i++) {
322 		binfo = &g_array_index (details, GnmPageBreak, i);
323 		gsf_xml_out_start_element (state->output, GNM "break");
324 		gsf_xml_out_add_int (state->output, "pos", binfo->pos);
325 		if (binfo->type == GNM_PAGE_BREAK_MANUAL)
326 			gsf_xml_out_add_cstr_unchecked  (state->output, "type", "manual");
327 		else if (binfo->type == GNM_PAGE_BREAK_DATA_SLICE)
328 			gsf_xml_out_add_cstr_unchecked  (state->output, "type", "data-slice");
329 		else if (binfo->type == GNM_PAGE_BREAK_AUTO)
330 			gsf_xml_out_add_cstr_unchecked  (state->output, "type", "auto");
331 		gsf_xml_out_end_element (state->output); /* </break> */
332 	}
333 	gsf_xml_out_end_element (state->output);
334 }
335 
336 static void
xml_write_print_info(GnmOutputXML * state,GnmPrintInformation * pi)337 xml_write_print_info (GnmOutputXML *state, GnmPrintInformation *pi)
338 {
339 	char  *paper_name;
340 	char const *uri;
341 	double header;
342 	double footer;
343 	double left;
344 	double right;
345 	double edge_to_above_footer;
346 	double edge_to_below_header;
347 	GtkPageOrientation orient;
348 
349 	g_return_if_fail (pi != NULL);
350 
351 	gsf_xml_out_start_element (state->output, GNM "PrintInformation");
352 
353 	gsf_xml_out_start_element (state->output, GNM "Margins");
354 
355 	print_info_get_margins (pi, &header, &footer, &left, &right,
356 				&edge_to_below_header, &edge_to_above_footer);
357 	xml_write_print_unit (state, GNM "top", edge_to_below_header,
358 			      pi->desired_display.header);
359 	xml_write_print_unit (state, GNM "bottom", edge_to_above_footer,
360 			      pi->desired_display.footer);
361 	xml_write_print_unit (state, GNM "left", left,
362 			      pi->desired_display.left);
363 	xml_write_print_unit (state, GNM "right", right,
364 			      pi->desired_display.right);
365 	xml_write_print_unit (state, GNM "header", header,
366 			      pi->desired_display.top);
367 	xml_write_print_unit (state, GNM "footer", footer,
368 			      pi->desired_display.bottom);
369 	gsf_xml_out_end_element (state->output);
370 
371 	gsf_xml_out_start_element (state->output, GNM "Scale");
372 	if (pi->scaling.type == PRINT_SCALE_PERCENTAGE) {
373 		gsf_xml_out_add_cstr_unchecked  (state->output, "type", "percentage");
374 		go_xml_out_add_double  (state->output, "percentage", pi->scaling.percentage.x);
375 	} else {
376 		gsf_xml_out_add_cstr_unchecked  (state->output, "type", "size_fit");
377 		go_xml_out_add_double  (state->output, "cols", pi->scaling.dim.cols);
378 		go_xml_out_add_double  (state->output, "rows", pi->scaling.dim.rows);
379 	}
380 	gsf_xml_out_end_element (state->output);
381 
382 	gsf_xml_out_start_element (state->output, GNM "vcenter");
383 	gsf_xml_out_add_int  (state->output, "value", pi->center_vertically);
384 	gsf_xml_out_end_element (state->output);
385 
386 	gsf_xml_out_start_element (state->output, GNM "hcenter");
387 	gsf_xml_out_add_int  (state->output, "value", pi->center_horizontally);
388 	gsf_xml_out_end_element (state->output);
389 
390 	gsf_xml_out_start_element (state->output, GNM "grid");
391 	gsf_xml_out_add_int  (state->output, "value",    pi->print_grid_lines);
392 	gsf_xml_out_end_element (state->output);
393 
394 	gsf_xml_out_start_element (state->output, GNM "even_if_only_styles");
395 	gsf_xml_out_add_int  (state->output, "value",    pi->print_even_if_only_styles);
396 	gsf_xml_out_end_element (state->output);
397 
398 	gsf_xml_out_start_element (state->output, GNM "monochrome");
399 	gsf_xml_out_add_int  (state->output, "value",    pi->print_black_and_white);
400 	gsf_xml_out_end_element (state->output);
401 
402 	gsf_xml_out_start_element (state->output, GNM "draft");
403 	gsf_xml_out_add_int  (state->output, "value",    pi->print_as_draft);
404 	gsf_xml_out_end_element (state->output);
405 
406 	gsf_xml_out_start_element (state->output, GNM "titles");
407 	gsf_xml_out_add_int  (state->output, "value",    pi->print_titles);
408 	gsf_xml_out_end_element (state->output);
409 
410 	gsf_xml_out_start_element (state->output, GNM "do_not_print");
411 	gsf_xml_out_add_int  (state->output, "value",    pi->do_not_print);
412 	gsf_xml_out_end_element (state->output);
413 
414 	gsf_xml_out_start_element (state->output, GNM "print_range");
415 	gsf_xml_out_add_enum (state->output, "value",
416 			      GNM_PRINT_RANGE_TYPE,
417 			      print_info_get_printrange (pi) );
418 	gsf_xml_out_end_element (state->output);
419 
420 	xml_write_print_repeat_range (state, GNM "repeat_top", pi->repeat_top);
421 	xml_write_print_repeat_range (state, GNM "repeat_left", pi->repeat_left);
422 
423 	/* this was once an enum, hence the silly strings */
424 	gsf_xml_out_simple_element (state->output, GNM "order",
425 		pi->print_across_then_down ? "r_then_d" :"d_then_r");
426 
427 	orient = print_info_get_paper_orientation (pi);
428 	gsf_xml_out_simple_element (state->output, GNM "orientation",
429 		(orient == GTK_PAGE_ORIENTATION_PORTRAIT
430 		 || orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
431 				    ? "portrait" : "landscape");
432 #warning TODO: we should also handle inversion
433 
434 	xml_write_print_hf (state, GNM "Header", pi->header);
435 	xml_write_print_hf (state, GNM "Footer", pi->footer);
436 
437 	paper_name = print_info_get_paper (pi);
438 	if (paper_name)
439 		gsf_xml_out_simple_element (state->output, GNM "paper",
440 					    paper_name);
441 	g_free (paper_name);
442 
443 	uri = print_info_get_printtofile_uri (pi);
444 	if (uri)
445 		gsf_xml_out_simple_element (state->output, GNM "print-to-uri",
446 					    uri);
447 
448 	if (NULL != pi->page_breaks.v)
449 		xml_write_breaks (state, pi->page_breaks.v);
450 	if (NULL != pi->page_breaks.h)
451 		xml_write_breaks (state, pi->page_breaks.h);
452 
453 	gsf_xml_out_start_element (state->output, GNM "comments");
454 	gsf_xml_out_add_enum (state->output, "placement",
455 			      GNM_PRINT_COMMENT_PLACEMENT_TYPE,
456 			      pi->comment_placement);
457 	gsf_xml_out_end_element (state->output);
458 
459 	gsf_xml_out_start_element (state->output, GNM "errors");
460 	gsf_xml_out_add_enum (state->output, "PrintErrorsAs",
461 			      GNM_PRINT_ERRORS_TYPE,
462 			      pi->error_display);
463 	gsf_xml_out_end_element (state->output);
464 
465 	gsf_xml_out_end_element (state->output);
466 }
467 
468 static void
xml_write_style(GnmOutputXML * state,GnmStyle const * style)469 xml_write_style (GnmOutputXML *state, GnmStyle const *style)
470 {
471 	static char const *border_names[] = {
472 		GNM "Top",
473 		GNM "Bottom",
474 		GNM "Left",
475 		GNM "Right",
476 		GNM "Rev-Diagonal",
477 		GNM "Diagonal"
478 	};
479 	GnmValidation const *v;
480 	GnmHLink   const *lnk;
481 	GnmInputMsg const *im;
482 	GnmStyleConditions const *sc;
483 	GnmStyleBorderType t;
484 	unsigned i;
485 	gboolean started;
486 
487 	gsf_xml_out_start_element (state->output, GNM "Style");
488 
489 	if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H))
490 		gsf_xml_out_add_enum (state->output, "HAlign",
491 				      GNM_ALIGN_H_TYPE,
492 				      gnm_style_get_align_h (style));
493 	if (gnm_style_is_element_set (style, MSTYLE_ALIGN_V))
494 		gsf_xml_out_add_enum (state->output, "VAlign",
495 				      GNM_ALIGN_V_TYPE,
496 				      gnm_style_get_align_v (style));
497 	if (gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT))
498 		gsf_xml_out_add_bool (state->output, "WrapText",
499 				      gnm_style_get_wrap_text (style));
500 	if (gnm_style_is_element_set (style, MSTYLE_SHRINK_TO_FIT))
501 		gsf_xml_out_add_bool (state->output, "ShrinkToFit",
502 				      gnm_style_get_shrink_to_fit (style));
503 	if (gnm_style_is_element_set (style, MSTYLE_ROTATION))
504 		gsf_xml_out_add_int (state->output, "Rotation",
505 				     gnm_style_get_rotation (style));
506 	if (gnm_style_is_element_set (style, MSTYLE_PATTERN))
507 		gsf_xml_out_add_int (state->output, "Shade",
508 				     gnm_style_get_pattern (style));
509 	if (gnm_style_is_element_set (style, MSTYLE_INDENT))
510 		gsf_xml_out_add_int (state->output, "Indent", gnm_style_get_indent (style));
511 	if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_LOCKED))
512 		gsf_xml_out_add_bool (state->output, "Locked",
513 				      gnm_style_get_contents_locked (style));
514 	if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN))
515 		gsf_xml_out_add_bool (state->output, "Hidden",
516 				      gnm_style_get_contents_hidden (style));
517 	if (gnm_style_is_element_set (style, MSTYLE_FONT_COLOR))
518 		gnm_xml_out_add_color (state->output, "Fore",
519 				       gnm_style_get_font_color (style));
520 	if (gnm_style_is_element_set (style, MSTYLE_COLOR_BACK))
521 		gnm_xml_out_add_color (state->output, "Back",
522 				       gnm_style_get_back_color (style));
523 	if (gnm_style_is_element_set (style, MSTYLE_COLOR_PATTERN))
524 		gnm_xml_out_add_color (state->output, "PatternColor",
525 				       gnm_style_get_pattern_color (style));
526 	if (gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
527 		const char *fmt = go_format_as_XL (gnm_style_get_format (style));
528 		gsf_xml_out_add_cstr (state->output, "Format", fmt);
529 	}
530 
531 	if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME) ||
532 	    gnm_style_is_element_set (style, MSTYLE_FONT_SIZE) ||
533 	    gnm_style_is_element_set (style, MSTYLE_FONT_BOLD) ||
534 	    gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC) ||
535 	    gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
536 	    gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
537 	    gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT)) {
538 		gsf_xml_out_start_element (state->output, GNM "Font");
539 
540 		if (gnm_style_is_element_set (style, MSTYLE_FONT_SIZE))
541 			xml_out_add_points (state->output, "Unit", gnm_style_get_font_size (style));
542 		if (gnm_style_is_element_set (style, MSTYLE_FONT_BOLD))
543 			gsf_xml_out_add_int (state->output, "Bold", gnm_style_get_font_bold (style));
544 		if (gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC))
545 			gsf_xml_out_add_int (state->output, "Italic", gnm_style_get_font_italic (style));
546 		if (gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE))
547 			gsf_xml_out_add_int (state->output, "Underline", (int)gnm_style_get_font_uline (style));
548 		if (gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
549 			gsf_xml_out_add_int (state->output, "StrikeThrough", gnm_style_get_font_strike (style));
550 		if (gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT))
551 			gsf_xml_out_add_int (state->output, "Script", (int)gnm_style_get_font_script (style));
552 
553 		if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME)) {
554 			const char *fontname = gnm_style_get_font_name (style);
555 			gsf_xml_out_add_cstr (state->output, NULL, fontname);
556 		}
557 
558 		gsf_xml_out_end_element (state->output);
559 	}
560 
561 	if (gnm_style_is_element_set (style, MSTYLE_HLINK) &&
562 	    NULL != (lnk = gnm_style_get_hlink (style))) {
563 		gsf_xml_out_start_element (state->output, GNM "HyperLink");
564 		gsf_xml_out_add_cstr (state->output, "type", g_type_name (G_OBJECT_TYPE (lnk)));
565 		gsf_xml_out_add_cstr (state->output, "target", gnm_hlink_get_target (lnk));
566 		if (gnm_hlink_get_tip (lnk) != NULL)
567 			gsf_xml_out_add_cstr (state->output, "tip", gnm_hlink_get_tip (lnk));
568 		gsf_xml_out_end_element (state->output);
569 	}
570 
571 	if (gnm_style_is_element_set (style, MSTYLE_VALIDATION) &&
572 	    NULL != (v = gnm_style_get_validation (style))) {
573 		unsigned ui;
574 
575 		gsf_xml_out_start_element (state->output, GNM "Validation");
576 		gsf_xml_out_add_enum (state->output, "Style",
577 				      GNM_VALIDATION_STYLE_TYPE, v->style);
578 		gsf_xml_out_add_enum (state->output, "Type",
579 				      GNM_VALIDATION_TYPE_TYPE, v->type);
580 
581 		switch (v->type) {
582 		case GNM_VALIDATION_TYPE_AS_INT:
583 		case GNM_VALIDATION_TYPE_AS_NUMBER:
584 		case GNM_VALIDATION_TYPE_AS_DATE:
585 		case GNM_VALIDATION_TYPE_AS_TIME:
586 		case GNM_VALIDATION_TYPE_TEXT_LENGTH:
587 			gsf_xml_out_add_enum (state->output, "Operator",
588 					      GNM_VALIDATION_OP_TYPE, v->op);
589 			break;
590 		default:
591 			break;
592 		}
593 
594 		gsf_xml_out_add_bool (state->output, "AllowBlank", v->allow_blank);
595 		gsf_xml_out_add_bool (state->output, "UseDropdown", v->use_dropdown);
596 
597 		if (v->title != NULL && v->title->str[0] != '\0')
598 			gsf_xml_out_add_cstr (state->output, "Title", v->title->str);
599 		if (v->msg != NULL && v->msg->str[0] != '\0')
600 			gsf_xml_out_add_cstr (state->output, "Message", v->msg->str);
601 
602 		for (ui = 0; ui < G_N_ELEMENTS (v->deps); ui++) {
603 			GnmExprTop const *texpr = dependent_managed_get_expr (&v->deps[ui]);
604 			if (texpr) {
605 				const char *elem = ui == 0 ? GNM "Expression0" : GNM "Expression1";
606 				char *tmp;
607 				GnmParsePos pp;
608 				parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
609 				tmp = gnm_expr_top_as_string (texpr, &pp, state->convs);
610 				gsf_xml_out_simple_element (state->output, elem, tmp);
611 				g_free (tmp);
612 			}
613 		}
614 		gsf_xml_out_end_element (state->output); /* </Validation> */
615 	}
616 
617 	if (gnm_style_is_element_set (style, MSTYLE_INPUT_MSG) &&
618 	    NULL != (im = gnm_style_get_input_msg (style))) {
619 		char const *txt;
620 		gsf_xml_out_start_element (state->output, GNM "InputMessage");
621 		if (NULL != (txt = gnm_input_msg_get_title (im)))
622 			gsf_xml_out_add_cstr (state->output, "Title", txt);
623 		if (NULL != (txt = gnm_input_msg_get_msg (im)))
624 			gsf_xml_out_add_cstr (state->output, "Message", txt);
625 		gsf_xml_out_end_element (state->output); /* </InputMessage> */
626 	}
627 
628 	if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
629 	    NULL != (sc = gnm_style_get_conditions (style))) {
630 		GPtrArray const *conds = gnm_style_conditions_details (sc);
631 		if (conds != NULL) {
632 			GnmParsePos pp;
633 			parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
634 
635 			for (i = 0 ; i < conds->len ; i++) {
636 				unsigned ui;
637 				GnmStyleCond const *cond =
638 					g_ptr_array_index (conds, i);
639 				gsf_xml_out_start_element (state->output, GNM "Condition");
640 				gsf_xml_out_add_int (state->output, "Operator", cond->op);
641 				for (ui = 0; ui < 2; ui++) {
642 					GnmExprTop const *texpr = gnm_style_cond_get_expr (cond, ui);
643 					char *tmp = texpr
644 						? gnm_expr_top_as_string (texpr, &pp, state->convs)
645 						: NULL;
646 					const char *attr = (ui == 0)
647 						? GNM "Expression0"
648 						: GNM "Expression1";
649 					if (tmp) {
650 						gsf_xml_out_simple_element (state->output, attr, tmp);
651 						g_free (tmp);
652 					}
653 				}
654 				xml_write_style (state, cond->overlay);
655 				gsf_xml_out_end_element (state->output); /* </Condition> */
656 			}
657 		}
658 	}
659 
660 	started = FALSE;
661 	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
662 		GnmBorder const *border;
663 		if (gnm_style_is_element_set (style, i) &&
664 		    NULL != (border = gnm_style_get_border (style, i)) &&
665 		    GNM_STYLE_BORDER_NONE != (t = border->line_type)) {
666 			GnmColor const *col   = border->color;
667 
668 			if (!started) {
669 				gsf_xml_out_start_element (state->output, GNM "StyleBorder");
670 				started = TRUE;
671 			}
672 
673 			gsf_xml_out_start_element (state->output,
674 				border_names [i - MSTYLE_BORDER_TOP]);
675 			gsf_xml_out_add_int (state->output, "Style", t);
676 			gnm_xml_out_add_color (state->output, "Color", col);
677 			gsf_xml_out_end_element (state->output);
678 		}
679 	}
680 	if (started)
681 		gsf_xml_out_end_element (state->output);
682 
683 	gsf_xml_out_end_element (state->output);
684 }
685 
686 static void
xml_write_style_region(GnmOutputXML * state,GnmStyleRegion const * region)687 xml_write_style_region (GnmOutputXML *state, GnmStyleRegion const *region)
688 {
689 	gsf_xml_out_start_element (state->output, GNM "StyleRegion");
690 	xml_out_add_range (state->output, &region->range);
691 	if (region->style != NULL)
692 		xml_write_style (state, region->style);
693 	gsf_xml_out_end_element (state->output);
694 }
695 
696 static int
cb_sheet_style_order(GnmStyleRegion const * a,GnmStyleRegion const * b)697 cb_sheet_style_order (GnmStyleRegion const *a, GnmStyleRegion const *b)
698 {
699 	GnmRange const *ra = &a->range;
700 	GnmRange const *rb = &b->range;
701 	int res;
702 
703 	res = ra->start.col - rb->start.col;
704 
705 	if (res == 0)
706 		res = ra->start.row - rb->start.row;
707 
708 	return res;
709 }
710 
711 static void
xml_write_styles(GnmOutputXML * state)712 xml_write_styles (GnmOutputXML *state)
713 {
714 	GnmStyleList *styles =
715 		g_slist_sort (sheet_style_get_range (state->sheet, NULL),
716 			      (GCompareFunc)cb_sheet_style_order);
717 	if (styles != NULL) {
718 		GnmStyleList *ptr;
719 
720 		gsf_xml_out_start_element (state->output, GNM "Styles");
721 		for (ptr = styles; ptr; ptr = ptr->next)
722 			xml_write_style_region (state, ptr->data);
723 		gsf_xml_out_end_element (state->output);
724 		style_list_free (styles);
725 	}
726 }
727 
728 typedef struct {
729 	GnmOutputXML *state;
730 	gboolean is_column;
731 	ColRowInfo prev;
732 	int prev_pos, rle_count;
733 	GnmCellRegion const *cr;
734 } closure_write_colrow;
735 
736 static gboolean
xml_write_colrow_info(GnmColRowIter const * iter,closure_write_colrow * closure)737 xml_write_colrow_info (GnmColRowIter const *iter, closure_write_colrow *closure)
738 {
739 	ColRowInfo const *prev = &closure->prev;
740 	GsfXMLOut *output = closure->state->output;
741 	ColRowInfo const *def =
742 		sheet_colrow_get_default (closure->state->sheet,
743 					  closure->is_column);
744 
745 	closure->rle_count++;
746 	if (NULL != iter && col_row_info_equal (prev, iter->cri))
747 		return FALSE;
748 
749 	if (closure->prev_pos != -1 && !col_row_info_equal (prev, def)) {
750 		if (closure->is_column)
751 			gsf_xml_out_start_element (output, GNM "ColInfo");
752 		else
753 			gsf_xml_out_start_element (output, GNM "RowInfo");
754 
755 		gsf_xml_out_add_int (output, "No", closure->prev_pos);
756 		xml_out_add_points (output, "Unit", prev->size_pts);
757 		if (prev->hard_size)
758 			gsf_xml_out_add_bool (output, "HardSize", TRUE);
759 		if (!prev->visible)
760 			gsf_xml_out_add_bool (output, "Hidden", TRUE);
761 		if (prev->is_collapsed)
762 			gsf_xml_out_add_bool (output, "Collapsed", TRUE);
763 		if (prev->outline_level > 0)
764 			gsf_xml_out_add_int (output, "OutlineLevel", prev->outline_level);
765 
766 		if (closure->rle_count > 1)
767 			gsf_xml_out_add_int (output, "Count", closure->rle_count);
768 		gsf_xml_out_end_element (output);
769 	}
770 
771 	closure->rle_count = 0;
772 	if (NULL != iter) {
773 		closure->prev = *iter->cri;
774 		closure->prev_pos = iter->pos;
775 	}
776 
777 	return FALSE;
778 }
779 
780 static void
xml_write_cols_rows(GnmOutputXML * state,GnmCellRegion const * cr)781 xml_write_cols_rows (GnmOutputXML *state, GnmCellRegion const *cr)
782 {
783 	const Sheet *sheet = state->sheet;
784 	int i;
785 
786 	for (i = 0; i < 2; i++) {
787 		closure_write_colrow closure;
788 		gboolean is_cols = (i == 0);
789 
790 		gsf_xml_out_start_element (state->output,
791 					   is_cols ? GNM "Cols" : GNM "Rows");
792 
793 		if (sheet)
794 			xml_out_add_points
795 				(state->output, "DefaultSizePts",
796 				 sheet_colrow_get_default (sheet, is_cols)->size_pts);
797 
798 		closure.state = state;
799 		closure.cr = cr;
800 		closure.is_column = is_cols;
801 		memset (&closure.prev, 0, sizeof (closure.prev));
802 		closure.prev_pos = -1;
803 		closure.rle_count = 0;
804 		if (cr)
805 			colrow_state_list_foreach
806 				(is_cols ? cr->col_state : cr->row_state,
807 				 sheet, is_cols,
808 				 is_cols ? cr->base.col : cr->base.row,
809 				 (ColRowHandler)&xml_write_colrow_info,
810 				 &closure);
811 		else
812 			sheet_colrow_foreach
813 				(sheet, is_cols, 0, -1,
814 				 (ColRowHandler)&xml_write_colrow_info,
815 				 &closure);
816 		xml_write_colrow_info (NULL, &closure); /* flush */
817 		gsf_xml_out_end_element (state->output); /* </gnm:Cols> */
818 	}
819 }
820 
821 static void
xml_write_selection_info(GnmOutputXML * state)822 xml_write_selection_info (GnmOutputXML *state)
823 {
824 	GSList *ptr, *copy;
825 	SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
826 	if (!sv) return;  /* Hidden.  */
827 
828 	gsf_xml_out_start_element (state->output, GNM "Selections");
829 	gsf_xml_out_add_int (state->output, "CursorCol", sv->edit_pos_real.col);
830 	gsf_xml_out_add_int (state->output, "CursorRow", sv->edit_pos_real.row);
831 
832 	/* Insert the selections in REVERSE order */
833 	copy = g_slist_reverse (g_slist_copy (sv->selections));
834 	for (ptr = copy; ptr; ptr = ptr->next) {
835 		GnmRange const *r = ptr->data;
836 		gsf_xml_out_start_element (state->output, GNM "Selection");
837 		xml_out_add_range (state->output, r);
838 		gsf_xml_out_end_element (state->output); /* </gnm:Selection> */
839 	}
840 	g_slist_free (copy);
841 
842 	gsf_xml_out_end_element (state->output); /* </gnm:Selections> */
843 }
844 
845 static void
xml_write_cell_and_position(GnmOutputXML * state,GnmExprTop const * texpr,GnmValue const * val,GnmParsePos const * pp)846 xml_write_cell_and_position (GnmOutputXML *state,
847 			     GnmExprTop const *texpr, GnmValue const *val,
848 			     GnmParsePos const *pp)
849 {
850 	gboolean write_contents = TRUE;
851 	gboolean const is_shared_expr = (texpr != NULL) &&
852 		gnm_expr_top_is_shared (texpr);
853 
854 	/* Only the top left corner of an array needs to be saved (>= 0.53) */
855 	if (texpr && gnm_expr_top_is_array_elem (texpr, NULL, NULL))
856 		return; /* DOM version would write <Cell Col= Row=/> */
857 
858 	gsf_xml_out_start_element (state->output, GNM "Cell");
859 	gsf_xml_out_add_int (state->output, "Row", pp->eval.row);
860 	gsf_xml_out_add_int (state->output, "Col", pp->eval.col);
861 
862 	/* As of version 0.53 we save the ID of shared expressions */
863 	if (is_shared_expr) {
864 		gpointer id = g_hash_table_lookup (state->expr_map, (gpointer) texpr);
865 
866 		if (id == NULL) {
867 			id = GINT_TO_POINTER (g_hash_table_size (state->expr_map) + 1);
868 			g_hash_table_insert (state->expr_map, (gpointer)texpr, id);
869 		} else
870 			write_contents = FALSE;
871 
872 		gsf_xml_out_add_int (state->output, "ExprID", GPOINTER_TO_INT (id));
873 	}
874 
875 	/* As of version 0.53 we save the size of the array as attributes */
876 	/* As of version 0.57 the attributes are in the Cell not the Content */
877 	if (texpr && gnm_expr_top_is_array_corner (texpr)) {
878 		int cols, rows;
879 		gnm_expr_top_get_array_size (texpr, &cols, &rows);
880 		gsf_xml_out_add_int (state->output, "Rows", rows);
881 		gsf_xml_out_add_int (state->output, "Cols", cols);
882 	}
883 
884 	if (write_contents) {
885 		gboolean write_value = !texpr || state->write_value_result;
886 		GString *str = state->cell_str;
887 
888 		g_string_truncate (str, 0);
889 
890 		if (write_value) {
891 			if (val != NULL) {
892 				gsf_xml_out_add_int (state->output, "ValueType", val->v_any.type);
893 				if (VALUE_FMT (val) != NULL) {
894 					const char *fmt = go_format_as_XL (VALUE_FMT (val));
895 					gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
896 				}
897 				value_get_as_gstring (val, str, state->convs);
898 				if (texpr) {
899 					gsf_xml_out_add_cstr (state->output, "Value", str->str);
900 					g_string_truncate (str, 0);
901 				}
902 			} else {
903 				g_warning ("%s has no value ?", cellpos_as_string (&pp->eval));
904 			}
905 		}
906 
907 		if (texpr) {
908 			GnmConventionsOut out;
909 			out.accum = str;
910 			out.pp    = pp;
911 			out.convs = state->convs;
912 
913 			g_string_append_c (str, '=');
914 			gnm_expr_top_as_gstring (texpr, &out);
915 		}
916 
917 		gsf_xml_out_add_cstr (state->output, NULL, str->str);
918 	}
919 	gsf_xml_out_end_element (state->output); /* </gnm:Cell> */
920 }
921 
922 static GnmValue *
cb_write_cell(GnmCellIter const * iter,GnmOutputXML * state)923 cb_write_cell (GnmCellIter const *iter, GnmOutputXML *state)
924 {
925 	GnmExprTop const *texpr = iter->cell->base.texpr;
926 	GnmValue const *value = iter->cell->value;
927 
928 	if (texpr == NULL && VALUE_IS_EMPTY (value))
929 		return NULL;
930 
931 	xml_write_cell_and_position (state, texpr, value, &iter->pp);
932 	return NULL;
933 }
934 
935 static void
xml_write_cells(GnmOutputXML * state)936 xml_write_cells (GnmOutputXML *state)
937 {
938 	gsf_xml_out_start_element (state->output, GNM "Cells");
939 	sheet_foreach_cell_in_region ((Sheet *)state->sheet,
940 				      CELL_ITER_IGNORE_NONEXISTENT,
941 				      0, 0, -1, -1,
942 				      (CellIterFunc) cb_write_cell, state);
943 	gsf_xml_out_end_element (state->output); /* </gnm:Cells> */
944 }
945 
946 static void
xml_write_merged_regions(GnmOutputXML * state)947 xml_write_merged_regions (GnmOutputXML *state)
948 {
949 	GSList *ptr = state->sheet->list_merged;
950 	if (ptr == NULL)
951 		return;
952 	gsf_xml_out_start_element (state->output, GNM "MergedRegions");
953 	for (; ptr != NULL ; ptr = ptr->next)
954 		gsf_xml_out_simple_element (state->output,
955 			GNM "Merge", range_as_string (ptr->data));
956 	gsf_xml_out_end_element (state->output); /* </gnm:MergedRegions> */
957 }
958 
959 static void
xml_write_sheet_layout(GnmOutputXML * state)960 xml_write_sheet_layout (GnmOutputXML *state)
961 {
962 	SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
963 	if (!sv) return;  /* Hidden.  */
964 
965 	gsf_xml_out_start_element (state->output, GNM "SheetLayout");
966 	gnm_xml_out_add_cellpos (state->output, "TopLeft", &sv->initial_top_left);
967 
968 	if (gnm_sheet_view_is_frozen (sv)) {
969 		gsf_xml_out_start_element (state->output, GNM "FreezePanes");
970 		gnm_xml_out_add_cellpos (state->output, "FrozenTopLeft", &sv->frozen_top_left);
971 		gnm_xml_out_add_cellpos (state->output, "UnfrozenTopLeft", &sv->unfrozen_top_left);
972 		gsf_xml_out_end_element (state->output); /* </gnm:FreezePanes> */
973 	}
974 	gsf_xml_out_end_element (state->output); /* </gnm:SheetLayout> */
975 }
976 
977 static void
xml_write_filter_expr(GnmOutputXML * state,GnmFilterCondition const * cond,unsigned i)978 xml_write_filter_expr (GnmOutputXML *state,
979 		       GnmFilterCondition const *cond, unsigned i)
980 {
981 	static char const *filter_cond_name[] = { "eq", "gt", "lt", "gte", "lte", "ne" };
982 	/*
983 	 * WARNING WARNING WARING
984 	 * Value and ValueType are _reversed !!!
985 	 */
986 	static struct { char const *op, *valtype, *val; } filter_expr_attrs[] = {
987 		{ "Op0", "Value0", "ValueType0" },
988 		{ "Op1", "Value1", "ValueType1" }
989 	};
990 
991 	GString *text = g_string_new (NULL);
992 	value_get_as_gstring (cond->value[i], text, state->convs);
993 	gsf_xml_out_add_cstr_unchecked (state->output,
994 		filter_expr_attrs[i].op, filter_cond_name [cond->op[i]]);
995 	gsf_xml_out_add_int (state->output,
996 		filter_expr_attrs[i].valtype, cond->value[i]->v_any.type);
997 	gsf_xml_out_add_cstr (state->output,
998 		filter_expr_attrs[i].val, text->str);
999 	g_string_free (text, TRUE);
1000 }
1001 
1002 static void
xml_write_filter_field(GnmOutputXML * state,GnmFilterCondition const * cond,unsigned i)1003 xml_write_filter_field (GnmOutputXML *state,
1004 			GnmFilterCondition const *cond, unsigned i)
1005 {
1006 	gsf_xml_out_start_element (state->output, GNM "Field");
1007 	gsf_xml_out_add_int (state->output, "Index", i);
1008 
1009 	switch (GNM_FILTER_OP_TYPE_MASK & cond->op[0]) {
1010 	case 0: gsf_xml_out_add_cstr_unchecked (state->output, "Type", "expr");
1011 		xml_write_filter_expr (state, cond, 0);
1012 		if (cond->op[1] != GNM_FILTER_UNUSED) {
1013 			xml_write_filter_expr (state, cond, 1);
1014 			gsf_xml_out_add_bool (state->output, "IsAnd", cond->is_and);
1015 		}
1016 		break;
1017 	case GNM_FILTER_OP_BLANKS:
1018 		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "blanks");
1019 		break;
1020 	case GNM_FILTER_OP_NON_BLANKS:
1021 		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "nonblanks");
1022 		break;
1023 	case GNM_FILTER_OP_TOP_N:
1024 		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "bucket");
1025 		gsf_xml_out_add_bool (state->output, "top",
1026 			cond->op[0] & 1 ? TRUE : FALSE);
1027 		gsf_xml_out_add_bool (state->output, "items",
1028 			cond->op[0] & 2 ? TRUE : FALSE);
1029 		go_xml_out_add_double (state->output, "count", cond->count);
1030 		break;
1031 	}
1032 
1033 	gsf_xml_out_end_element (state->output); /* </gnm:Field> */
1034 }
1035 
1036 static void
xml_write_sheet_filters(GnmOutputXML * state)1037 xml_write_sheet_filters (GnmOutputXML *state)
1038 {
1039 	GSList *ptr;
1040 	GnmFilter *filter;
1041 	GnmFilterCondition const *cond;
1042 	unsigned i;
1043 
1044 	if (state->sheet->filters == NULL)
1045 		return;
1046 
1047 	gsf_xml_out_start_element (state->output, GNM "Filters");
1048 
1049 	for (ptr = state->sheet->filters; ptr != NULL ; ptr = ptr->next) {
1050 		filter = ptr->data;
1051 		gsf_xml_out_start_element (state->output, GNM "Filter");
1052 		gsf_xml_out_add_cstr_unchecked (state->output, "Area",
1053 			range_as_string (&filter->r));
1054 
1055 		for (i = filter->fields->len ; i-- > 0 ; ) {
1056 			cond = gnm_filter_get_condition (filter, i);
1057 			if (cond != NULL && cond->op[0] != GNM_FILTER_UNUSED)
1058 				xml_write_filter_field (state, cond, i);
1059 		}
1060 
1061 		gsf_xml_out_end_element (state->output); /* </gnm:Filter> */
1062 	}
1063 
1064 	gsf_xml_out_end_element (state->output); /* </gnm:Filters> */
1065 }
1066 
1067 static void
xml_write_solver(GnmOutputXML * state)1068 xml_write_solver (GnmOutputXML *state)
1069 {
1070         GnmSolverParameters *param = state->sheet->solver_parameters;
1071 	GSList *ptr;
1072 	GnmCellRef const *target;
1073 	GnmValue const *input;
1074 
1075 	if (param == NULL)
1076 		return;
1077 
1078 	gsf_xml_out_start_element (state->output, GNM "Solver");
1079 
1080 	target = gnm_solver_param_get_target (param);
1081 	if (target != NULL) {
1082 		GnmExpr const *expr = gnm_expr_new_cellref (target);
1083 		GnmParsePos pp;
1084 		char *txt = gnm_expr_as_string
1085 			(expr,
1086 			 parse_pos_init_sheet (&pp, state->sheet),
1087 			 state->convs);
1088 		gsf_xml_out_add_cstr (state->output, "Target", txt);
1089 		g_free (txt);
1090 		gnm_expr_free (expr);
1091 	}
1092 
1093 	gsf_xml_out_add_int (state->output, "ModelType", param->options.model_type);
1094 	gsf_xml_out_add_int (state->output, "ProblemType", param->problem_type);
1095 	input = gnm_solver_param_get_input (param);
1096 	if (input)
1097 		gsf_xml_out_add_cstr (state->output, "Inputs",
1098 				      value_peek_string (input));
1099 	gsf_xml_out_add_int (state->output, "MaxTime",
1100 		param->options.max_time_sec);
1101 	gsf_xml_out_add_int (state->output, "MaxIter",
1102 		param->options.max_iter);
1103 	gsf_xml_out_add_bool (state->output, "NonNeg",
1104 		param->options.assume_non_negative);
1105 	gsf_xml_out_add_bool (state->output, "Discr",
1106 		param->options.assume_discrete);
1107 	gsf_xml_out_add_bool (state->output, "AutoScale",
1108 		param->options.automatic_scaling);
1109 	gsf_xml_out_add_bool (state->output, "ProgramR",
1110 		param->options.program_report);
1111 	gsf_xml_out_add_bool (state->output, "SensitivityR",
1112 		param->options.sensitivity_report);
1113 
1114 	for (ptr = param->constraints; ptr != NULL ; ptr = ptr->next) {
1115 		GnmSolverConstraint const *c = ptr->data;
1116 		int type;
1117 		GString *str = g_string_new (NULL);
1118 
1119 		/* Historical values.  Not a bit field.  */
1120 		switch (c->type) {
1121 		default:	 type = 0;	break;
1122 		case GNM_SOLVER_LE:   type = 1;	break;
1123 		case GNM_SOLVER_GE:   type = 2;	break;
1124 		case GNM_SOLVER_EQ:   type = 4;	break;
1125 		case GNM_SOLVER_INTEGER:  type = 8;	break;
1126 		case GNM_SOLVER_BOOLEAN: type = 16;	break;
1127 		}
1128 
1129 		gsf_xml_out_start_element (state->output, GNM "Constr");
1130 		gsf_xml_out_add_int (state->output, "Type", type);
1131 
1132 		gnm_solver_constraint_side_as_str (c, state->sheet, str, TRUE);
1133 		gsf_xml_out_add_cstr (state->output, "lhs", str->str);
1134 
1135 		if (gnm_solver_constraint_has_rhs (c)) {
1136 			g_string_truncate (str, 0);
1137 			gnm_solver_constraint_side_as_str (c, state->sheet,
1138 							   str, FALSE);
1139 			gsf_xml_out_add_cstr (state->output, "rhs", str->str);
1140 		}
1141 
1142 		gsf_xml_out_end_element (state->output); /* </gnm:Constr> */
1143 
1144 		g_string_free (str, TRUE);
1145 	}
1146 
1147 	gsf_xml_out_end_element (state->output); /* </gnm:Solver> */
1148 }
1149 
1150 static void
xml_write_scenario(GnmOutputXML * state,GnmScenario const * sc)1151 xml_write_scenario (GnmOutputXML *state, GnmScenario const *sc)
1152 {
1153 	GSList *l;
1154 	GnmParsePos pp;
1155 
1156 	parse_pos_init_sheet (&pp, sc->sheet);
1157 
1158 	gsf_xml_out_start_element (state->output, GNM "Scenario");
1159 
1160 	gsf_xml_out_add_cstr (state->output, "Name", sc->name);
1161 	if (sc->comment)
1162 		gsf_xml_out_add_cstr (state->output, "Comment", sc->comment);
1163 
1164 	for (l = sc->items; l; l = l->next) {
1165 		GnmScenarioItem const *sci = l->data;
1166 		GnmValue const *val = sci->value;
1167 		GString *str;
1168 		GnmConventionsOut out;
1169 
1170 		if (!gnm_scenario_item_valid (sci, NULL))
1171 			continue;
1172 
1173 		str = g_string_new (NULL);
1174 		gsf_xml_out_start_element (state->output, GNM "Item");
1175 
1176 		out.accum = str;
1177 		out.pp    = &pp;
1178 		out.convs = state->convs;
1179 
1180 		gnm_expr_top_as_gstring (dependent_managed_get_expr (&sci->dep), &out);
1181 		gsf_xml_out_add_cstr (state->output, "Range", str->str);
1182 
1183 		if (val) {
1184 			gsf_xml_out_add_int (state->output,
1185 					     "ValueType",
1186 					     val->v_any.type);
1187 			if (VALUE_FMT (val) != NULL) {
1188 				const char *fmt = go_format_as_XL (VALUE_FMT (val));
1189 				gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
1190 			}
1191 			g_string_truncate (str, 0);
1192 			value_get_as_gstring (val, str, state->convs);
1193 			gsf_xml_out_add_cstr (state->output, NULL, str->str);
1194 		}
1195 
1196 		gsf_xml_out_end_element (state->output); /* </gnm:Item> */
1197 		g_string_free (str, TRUE);
1198 	}
1199 
1200 	gsf_xml_out_end_element (state->output); /* </gnm:Scenario> */
1201 }
1202 
1203 
1204 static void
xml_write_scenarios(GnmOutputXML * state)1205 xml_write_scenarios (GnmOutputXML *state)
1206 {
1207 	GList *ptr;
1208 
1209 	if (state->sheet->scenarios == NULL)
1210 		return;
1211 
1212 	gsf_xml_out_start_element (state->output, GNM "Scenarios");
1213 
1214 	for (ptr = state->sheet->scenarios ; ptr != NULL ; ptr = ptr->next) {
1215 		GnmScenario const *sc = ptr->data;
1216 		xml_write_scenario (state, sc);
1217 	}
1218 
1219 	gsf_xml_out_end_element (state->output); /* </gnm:Scenarios> */
1220 }
1221 
1222 static int
so_by_pos(SheetObject * a,SheetObject * b)1223 so_by_pos (SheetObject *a, SheetObject *b)
1224 {
1225 	GnmRange const *ra = &a->anchor.cell_bound;
1226 	GnmRange const *rb = &b->anchor.cell_bound;
1227 	int i;
1228 	i = ra->start.col - rb->start.col;
1229 	if (!i) i = ra->start.row - rb->start.row;
1230 	if (!i) i = ra->end.col - rb->end.col;
1231 	if (!i) i = ra->end.row - rb->end.row;
1232 	return i;
1233 }
1234 
1235 static void
xml_write_objects(GnmOutputXML * state,GSList * objects)1236 xml_write_objects (GnmOutputXML *state, GSList *objects)
1237 {
1238 	gboolean needs_container = TRUE;
1239 	char buffer[4*(DBL_DIG+10)];
1240 	char const *type_name;
1241 	char *tmp;
1242 	GSList *ptr;
1243 	GSList *with_zorder = NULL;
1244 	GSList *without_zorder = NULL;
1245 
1246 	/*
1247 	 * Most objects are selectable and the order therefore matters.
1248 	 * We write those in reverse order because sheet_object_set_sheet
1249 	 * will reverse them on input.
1250 	 *
1251 	 * Cell comments are separated out and sorted.  This helps
1252 	 * consistency.
1253 	 *
1254 	 * Yet other objects have no export method and we drop those on
1255 	 * the floor.
1256 	 */
1257 	for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1258 		SheetObject *so = ptr->data;
1259 		SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1260 		if (klass == NULL || klass->write_xml_sax == NULL)
1261 			continue;
1262 
1263 		if (GNM_IS_CELL_COMMENT (so))
1264 			without_zorder = g_slist_prepend (without_zorder, so);
1265 		else
1266 			with_zorder = g_slist_prepend (with_zorder, so);
1267 	}
1268 	without_zorder = g_slist_sort (without_zorder, (GCompareFunc)so_by_pos);
1269 	objects = g_slist_concat (without_zorder, with_zorder);
1270 
1271 	for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1272 		SheetObject *so = ptr->data;
1273 		SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1274 		GnmRange cell_bound = so->anchor.cell_bound;
1275 
1276 		switch (so->anchor.mode) {
1277 		case GNM_SO_ANCHOR_TWO_CELLS:
1278 			break;
1279 		case GNM_SO_ANCHOR_ONE_CELL:
1280 			cell_bound.end = cell_bound.start;
1281 			break;
1282 		case GNM_SO_ANCHOR_ABSOLUTE:
1283 			range_init (&cell_bound, 0, 0, 0, 0);
1284 			break;
1285 		default:
1286 			g_assert_not_reached ();
1287 		}
1288 
1289 		if (needs_container) {
1290 			needs_container = FALSE;
1291 			gsf_xml_out_start_element (state->output, GNM "Objects");
1292 		}
1293 
1294 		/* A hook so that things can sometimes change names */
1295 		type_name = klass->xml_export_name;
1296 		if (type_name == NULL)
1297 			type_name = G_OBJECT_TYPE_NAME (so);
1298 
1299 		tmp = g_strconcat (GNM, type_name, NULL);
1300 		gsf_xml_out_start_element (state->output, tmp);
1301 		if (so->name)
1302 			gsf_xml_out_add_cstr (state->output, "Name", so->name);
1303 		if (so->anchor.mode != GNM_SO_ANCHOR_ABSOLUTE)
1304 			gsf_xml_out_add_cstr (state->output, "ObjectBound", range_as_string (&cell_bound));
1305 		if (so->anchor.mode != GNM_SO_ANCHOR_TWO_CELLS)
1306 			gsf_xml_out_add_enum (state->output,
1307 					      "AnchorMode",
1308 					      GNM_SHEET_OBJECT_ANCHOR_MODE_TYPE,
1309 					      so->anchor.mode);
1310 		snprintf (buffer, sizeof (buffer), "%.3g %.3g %.3g %.3g",
1311 			  so->anchor.offset [0], so->anchor.offset [1],
1312 			  so->anchor.offset [2], so->anchor.offset [3]);
1313 		gsf_xml_out_add_cstr (state->output, "ObjectOffset", buffer);
1314 
1315 		gsf_xml_out_add_int (state->output, "Direction",
1316 			so->anchor.base.direction);
1317 		gsf_xml_out_add_int
1318 		  (state->output, "Print",
1319 		   sheet_object_get_print_flag (so) ? 1 : 0);
1320 
1321 		(*klass->write_xml_sax) (so, state->output, state->convs);
1322 
1323 		gsf_xml_out_end_element (state->output); /* </gnm:{typename}> */
1324 		g_free (tmp);
1325 	}
1326 	g_slist_free (objects);
1327 
1328 	if (!needs_container)
1329 		gsf_xml_out_end_element (state->output); /* </gnm:Objects> */
1330 }
1331 
1332 static void
xml_write_sheet(GnmOutputXML * state,Sheet const * sheet)1333 xml_write_sheet (GnmOutputXML *state, Sheet const *sheet)
1334 {
1335 	GnmColor *c;
1336 
1337 	state->sheet = sheet;
1338 	gsf_xml_out_start_element (state->output, GNM "Sheet");
1339 
1340 	gsf_xml_out_add_bool (state->output,
1341 		"DisplayFormulas",	sheet->display_formulas);
1342 	gsf_xml_out_add_bool (state->output,
1343 		"HideZero",		sheet->hide_zero);
1344 	gsf_xml_out_add_bool (state->output,
1345 		"HideGrid",		sheet->hide_grid);
1346 	gsf_xml_out_add_bool (state->output,
1347 		"HideColHeader",	sheet->hide_col_header);
1348 	gsf_xml_out_add_bool (state->output,
1349 		"HideRowHeader",	sheet->hide_row_header);
1350 	gsf_xml_out_add_bool (state->output,
1351 		"DisplayOutlines",	sheet->display_outlines);
1352 	gsf_xml_out_add_bool (state->output,
1353 		"OutlineSymbolsBelow",	sheet->outline_symbols_below);
1354 	gsf_xml_out_add_bool (state->output,
1355 		"OutlineSymbolsRight",	sheet->outline_symbols_right);
1356 	if (sheet->text_is_rtl)
1357 		gsf_xml_out_add_bool (state->output,
1358 			"RTL_Layout", sheet->text_is_rtl);
1359 	if (sheet->is_protected)
1360 		gsf_xml_out_add_bool (state->output,
1361 			"Protected", sheet->is_protected);
1362 
1363 	/* TODO : Make this an enum internally eventually */
1364 	if (sheet->convs->r1c1_addresses)
1365 		gsf_xml_out_add_cstr_unchecked (state->output,
1366 			"ExprConvention", "gnumeric:R1C1");
1367 
1368 	gsf_xml_out_add_enum (state->output,
1369 		"Visibility", GNM_SHEET_VISIBILITY_TYPE, sheet->visibility);
1370 
1371 	if (sheet->tab_color != NULL)
1372 		gnm_xml_out_add_color (state->output, "TabColor", sheet->tab_color);
1373 	if (sheet->tab_text_color != NULL)
1374 		gnm_xml_out_add_color (state->output, "TabTextColor", sheet->tab_text_color);
1375 	if (NULL != (c = sheet_style_get_auto_pattern_color (sheet))) {
1376 		gnm_xml_out_add_color (state->output, "GridColor", c);
1377 		style_color_unref (c);
1378 	}
1379 
1380 	gsf_xml_out_simple_element (state->output,
1381 		GNM "Name", sheet->name_unquoted);
1382 	gsf_xml_out_simple_int_element (state->output,
1383 		GNM "MaxCol", sheet->cols.max_used);
1384 	gsf_xml_out_simple_int_element (state->output,
1385 		GNM "MaxRow", sheet->rows.max_used);
1386 	gsf_xml_out_simple_float_element (state->output,
1387 		GNM "Zoom", sheet->last_zoom_factor_used, 4);
1388 
1389 	xml_write_named_expressions (state, sheet->names);
1390 	xml_write_print_info (state, sheet->print_info);
1391 	xml_write_styles (state);
1392 	xml_write_cols_rows (state, NULL);
1393 	xml_write_selection_info (state);
1394 	xml_write_objects (state, sheet->sheet_objects);
1395 	xml_write_cells (state);
1396 
1397 	xml_write_merged_regions (state);
1398 	xml_write_sheet_layout (state);
1399 	xml_write_sheet_filters (state);
1400 	xml_write_solver (state);
1401 	xml_write_scenarios (state);
1402 
1403 	gnm_xml_out_end_element_check (state->output, GNM "Sheet");
1404 	state->sheet = NULL;
1405 }
1406 
1407 static void
xml_write_sheets(GnmOutputXML * state)1408 xml_write_sheets (GnmOutputXML *state)
1409 {
1410 	int i, n = workbook_sheet_count (state->wb);
1411 	gsf_xml_out_start_element (state->output, GNM "Sheets");
1412 	for (i = 0 ; i < n ; i++)
1413 		xml_write_sheet (state, workbook_sheet_by_index (state->wb, i));
1414 	gsf_xml_out_end_element (state->output); /* </gnm:Sheets> */
1415 }
1416 
1417 static void
xml_write_uidata(GnmOutputXML * state)1418 xml_write_uidata (GnmOutputXML *state)
1419 {
1420 	gsf_xml_out_start_element (state->output, GNM "UIData");
1421 	gsf_xml_out_add_int (state->output, "SelectedTab",
1422 		wb_view_cur_sheet (state->wb_view)->index_in_wb);
1423 	gsf_xml_out_end_element (state->output); /* </gnm:UIData> */
1424 }
1425 
1426 static void
xml_write_date_conventions_as_attr(GnmOutputXML * state,GODateConventions const * conv)1427 xml_write_date_conventions_as_attr (GnmOutputXML *state,
1428 				    GODateConventions const *conv)
1429 {
1430 	if (conv->use_1904)
1431 		gsf_xml_out_add_cstr_unchecked (state->output,
1432 			GNM "DateConvention", "Apple:1904");
1433 }
1434 
1435 static void
xml_write_number_system(GnmOutputXML * state)1436 xml_write_number_system (GnmOutputXML *state)
1437 {
1438 	/*
1439 	 * These numbers define how to interpret decimal values in the
1440 	 * file.  They are not yet used, but should be used when the
1441 	 * number system of the loading Gnumeric is different from the
1442 	 * number system of the saving Gnumeric.
1443 	 */
1444 	gsf_xml_out_add_int (state->output, "FloatRadix", FLT_RADIX);
1445 	gsf_xml_out_add_int (state->output, "FloatDigits", GNM_MANT_DIG);
1446 }
1447 
1448 
1449 static void
xml_write_calculation(GnmOutputXML * state)1450 xml_write_calculation (GnmOutputXML *state)
1451 {
1452 	gsf_xml_out_start_element (state->output, GNM "Calculation");
1453 	gsf_xml_out_add_bool (state->output,
1454 		"ManualRecalc",		!state->wb->recalc_auto);
1455 	gsf_xml_out_add_bool (state->output,
1456 		"EnableIteration",	state->wb->iteration.enabled);
1457 	gsf_xml_out_add_int (state->output,
1458 		"MaxIterations",	state->wb->iteration.max_number);
1459 	go_xml_out_add_double (state->output,
1460 		"IterationTolerance",	state->wb->iteration.tolerance);
1461 	xml_write_date_conventions_as_attr (state,
1462 					    workbook_date_conv (state->wb));
1463 	xml_write_number_system (state);
1464 	gsf_xml_out_end_element (state->output); /* </gnm:Calculation> */
1465 }
1466 
1467 GnmConventions *
gnm_xml_io_conventions(void)1468 gnm_xml_io_conventions (void)
1469 {
1470 	GnmConventions *res = gnm_conventions_new ();
1471 	gnm_float l10;
1472 
1473 	res->decimal_sep_dot	= TRUE;
1474 	res->input.range_ref	= rangeref_parse;
1475 	res->output.range_ref	= gnm_1_0_rangeref_as_string;
1476 	res->range_sep_colon	= TRUE;
1477 	res->arg_sep		= ',';
1478 	res->array_col_sep	= ',';
1479 	res->array_row_sep	= ';';
1480 	res->output.translated	= FALSE;
1481 
1482 	l10 = gnm_log10 (FLT_RADIX);
1483 	res->output.decimal_digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
1484 		(l10 == (int)l10 ? 0 : 1);
1485 
1486 	return res;
1487 }
1488 
1489 static void
gnm_xml_file_save_full(G_GNUC_UNUSED GOFileSaver const * fs,G_GNUC_UNUSED GOIOContext * io_context,GoView const * view,GsfOutput * output,gboolean compress)1490 gnm_xml_file_save_full (G_GNUC_UNUSED GOFileSaver const *fs,
1491 			G_GNUC_UNUSED GOIOContext *io_context,
1492 			GoView const *view, GsfOutput *output,
1493 			gboolean compress)
1494 {
1495 	GnmOutputXML state;
1496 	GsfOutput   *gzout = NULL;
1497 	GnmLocale   *locale;
1498 	WorkbookView *wb_view = GNM_WORKBOOK_VIEW (view);
1499 
1500 	if (compress) {
1501 		gzout  = gsf_output_gzip_new (output, NULL);
1502 		output = gzout;
1503 	}
1504 
1505 	state.wb_view	= wb_view;
1506 	state.wb	= wb_view_get_workbook (wb_view);
1507 	state.sheet	= NULL;
1508 	state.output	= gsf_xml_out_new (output);
1509 	state.convs	= gnm_xml_io_conventions ();
1510 	state.expr_map  = g_hash_table_new (g_direct_hash, g_direct_equal);
1511 	state.cell_str  = g_string_new (NULL);
1512 	state.write_value_result = FALSE;
1513 	go_doc_init_write (GO_DOC (state.wb), state.output);
1514 
1515 	locale = gnm_push_C_locale ();
1516 
1517 	gsf_xml_out_start_element (state.output, GNM "Workbook");
1518 
1519 	/*
1520 	 * As long as we want older versions of Gnumeric to be able to read
1521 	 * the files we produce, we should not increase the version number
1522 	 * in the file we write.  Until 1.12.21, v10 was the highest listed
1523 	 * xml-sax-read.c's content_ns.
1524 	 */
1525 	gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:gnm",
1526 		"http://www.gnumeric.org/v10.dtd");
1527 #if 0 /* seems to break meta data */
1528 	/* default namespace added for 1.8 */
1529 	gsf_xml_out_add_cstr_unchecked (state.output, "xmlns",
1530 		"http://www.gnumeric.org/v10.dtd");
1531 #endif
1532 	gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:xsi",
1533 		"http://www.w3.org/2001/XMLSchema-instance");
1534 	gsf_xml_out_add_cstr_unchecked (state.output, "xsi:schemaLocation",
1535 		"http://www.gnumeric.org/v9.xsd");
1536 
1537 	xml_write_version	    (&state);
1538 	xml_write_attributes	    (&state);
1539 	xml_write_meta_data	    (&state);
1540 	xml_write_conventions	    (&state);	/* DEPRECATED, moved to Calculation */
1541 	xml_write_calculation	    (&state);
1542 	xml_write_sheet_names	    (&state);
1543 	xml_write_named_expressions (&state, state.wb->names);
1544 	xml_write_geometry	    (&state);
1545 	xml_write_sheets	    (&state);
1546 	xml_write_uidata	    (&state);
1547 	go_doc_write (GO_DOC (state.wb), state.output);
1548 
1549 	gsf_xml_out_end_element (state.output); /* </Workbook> */
1550 
1551 	gnm_pop_C_locale (locale);
1552 
1553 	g_hash_table_destroy (state.expr_map);
1554 	g_string_free (state.cell_str, TRUE);
1555 	gnm_conventions_unref (state.convs);
1556 	g_object_unref (state.output);
1557 
1558 	if (gzout) {
1559 		gsf_output_close (gzout);
1560 		g_object_unref (gzout);
1561 	}
1562 }
1563 
1564 static void
gnm_xml_file_save(GOFileSaver const * fs,GOIOContext * io_context,GoView const * view,GsfOutput * output)1565 gnm_xml_file_save (GOFileSaver const *fs, GOIOContext *io_context,
1566 		   GoView const *view, GsfOutput *output)
1567 {
1568 	gboolean compress;
1569 	char const  *extension = NULL;
1570 
1571 	/* If the suffix is .xml disable compression */
1572 	if (NULL != gsf_output_name (output))
1573 		extension = gsf_extension_pointer (gsf_output_name (output));
1574 	if (NULL != extension && g_ascii_strcasecmp (extension, "xml") == 0)
1575 		compress = FALSE;
1576 	else
1577 		compress = (gnm_conf_get_core_xml_compression_level () > 0);
1578 
1579 	gnm_xml_file_save_full (fs, io_context, view, output, compress);
1580 }
1581 
1582 static void
gnm_xml_file_save_xml(GOFileSaver const * fs,GOIOContext * io_context,GoView const * view,GsfOutput * output)1583 gnm_xml_file_save_xml (GOFileSaver const *fs, GOIOContext *io_context,
1584 		   GoView const *view, GsfOutput *output)
1585 {
1586 	gnm_xml_file_save_full (fs, io_context, view, output, FALSE);
1587 }
1588 
1589 /**************************************************************************/
1590 
1591 typedef struct {
1592 	GnmOutputXML  state;
1593 	GnmCellRegion const *cr;
1594 	GnmParsePos   pp;
1595 } XMLCellCopyState;
1596 
1597 static void
cb_xml_write_cell_region_cells(GnmCellCopy * cc,G_GNUC_UNUSED gconstpointer ignore,XMLCellCopyState * state)1598 cb_xml_write_cell_region_cells (GnmCellCopy *cc,
1599 				G_GNUC_UNUSED gconstpointer ignore,
1600 				XMLCellCopyState *state)
1601 {
1602 	state->pp.eval.col = state->cr->base.col + cc->offset.col;
1603 	state->pp.eval.row = state->cr->base.row + cc->offset.row;
1604 	xml_write_cell_and_position (&state->state,
1605 		cc->texpr, cc->val, &state->pp);
1606 }
1607 
1608 static int
by_row_col(GnmCellCopy * cc_a,gpointer val_a,GnmCellCopy * cc_b,gpointer val_b,gpointer user)1609 by_row_col (GnmCellCopy *cc_a, gpointer val_a,
1610 	    GnmCellCopy *cc_b, gpointer val_b,
1611 	    gpointer user)
1612 {
1613 	int res = cc_a->offset.row - cc_b->offset.row;
1614 	if (!res)
1615 		res = cc_a->offset.col - cc_b->offset.col;
1616 	return res;
1617 }
1618 
1619 /**
1620  * gnm_cellregion_to_xml:
1621  * @cr: the content to store.
1622  *
1623  * Returns: (transfer full): %NULL on error
1624  **/
1625 GsfOutputMemory *
gnm_cellregion_to_xml(GnmCellRegion const * cr)1626 gnm_cellregion_to_xml (GnmCellRegion const *cr)
1627 {
1628 	XMLCellCopyState state;
1629 	GnmStyleList *s_ptr;
1630 	GSList       *ptr;
1631 	GsfOutput    *buf = gsf_output_memory_new ();
1632 	GnmLocale    *locale;
1633 	GODoc	     *doc = NULL;
1634 
1635 	g_return_val_if_fail (cr != NULL, NULL);
1636 	g_return_val_if_fail (IS_SHEET (cr->origin_sheet), NULL);
1637 
1638 	state.state.wb_view = NULL;
1639 	state.state.wb = NULL;
1640 	state.state.sheet = cr->origin_sheet;
1641 	state.state.output = gsf_xml_out_new (buf);
1642 	state.state.convs = gnm_xml_io_conventions ();
1643 	state.state.expr_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1644 	state.state.cell_str = g_string_new (NULL);
1645 	state.state.write_value_result = TRUE;
1646 
1647 	locale = gnm_push_C_locale ();
1648 	if (cr->origin_sheet) {
1649 		/* hoping this always occur */
1650 		doc = GO_DOC (cr->origin_sheet->workbook);
1651 		go_doc_init_write (doc, state.state.output);
1652 	}
1653 
1654 	gsf_xml_out_start_element (state.state.output, GNM "ClipboardRange");
1655 
1656 	/* backwards compat, must be first */
1657 	gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns:gnm",
1658 		"http://www.gnumeric.org/v10.dtd");
1659 	/* default namespace added for 1.8 */
1660 	gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns",
1661 		"http://www.gnumeric.org/v10.dtd");
1662 
1663 	gsf_xml_out_add_int (state.state.output, "Cols", cr->cols);
1664 	gsf_xml_out_add_int (state.state.output, "Rows", cr->rows);
1665 	gsf_xml_out_add_int (state.state.output, "BaseCol", cr->base.col);
1666 	gsf_xml_out_add_int (state.state.output, "BaseRow", cr->base.row);
1667 	if (cr->origin_sheet)
1668 		xml_write_date_conventions_as_attr
1669 			(&state.state,
1670 			 sheet_date_conv (cr->origin_sheet));
1671 	xml_write_number_system (&state.state);
1672 	if (cr->not_as_contents)
1673 		gsf_xml_out_add_bool (state.state.output, "NotAsContent", TRUE);
1674 
1675 	xml_write_cols_rows (&state.state, cr);
1676 
1677 	if (cr->styles != NULL) {
1678 		gsf_xml_out_start_element (state.state.output, GNM "Styles");
1679 		for (s_ptr = cr->styles ; s_ptr != NULL ; s_ptr = s_ptr->next)
1680 			xml_write_style_region (&state.state, s_ptr->data);
1681 		gsf_xml_out_end_element (state.state.output); /* </Styles> */
1682 	}
1683 
1684 	if (cr->merged != NULL) {
1685 		gsf_xml_out_start_element (state.state.output, GNM "MergedRegions");
1686 		for (ptr = cr->merged ; ptr != NULL ; ptr = ptr->next) {
1687 			gsf_xml_out_start_element (state.state.output, GNM "Merge");
1688 			gsf_xml_out_add_cstr_unchecked (state.state.output, NULL,
1689 				range_as_string (ptr->data));
1690 			gsf_xml_out_end_element (state.state.output); /* </Merge> */
1691 		}
1692 		gsf_xml_out_end_element (state.state.output); /* </gnm:MergedRegions> */
1693 	}
1694 
1695 	/* NOTE SNEAKY : ensure that sheet names have explicit workbooks */
1696 	state.pp.wb    = NULL;
1697 	state.pp.sheet = cr->origin_sheet;
1698 	state.cr = cr;
1699 	if (cr->cell_content != NULL) {
1700 		gsf_xml_out_start_element (state.state.output, GNM "Cells");
1701 		gnm_hash_table_foreach_ordered
1702 			(cr->cell_content,
1703 			 (GHFunc) cb_xml_write_cell_region_cells,
1704 			 (GnmHashTableOrder)by_row_col,
1705 			 &state);
1706 		gsf_xml_out_end_element (state.state.output); /* </Cells> */
1707 	}
1708 
1709 	xml_write_objects (&state.state, cr->objects);
1710 
1711 	if (NULL != doc)
1712 		go_doc_write (doc, state.state.output);
1713 	gsf_xml_out_end_element (state.state.output); /* </ClipboardRange> */
1714 
1715 	gnm_pop_C_locale (locale);
1716 
1717 	g_hash_table_destroy (state.state.expr_map);
1718 	g_string_free (state.state.cell_str, TRUE);
1719 	gnm_conventions_unref (state.state.convs);
1720 	g_object_unref (state.state.output);
1721 
1722 	gsf_output_close (buf);
1723 
1724 	return GSF_OUTPUT_MEMORY (buf);
1725 }
1726 
1727 #define XML_SAX_ID "Gnumeric_XmlIO:sax"
1728 #define XML_SAX_ID_0 "Gnumeric_XmlIO:sax:0"
1729 
1730 void
gnm_xml_sax_write_init(void)1731 gnm_xml_sax_write_init (void)
1732 {
1733 	GOFileSaver *saver = go_file_saver_new
1734 		(XML_SAX_ID,
1735 		 "gnumeric",
1736 		 _("Gnumeric XML (*.gnumeric)"),
1737 		 GO_FILE_FL_AUTO, gnm_xml_file_save);
1738 	g_object_set (G_OBJECT (saver),
1739 		      "mime-type", "application/x-gnumeric",
1740 		      NULL);
1741 
1742 	go_file_saver_register_as_default (saver, 50);
1743 	g_object_unref (saver);
1744 
1745 	saver = go_file_saver_new
1746 		(XML_SAX_ID_0,
1747 		 "xml",
1748 		 _("Gnumeric XML uncompressed (*.xml)"),
1749 		 GO_FILE_FL_AUTO, gnm_xml_file_save_xml);
1750 	g_object_set (G_OBJECT (saver),
1751 		      "mime-type", "application/xml",
1752 		      NULL);
1753 
1754 	go_file_saver_register (saver);
1755 	g_object_unref (saver);
1756 }
1757 
1758 void
gnm_xml_sax_write_shutdown(void)1759 gnm_xml_sax_write_shutdown (void)
1760 {
1761 	go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID));
1762 	go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID_0));
1763 }
1764