xml-io.c 44.7 KB
Newer Older
1 2 3
/*
 * xml-io.c: save/read gnumeric Sheets using an XML encoding.
 *
4 5 6
 * Authors:
 *   Daniel Veillard <Daniel.Veillard@w3.org>
 *   Miguel de Icaza <miguel@gnu.org>
7 8 9 10 11 12 13
 *
 * $Id$
 */

#include <config.h>
#include <stdio.h>
#include <gnome.h>
14
#include <locale.h>
15
#include "gnumeric.h"
16 17
#include "gnome-xml/tree.h"
#include "gnome-xml/parser.h"
18
#include "color.h"
19
#include "sheet-object.h"
20
#include "sheet-object-graphic.h"
21
#include "xml-io.h"
22
#include "file.h"
23 24 25 26

/*
 * A parsing context.
 */
27 28 29 30 31 32 33 34 35 36 37 38 39
typedef struct {
	xmlDocPtr doc;		/* Xml document */
	xmlNsPtr ns;		/* Main name space */
	xmlNodePtr parent;	/* used only for g_hash_table_foreach callbacks */
	Sheet *sheet;		/* the associated sheet */
	Workbook *wb;		/* the associated sheet */
	GHashTable *style_table;/* to generate the styles and then the links to it */
	int style_count;        /* A style number */
	xmlNodePtr style_node;  /* The node where we insert the styles */
} parse_xml_context_t;

static Sheet      *xml_sheet_read     (parse_xml_context_t *ctxt, xmlNodePtr tree);
static xmlNodePtr  xml_sheet_write    (parse_xml_context_t *ctxt, Sheet *sheet);
40
static gboolean    xml_workbook_read  (Workbook *wb, parse_xml_context_t *ctxt, xmlNodePtr tree);
41
static xmlNodePtr  xml_workbook_write (parse_xml_context_t *ctxt, Workbook *wb);
42

43 44 45
/*
 * Internal stuff: xml helper functions.
 */
46 47 48 49 50

/*
 * Get a value for a node either carried as an attibute or as
 * the content of a child.
 */
51
static char *
52
xml_value_get (xmlNodePtr node, const char *name)
53
{
54
	char *ret;
55 56
	xmlNodePtr child;

57
	ret = (char *) xmlGetProp (node, name);
58
	if (ret != NULL)
Morten Welinder's avatar
Morten Welinder committed
59
		return ret;
60 61 62
	child = node->childs;

	while (child != NULL) {
63 64 65 66 67 68 69 70
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
			ret = xmlNodeGetContent(child);
			if (ret != NULL)
			    return (ret);
		}
71 72 73 74
		child = child->next;
	}

	return NULL;
75 76
}

77
#if 0
78 79 80 81
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
82
static String *
83
xml_get_value_string (xmlNodePtr node, const char *name)
84
{
85
	char *val;
86 87
	String *ret;

88
	val = xml_value_get(node, name);
89 90 91 92
	if (val == NULL) return(NULL);
        ret = string_get(val);
	free(val);
	return(ret);
93
}
94
#endif
95

96 97 98 99
/*
 * Get an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
100
static int
101
xml_get_value_int (xmlNodePtr node, const char *name, int *val)
102
{
103
	char *ret;
104
	int i;
105
	int res;
106

107
	ret = xml_value_get (node, name);
108 109 110 111 112 113
	if (ret == NULL) return(0);
	res = sscanf (ret, "%d", &i);
	free(ret);
	
	if (res == 1) {
	        *val = i;
114 115 116
		return 1;
	}
	return 0;
117 118
}

119
#if 0
120 121 122 123
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
124
static int
125
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
126
{
127 128
	int res;
	char *ret;
129 130
	float f;

131
	ret = xml_value_get (node, name);
132 133 134 135 136 137
	if (ret == NULL) return(0);
	res = sscanf (ret, "%f", &f);
	free(ret);
	
	if (res == 1) {
	        *val = f;
138 139 140
		return 1;
	}
	return 0;
141
}
142
#endif
143 144 145 146 147

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
148
static int
149
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
150
{
151 152
	int res;
	char *ret;
153

154
	ret = xml_value_get (node, name);
155
	if (ret == NULL) return(0);
156
	res = sscanf (ret, "%lf", val);
157
	free(ret);
158

159
	return (res == 1);
160 161
}

162
#if 0
163 164 165
/*
 * Get a set of coodinates for a node, carried as the content of a child.
 */
166
static int
167
xml_get_coordinate (xmlNodePtr node, const char *name, double *x, double *y)
168
{
169 170
	int res;
	char *ret;
171
	float X, Y;
172

173
	ret = xml_value_get (node, name);
174
	if (ret == NULL) return(0);
175
	res = sscanf (ret, "(%lf %lf)", x, y)
176
	free(ret);
177 178

	return (res == 2)
179
}
180
#endif
181 182 183 184 185

/*
 * Get a pair of coodinates for a node, carried as the content of a child.
 */

186
static int
187
xml_get_coordinates (xmlNodePtr node, const char *name,
188 189
		   double *x1, double *y1, double *x2, double *y2)
{
190 191
	int res;
	char *ret;
192

193
	ret = xml_value_get (node, name);
194
	if (ret == NULL) return(0);
195
	res = sscanf (ret, "(%lf %lf)(%lf %lf)", x1, y1, x2, y2);
196 197
	free(ret);
	
198
	if (res == 4) 
199
		return 1;
200

201
	return 0;
202 203
}

204
#if 0
205 206 207
/*
 * Get a GnomeCanvasPoints for a node, carried as the content of a child.
 */
208
static GnomeCanvasPoints *
209
xml_get_gnome_canvas_points (xmlNodePtr node, const char *name)
210
{
211
	char *val;
212 213 214 215
	GnomeCanvasPoints *ret = NULL;
	int res;
	const char *ptr;
	int index = 0, i;
216
	float coord[20];	/* TODO: must be dynamic !!!! */
217

218
	val = xml_value_get (node, name);
219
	if (val == NULL) return(NULL);
220 221 222 223 224
	ptr = val;
	do {
		while ((*ptr) && (*ptr != '('))
			ptr++;
		if (*ptr == 0)
225
			break;
226
		res = sscanf (ptr, "(%lf %lf)", &coord[index], &coord[index + 1]);
227 228 229 230 231 232 233 234 235 236 237 238 239
		if (res != 2)
			break;
		index += 2;
		ptr++;
	} while (res > 0);
	free(val);

	if (index >= 2)
		ret = gnome_canvas_points_new (index / 2);
	if (ret == NULL)
		return NULL;
	for (i = 0; i < index; i++)
		ret->coords[i] = coord[i];
240
	return ret;
241
}
242
#endif
243 244 245 246 247

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
248
static void
249
xml_set_gnome_canvas_points (xmlNodePtr node, const char *name,
250
			     GnomeCanvasPoints *val)
251 252 253 254 255 256 257 258 259
{
	xmlNodePtr child;
	char *str, *base;
	int i;

	if (val == NULL)
		return;
	if ((val->num_points < 0) || (val->num_points > 5000))
		return;
260
	base = str = g_malloc (val->num_points * 30 * sizeof (char) + 1);
261 262 263
	if (str == NULL)
		return;
	for (i = 0; i < val->num_points; i++){
264 265 266
		sprintf (str, "(%f %f)", val->coords[2 * i],
			 val->coords[2 * i + 1]);
		str += strlen (str);
267
	}
268
	*str = 0;
269 270 271 272 273 274 275 276 277 278

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, base);
			free (base);
			return;
		}
		child = child->next;
	}
279
	xmlNewChild (node, NULL, name, xmlEncodeEntities(node->doc, base));
280
	g_free (base);
281 282 283
}


284 285 286 287
/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
288 289
static void
xml_set_value (xmlNodePtr node, const char *name, const char *val)
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
{
	const char *ret;
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, val);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val);
308 309
}

310 311 312 313
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
314 315
static void
xml_set_value_string (xmlNodePtr node, const char *name, String *val)
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
{
	const char *ret;
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, val->str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val->str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val->str);
334 335
}

336 337 338 339
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
340
static void
341
xml_set_value_int (xmlNodePtr node, const char *name, int val)
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
{
	const char *ret;
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%d", val);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
362 363
}

364
#if 0
365 366 367 368
/*
 * Set a float value for a node either carried as an attibute or as
 * the content of a child.
 */
369
static void
370
xml_set_value_float (xmlNodePtr node, const char *name, float val)
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
{
	const char *ret;
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%f", val);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
391
}
392
#endif
393 394 395 396 397

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
398
static void
399
xml_set_value_double (xmlNodePtr node, const char *name, double val)
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
{
	const char *ret;
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%f", (float) val);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
420 421 422 423 424
}

/*
 * Search a child by name, if needed go down the tree to find it. 
 */
425 426
static xmlNodePtr
xml_search_child (xmlNodePtr node, const char *name)
427 428 429 430 431 432 433 434 435 436 437 438
{
	xmlNodePtr ret;
	xmlNodePtr child;

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name))
			return child;
		child = child->next;
	}
	child = node->childs;
	while (child != NULL){
439
		ret = xml_search_child (child, name);
440 441 442 443 444
		if (ret != NULL)
			return ret;
		child = child->next;
	}
	return NULL;
445 446 447 448 449 450 451 452 453 454
}

/*
 * Get a color value for a node either carried as an attibute or as
 * the content of a child.
 *
 * TODO PBM: at parse time one doesn't have yet a widget, so we have
 *           to retrieve the default colormap, but this may be a bad
 *           option ...
 */
455
static int
456
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
457
{
458
	char *ret;
459 460
	int red, green, blue;

461
	ret = xml_value_get (node, name);
462 463
	if (ret == NULL) return(0);
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
464
		*color = style_color_new (red, green, blue);
465
		free(ret);
466 467 468
		return 1;
	}
	return 0;
469 470 471 472 473 474
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
475 476
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
{
	const char *ret;
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
497
}
498 499 500 501 502

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
503
 **/
504

505 506
static int
style_is_default_fore (StyleColor *color)
507
{
508 509
	if (!color)
		return TRUE;
510

511 512 513 514
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
515 516
}

517 518
static int
style_is_default_back (StyleColor *color)
519
{
520 521
	if (!color)
		return TRUE;
522

523 524 525 526
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
527 528 529 530 531
}

/*
 * Create an XML subtree of doc equivalent to the given StyleBorder.
 */
532 533 534
static char *BorderTypes[8] =
{
	"none",
535 536 537 538 539 540 541
 	"thin",
 	"medium",
 	"dashed",
 	"dotted",
 	"thick",
 	"double",
	"hair"
542 543
};

544 545 546 547 548 549
static char *StyleSideNames[4] =
{
 	"Top",
 	"Bottom",
 	"Left",
 	"Right"
Arturo Espinosa's avatar
Arturo Espinosa committed
550
};
551

552
static xmlNodePtr
553
xml_write_style_border (parse_xml_context_t *ctxt, StyleBorder *border)
554 555 556
{
	xmlNodePtr cur;
	xmlNodePtr side;
Arturo Espinosa's avatar
Arturo Espinosa committed
557
	int lp;
558
       
Arturo Espinosa's avatar
Arturo Espinosa committed
559 560 561
	for (lp = 3; lp >= 0; lp--)
		if (border->type [lp] != BORDER_NONE)
			break;
562
	if (lp < 0)
Arturo Espinosa's avatar
Arturo Espinosa committed
563
		return NULL;
564

565
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
566
	
567 568 569 570
	for (lp = 0; lp < 4; lp++){
 		if (border->type[lp] != BORDER_NONE){
 			side = xmlNewChild (cur, ctxt->ns, StyleSideNames [lp],
 					    BorderTypes [border->type [lp]]);
571
 			xml_set_color_value (side, "Color", border->color [lp]);
572
 		}
573 574
	}
	return cur;
575 576 577 578 579
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
580
static StyleBorder *
581
xml_read_style_border (parse_xml_context_t *ctxt, xmlNodePtr tree)
582 583
{
	StyleBorder *ret;
Arturo Espinosa's avatar
Arturo Espinosa committed
584
 	StyleBorderType style [4] = { BORDER_NONE, BORDER_NONE, BORDER_NONE, BORDER_NONE };
585
	StyleColor *color [4] = { NULL, NULL, NULL, NULL };
586
	xmlNodePtr side;
Arturo Espinosa's avatar
Arturo Espinosa committed
587
	int lp;
588 589 590

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
591
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
592 593
			 tree->name);
	}
594

Arturo Espinosa's avatar
Arturo Espinosa committed
595
 	for (lp = 0; lp < 4; lp++)
596
 	{
597
 		if ((side = xml_search_child (tree, StyleSideNames [lp])) != NULL)
598 599
 		{
 			/* FIXME: need to read the proper type */
Arturo Espinosa's avatar
Arturo Espinosa committed
600
 			style [lp] = BORDER_THICK ;
601
 			xml_get_color_value (side, "Color", &color [lp]);
602
 		}
603
	}
604
	
Arturo Espinosa's avatar
Arturo Espinosa committed
605
	ret = style_border_new (style, color);
606

607
	return NULL;
608 609 610 611 612
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
613
static xmlNodePtr
614
xml_write_style (parse_xml_context_t *ctxt, Style *style, int style_idx)
615 616 617 618 619 620 621 622
{
	xmlNodePtr cur, child;

	if ((style->halign == 0) && (style->valign == 0) &&
	    (style->orientation == 0) && (style->format == NULL) &&
	    (style->font == NULL) && (style->border == NULL))
		return NULL;

623
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
624 625 626 627 628 629 630 631
	if (style_idx != -1)
		xml_set_value_int (cur, "No", style_idx);
	
	xml_set_value_int (cur, "HAlign", style->halign);
	xml_set_value_int (cur, "VAlign", style->valign);
	xml_set_value_int (cur, "Fit", style->fit_in_cell);
	xml_set_value_int (cur, "Orient", style->orientation);
	xml_set_value_int (cur, "Shade", style->pattern);
632 633

	if (!style_is_default_fore (style->fore_color))
634
		xml_set_color_value (cur, "Fore", style->fore_color);
635 636

	if (!style_is_default_back (style->back_color))
637
		xml_set_color_value (cur, "Back", style->back_color);
638 639

	if (style->format != NULL){
640
		xml_set_value (cur, "Format", style->format->format);
641 642 643
	}

	if (style->font != NULL){
644 645 646 647 648 649 650
		child = xmlNewChild (cur, ctxt->ns, "Font", 
				     xmlEncodeEntities(ctxt->doc,
						       style->font->font_name));
		xml_set_value_double (child, "Unit", style->font->size);
		xml_set_value_int (child, "Bold", style->font->is_bold);
		xml_set_value_int (child, "Italic", style->font->is_italic);

651 652 653
	}

	if (style->border != NULL){
654
		child = xml_write_style_border (ctxt, style->border);
655 656 657 658 659
		if (child)
			xmlAddChild (cur, child);
	}

	return cur;
660 661
}

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
static xmlNodePtr
xml_write_names (parse_xml_context_t *ctxt, Workbook *wb)
{
	GList *names, *m;
	xmlNodePtr cur;

	g_return_val_if_fail (wb != NULL, NULL);

	m = names = expr_name_list (wb, FALSE);

	if (!names)
		return NULL;

	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Names", NULL);

	while (names) {
		xmlNodePtr   tmp;
		ExprName    *expr_name = names->data;
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
		xmlNewChild (tmp, ctxt->ns, "name",
			     xmlEncodeEntities (ctxt->doc, expr_name->name->str));

		text = expr_name_value (expr_name);
		xmlNewChild (tmp, ctxt->ns, "value",
			     xmlEncodeEntities (ctxt->doc, text));
		g_free (text);

		xmlAddChild (cur, tmp);
		names = g_list_next (names);
	}
	g_list_free (m);
	return cur;
}

static void
xml_read_names (parse_xml_context_t *ctxt, xmlNodePtr tree, Workbook *wb)
{
	xmlNodePtr child;

	g_return_if_fail (wb != NULL);
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);

	child = tree->childs;
	while (child) {
711
		char *name  = NULL;
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

			bits = child->childs;
			while (bits) {
				
				if (!strcmp (bits->name, "name")) {
					name = xmlNodeGetContent (bits);
				} else {
					char     *txt;
					char     *error;
					g_return_if_fail (name != NULL);

					txt = xmlNodeGetContent (bits);
					g_return_if_fail (txt != NULL);
					g_return_if_fail (!strcmp (bits->name, "value"));

					if (!expr_name_create (wb, name, txt, &error))
						g_warning (error);

					g_free (txt);
				}
734
				bits = bits->next;
735 736 737 738 739 740
			}
		}
		child = child->next;
	}
}

741
static xmlNodePtr
742
xml_write_summary (parse_xml_context_t *ctxt, SummaryInfo *summary_info)
743 744 745 746
{
	GList *items, *m;
	xmlNodePtr cur;

747
	if (!summary_info)
748 749
		return NULL;

750
	m = items = summary_info_as_list (summary_info);
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780

	if (!items)
		return NULL;

	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Summary", NULL);

	while (items) {
		xmlNodePtr   tmp;
		SummaryItem *sit = items->data;
		if (sit) {
			char *text;

			tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Item", NULL);
			xmlNewChild (tmp, ctxt->ns, "name",
				     xmlEncodeEntities (ctxt->doc, sit->name));

			if (sit->type == SUMMARY_INT) {

				text = g_strdup_printf ("%d", sit->v.i);
				xmlNewChild (tmp, ctxt->ns, "val-int",
					     xmlEncodeEntities (ctxt->doc, text));

			} else {

				text = summary_item_as_text (sit);
				xmlNewChild (tmp, ctxt->ns, "val-string",
					     xmlEncodeEntities (ctxt->doc, text));

			}
			g_free (text);
781
			xmlAddChild (cur, tmp);
782 783 784 785 786 787 788 789
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
790
xml_read_summary (parse_xml_context_t *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
791
{
792
	xmlNodePtr child;
793

794
	g_return_if_fail (summary_info);
795 796 797 798 799
	g_return_if_fail (ctxt);
	g_return_if_fail (tree);

	child = tree->childs;
	while (child) {
800
		char *name = NULL;
801 802 803 804 805 806 807 808
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

			bits = child->childs;
			while (bits) {
				SummaryItem *sit = NULL;
				
				if (!strcmp (bits->name, "name")) {
809
					name = xmlNodeGetContent (bits);
810 811 812 813 814
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
815 816 817 818 819 820 821
					if (txt != NULL){
						if (!strcmp (bits->name, "val-string"))
							sit = summary_item_new_string (name, txt);
						else if (!strcmp (bits->name, "val-int"))
							sit = summary_item_new_int    (name, atoi (txt));
						
						if (sit)
822
							summary_info_add (summary_info, sit);
823 824
						g_free (txt);
					}
825
				}
826
				bits = bits->next;
827 828 829 830 831 832
			}
		}
		child = child->next;
	}
}

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
static const char *
font_component (const char *fontname, int idx)
{
	int i = 0;
	const char *p = fontname;
	
	for (; *p && i < idx; p++){
		if (*p == '-')
			i++;
	}
	if (*p == '-')
		p++;

	return p;
}

/**
 * style_font_new_from_x11:
 * @fontname: an X11-like font name.
 * @scale: scale desired
 *
 * Tries to guess the fontname, the weight and italization parameters
 * to invoke style_font_new
 *
 * Returns: A valid style font.
 */
static StyleFont *
style_font_new_from_x11 (const char *fontname, double units, double scale)
{
	StyleFont *sf;
	char *typeface = "Helvetica";
	int is_bold = 0;
	int is_italic = 0;
	const char *c;

	/*
	 * FIXME: we should do something about the typeface instead
	 * of hardcoding it to helvetica.
	 */
	
	c = font_component (fontname, 2);
	if (strncmp (c, "bold", 4) == 0)
		is_bold = 1;

	c = font_component (fontname, 3);
	if (strncmp (c, "o", 1) == 0)
		is_italic = 1;
	if (strncmp (c, "i", 1) == 0)
		is_italic = 1;

	sf = style_font_new (typeface, units, scale, is_bold, is_italic);
	if (sf == NULL)
		sf = style_font_new_from (gnumeric_default_font, scale);

	return sf;
}

890 891 892
/*
 * Create a Style equivalent to the XML subtree of doc.
 */
893
static Style *
894
xml_read_style (parse_xml_context_t *ctxt, xmlNodePtr tree, Style * ret)
895 896
{
	xmlNodePtr child;
Morten Welinder's avatar
Morten Welinder committed
897
	char *prop;
898 899 900 901 902
	int val;
	StyleColor *c;

	if (strcmp (tree->name, "Style")){
		fprintf (stderr,
903
			 "xml_read_style: invalid element type %s, 'Style' expected\n",
904 905 906 907 908 909 910 911
			 tree->name);
	}
	if (ret == NULL)
		ret = style_new_empty ();
	
	if (ret == NULL)
		return NULL;

912
	if (xml_get_value_int (tree, "HAlign", &val)){
913 914 915
		ret->halign = val;
		ret->valid_flags |= STYLE_ALIGN;
	}
916
	if (xml_get_value_int (tree, "Fit", &val)){
917 918 919
		ret->fit_in_cell = val;
		ret->valid_flags |= STYLE_ALIGN;
	}
920
	if (xml_get_value_int (tree, "VAlign", &val)){
921 922 923
		ret->valign = val;
		ret->valid_flags |= STYLE_ALIGN;
	}
924
	if (xml_get_value_int (tree, "Orient", &val)){
925 926 927
		ret->orientation = val;
		ret->valid_flags |= STYLE_ALIGN;
	}
928
	if (xml_get_value_int (tree, "Shade", &val)){
929 930 931
		ret->pattern = val;
		ret->valid_flags |= STYLE_PATTERN;
	}
932
	if (xml_get_color_value (tree, "Fore", &c)){
933 934 935
		ret->fore_color = c;
		ret->valid_flags |= STYLE_FORE_COLOR;
	}
936
	if (xml_get_color_value (tree, "Back", &c)){
937 938 939
		ret->back_color = c;
		ret->valid_flags |= STYLE_BACK_COLOR;
	}
940
	prop = xml_value_get (tree, "Format");
941 942
	if (prop != NULL){
		if (ret->format == NULL){
Morten Welinder's avatar
Morten Welinder committed
943
			ret->format = style_format_new ((const char *) prop);
944
			ret->valid_flags |= STYLE_FORMAT;
945
		}
Morten Welinder's avatar
Morten Welinder committed
946
		free (prop);
947
	}
948

949 950 951
	child = tree->childs;
	while (child != NULL){
		if (!strcmp (child->name, "Font")){
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
			char *font;
			double units = 14;
			int is_bold = 0;
			int is_italic = 0;
			int t;
				
			xml_get_value_double (child, "Unit", &units);

			if (xml_get_value_int (child, "Bold", &t))
				is_bold = t;
			if (xml_get_value_int (child, "Italic", &t))
				is_italic = t;
			
			font = xmlNodeGetContent(child);
			if (font != NULL) {
				StyleFont *sf;

				if (*font == '-'){
					sf = style_font_new_from_x11 (font, units, 1.0);
				} else
					sf = style_font_new (font, units, 1.0, is_bold, is_italic);

				ret->font = sf;
				free(font);
			}
			if (ret->font){
				ret->valid_flags |= STYLE_FONT;
979 980 981 982
			}
		} else if (!strcmp (child->name, "StyleBorder")){
			StyleBorder *sb;

983
			sb = xml_read_style_border (ctxt, child);
984
		} else {
985
			fprintf (stderr, "xml_read_style: unknown type '%s'\n",
986 987 988 989 990 991 992
				 child->name);
		}
		child = child->next;
	}

	/* Now add defaults to any style that was not loaded */
	return ret;
993 994
}

995
#if 0
996 997 998
/*
 * Create an XML subtree of doc equivalent to the given StyleRegion.
 */
999
static xmlNodePtr
1000
xml_write_style_region (parse_xml_context_t *ctxt, StyleRegion *region)
1001 1002 1003
{
	xmlNodePtr cur, child;

1004
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleRegion", NULL);
1005 1006 1007 1008
	xml_set_value_int (cur, "startCol", region->range.start_col);
	xml_set_value_int (cur, "endCol", region->range.end_col);
	xml_set_value_int (cur, "startRow", region->range.start_row);
	xml_set_value_int (cur, "endRow", region->range.end_row);
1009 1010

	if (region->style != NULL){
1011
		child = xml_write_style (ctxt, region->style);
1012 1013 1014 1015
		if (child)
			xmlAddChild (cur, child);
	}
	return cur;
1016
}
1017
#endif
1018 1019 1020 1021

/*
 * Create a StyleRegion equivalent to the XML subtree of doc.
 */
1022
static void
1023
xml_read_style_region (parse_xml_context_t *ctxt, xmlNodePtr tree)
1024 1025 1026 1027 1028 1029 1030
{
	xmlNodePtr child;
	Style *style = NULL;
	int start_col = 0, start_row = 0, end_col = 0, end_row = 0;

	if (strcmp (tree->name, "StyleRegion")){
		fprintf (stderr,
1031
			 "xml_read_style_region: invalid element type %s, 'StyleRegion' expected`\n",
1032 1033 1034
			 tree->name);
		return;
	}
1035 1036 1037 1038
	xml_get_value_int (tree, "startCol", &start_col);
	xml_get_value_int (tree, "startRow", &start_row);
	xml_get_value_int (tree, "endCol", &end_col);
	xml_get_value_int (tree, "endRow", &end_row);
1039 1040
	child = tree->childs;
	if (child != NULL)
1041
		style = xml_read_style (ctxt, child, NULL);
1042 1043 1044
	if (style != NULL)
		sheet_style_attach (ctxt->sheet, start_col, start_row, end_col,
				    end_row, style);
1045 1046 1047

}

1048 1049 1050
/*
 * Create an XML subtree of doc equivalent to the given ColRowInfo.
 */
1051
static xmlNodePtr
1052
xml_write_colrow_info (parse_xml_context_t *ctxt, ColRowInfo *info, int col)
1053 1054 1055 1056
{
	xmlNodePtr cur;

	if (col)
1057
		cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "ColInfo", NULL);
1058
	else
1059
		cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "RowInfo", NULL);
1060

1061 1062 1063 1064 1065
	xml_set_value_int (cur, "No", info->pos);
	xml_set_value_double (cur, "Unit", info->units);
	xml_set_value_double (cur, "MarginA", info->margin_a_pt);
	xml_set_value_double (cur, "MarginB", info->margin_b);
	xml_set_value_int (cur, "HardSize", info->hard_size);
1066 1067

	return cur;
1068 1069 1070 1071 1072
}

/*
 * Create a ColRowInfo equivalent to the XML subtree of doc.
 */
1073
static ColRowInfo *
1074
xml_read_colrow_info (parse_xml_context_t *ctxt, xmlNodePtr tree, ColRowInfo *ret)
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
{
	int col = 0;
	int val;

	if (!strcmp (tree->name, "ColInfo")){
		col = 1;
	} else if (!strcmp (tree->name, "RowInfo")){
		col = 0;
	} else {
		fprintf (stderr,
1085
			 "xml_read_colrow_info: invalid element type %s, 'ColInfo/RowInfo' expected`\n",
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
			 tree->name);
		return NULL;
	}
	if (ret == NULL){
		if (col)
			ret = sheet_col_new (ctxt->sheet);
		else
			ret = sheet_row_new (ctxt->sheet);
	}
	if (ret == NULL)
		return NULL;
1097

1098 1099 1100 1101 1102
	xml_get_value_int (tree, "No", &ret->pos);
	xml_get_value_double (tree, "Unit", &ret->units);
	xml_get_value_double (tree, "MarginA", &ret->margin_a_pt);
	xml_get_value_double (tree, "MarginB", &ret->margin_b_pt);
	if (xml_get_value_int (tree, "HardSize", &val))
1103
		ret->hard_size = val;
1104

1105
	return ret;
1106 1107
}

1108 1109 1110
/*
 * Create an XML subtree of doc equivalent to the given Object.
 */
1111
static xmlNodePtr
1112
xml_write_sheet_object (parse_xml_context_t *ctxt, SheetObject *object)
1113
{
1114
	SheetObjectGraphic *sog = SHEET_OBJECT_GRAPHIC (object);
1115 1116
	xmlNodePtr cur = NULL;

1117
	switch (sog->type){
1118
	case SHEET_OBJECT_RECTANGLE:{
1119
			SheetObjectFilled *sof = SHEET_OBJECT_FILLED (object);
1120

1121
			cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Rectangle", NULL);
1122
			if (sof->fill_color != NULL)
1123 1124
				xml_set_value_string (cur, "FillColor", sof->fill_color);
			xml_set_value_int (cur, "Pattern", sof->pattern);
1125 1126
			break;
		}
1127

1128
	case SHEET_OBJECT_ELLIPSE:{
1129
			SheetObjectFilled *sof = SHEET_OBJECT_FILLED (object);
1130

1131
			cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Ellipse", NULL);
1132
			if (sof->fill_color != NULL)
1133 1134
				xml_set_value_string (cur, "FillColor", sof->fill_color);
			xml_set_value_int (cur, "Pattern", sof->pattern);
1135 1136
			break;
		}
1137

1138
	case SHEET_OBJECT_ARROW:
1139
		cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Arrow", NULL);
1140
		break;
1141

1142
	case SHEET_OBJECT_LINE:
1143
		cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Line", NULL);
1144 1145 1146 1147 1148
		break;
	}
	if (cur == NULL)
		return NULL;
	
1149 1150 1151
	xml_set_gnome_canvas_points (cur, "Points", object->bbox_points);
	xml_set_value_int (cur, "Width", sog->width);
	xml_set_value_string (cur, "Color", sog->color);
1152

1153
	return cur;
1154 1155 1156 1157 1158
}

/*
 * Create a Object equivalent to the XML subtree of doc.
 */
1159
static SheetObject *
1160
xml_read_sheet_object (parse_xml_context_t *ctxt, xmlNodePtr tree)
1161 1162
{
	SheetObject *ret;
1163
	SheetObjectFilled *sof;
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
	char *color;
	char *fill_color;
	int type;
	double x1, y1, x2, y2;
	int width = 1;
	int pattern;

	if (!strcmp (tree->name, "Rectangle")){
		type = SHEET_OBJECT_RECTANGLE;
	} else if (!strcmp (tree->name, "Ellipse")){
		type = SHEET_OBJECT_ELLIPSE;
	} else if (!strcmp (tree->name, "Arrow")){
		type = SHEET_OBJECT_ARROW;
	} else if (!strcmp (tree->name, "Line")){
		type = SHEET_OBJECT_LINE;
	} else {
		fprintf (stderr,
1181
			 "xml_read_sheet_object: invalid element type %s, 'Rectangle/Ellipse ...' expected`\n",
1182 1183 1184 1185
			 tree->name);
		return NULL;
	}
	
1186 1187 1188
	color = (char *) xml_value_get (tree, "Color");
	xml_get_coordinates (tree, "Points", &x1, &y1, &x2, &y2);
	xml_get_value_int (tree, "Width", &width);
1189 1190
	if ((type == SHEET_OBJECT_RECTANGLE) ||
	    (type == SHEET_OBJECT_ELLIPSE)){
1191 1192
		fill_color = (char *) xml_value_get (tree, "FillColor");
		xml_get_value_int (tree, "Pattern", &pattern);
1193 1194 1195
		ret = sheet_object_create_filled (
			ctxt->sheet, type,
			x1, y1, x2, y2, fill_color, color, width);
1196
		if (ret != NULL){
1197 1198
			sof = SHEET_OBJECT_FILLED (ret);
			sof->pattern = pattern;
1199 1200
		}
	} else {
1201 1202 1203
		ret = sheet_object_create_line (
			ctxt->sheet, type,
			x1, y1, x2, y2, color, width);
1204
	}
1205
	sheet_object_realize (ret);
1206
	return ret;
1207 1208
}

1209 1210 1211
/*
 * Create an XML subtree of doc equivalent to the given Cell.
 */
1212
static xmlNodePtr
1213
xml_write_cell (parse_xml_context_t *ctxt, Cell *cell)
1214
{
1215
	xmlNodePtr cur;
Miguel de Icaza's avatar