openoffice-read.c 71.4 KB
Newer Older
1
2
3
4
5
/* vim: set sw=8: */

/*
 * openoffice-read.c : import open/star calc files
 *
6
 * Copyright (C) 2002-2005 Jody Goldberg (jody@gnome.org)
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen (Ray)'s avatar
J.H.M. Dassen (Ray) committed
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20
21
22
23
24
25
 * USA
 */

#include <gnumeric-config.h>
#include <gnumeric.h>

Jody Goldberg's avatar
Jody Goldberg committed
26
#include <gnm-plugin.h>
27
28
29
#include <workbook-view.h>
#include <workbook.h>
#include <sheet.h>
30
31
#include <sheet-merge.h>
#include <ranges.h>
32
33
#include <cell.h>
#include <value.h>
34
#include <expr.h>
35
36
#include <expr-impl.h>
#include <expr-name.h>
37
#include <parse-util.h>
38
39
40
#include <style-color.h>
#include <sheet-style.h>
#include <mstyle.h>
41
#include <style-border.h>
Stepan Kasal's avatar
Stepan Kasal committed
42
#include <gnm-format.h>
43
#include <command-context.h>
JodyGoldberg's avatar
JodyGoldberg committed
44
#include <goffice/app/io-context.h>
45
#include <goffice/utils/go-units.h>
46
#include <goffice/utils/datetime.h>
47
48
49
50
51

#include <gsf/gsf-libxml.h>
#include <gsf/gsf-input.h>
#include <gsf/gsf-infile.h>
#include <gsf/gsf-infile-zip.h>
52
#include <gsf/gsf-opendoc-utils.h>
53
#include <gsf/gsf-doc-meta-data.h>
54
#include <glib/gi18n.h>
55

56
#include <string.h>
57
#include <locale.h>
58

Jody Goldberg's avatar
Jody Goldberg committed
59
GNM_PLUGIN_MODULE_HEADER;
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* Filter Type */
#define mime_openofficeorg1	"application/vnd.sun.xml.calc"
#define mime_opendocument	"application/vnd.oasis.opendocument.spreadsheet"

typedef enum {
	OOO_VER_UNKNOW	= -1,
	OOO_VER_1	=  0,
	OOO_VER_OPENDOC	=  1
} OOVer;

#define OD_BORDER_THIN		1
#define OD_BORDER_MEDIUM	2.5
#define OD_BORDER_THICK		5

75
76
77
78
79
80
81
typedef enum {
	OO_STYLE_UNKNOWN,
	OO_STYLE_CELL,
	OO_STYLE_COL,
	OO_STYLE_ROW,
	OO_STYLE_SHEET,
	OO_STYLE_GRAPHICS,
82
83
	OO_STYLE_PARAGRAPH,
	OO_STYLE_TEXT
84
85
} OOStyleType;

86
87
88
typedef struct {
	IOContext 	*context;	/* The IOcontext managing things */
	WorkbookView	*wb_view;	/* View for the new workbook */
89
	OOVer		 ver;
90

Morten Welinder's avatar
Morten Welinder committed
91
	GnmParsePos 	pos;
92

93
	int 		 col_inc, row_inc;
94
95
	gboolean 	 simple_content;
	gboolean 	 error_content;
96
97
	GHashTable	*cell_styles;
	GHashTable	*col_row_styles;
98
	GHashTable	*table_styles;
99
	GHashTable	*formats;
100
101
102
103
104

	union {
		GnmStyle *cell;
		double	 *col_row;
	} cur_style;
105
	gboolean	 h_align_is_valid, repeat_content;
106
	GnmStyle 	*default_style_cell;
107
	OOStyleType	 cur_style_type;
108
	GSList		*sheet_order;
109
110
111
	int	 	 richtext_len;
	GString		*accum_fmt;
	char		*fmt_name;
112
113

	GnmExprConventions *exprconv;
114
115
} OOParseState;

116
static gboolean oo_warning (GsfXMLIn *xin, char const *fmt, ...)
117
118
	G_GNUC_PRINTF (2, 3);

119
static gboolean
120
oo_warning (GsfXMLIn *xin, char const *fmt, ...)
121
{
122
	OOParseState *state = (OOParseState *)xin->user_state;
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
	char *msg;
	va_list args;

	va_start (args, fmt);
	msg = g_strdup_vprintf (fmt, args);
	va_end (args);

	if (IS_SHEET (state->pos.sheet)) {
		char *tmp;
		if (state->pos.eval.col >= 0 && state->pos.eval.row >= 0)
			tmp = g_strdup_printf ("%s!%s : %s",
				state->pos.sheet->name_quoted,
				cellpos_as_string (&state->pos.eval), msg);
		else
			tmp = g_strdup_printf ("%s : %s",
				state->pos.sheet->name_quoted, msg);
		g_free (msg);
		msg = tmp;
141
	}
142

143
	gnm_io_warning (state->context, "%s", msg);
144
	g_free (msg);
145
146

	return FALSE; /* convenience */
147
}
148

149
static gboolean
150
oo_attr_bool (GsfXMLIn *xin, xmlChar const * const *attrs,
151
	      int ns_id, char const *name, gboolean *res)
152
153
154
155
156
{
	g_return_val_if_fail (attrs != NULL, FALSE);
	g_return_val_if_fail (attrs[0] != NULL, FALSE);
	g_return_val_if_fail (attrs[1] != NULL, FALSE);

157
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
158
159
160
161
162
163
164
		return FALSE;
	*res = g_ascii_strcasecmp ((gchar *)attrs[1], "false") && strcmp (attrs[1], "0");

	return TRUE;
}

static gboolean
165
oo_attr_int (GsfXMLIn *xin, xmlChar const * const *attrs,
166
	     int ns_id, char const *name, int *res)
167
168
169
170
171
172
173
174
{
	char *end;
	int tmp;

	g_return_val_if_fail (attrs != NULL, FALSE);
	g_return_val_if_fail (attrs[0] != NULL, FALSE);
	g_return_val_if_fail (attrs[1] != NULL, FALSE);

175
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
176
177
178
		return FALSE;

	tmp = strtol ((gchar *)attrs[1], &end, 10);
179
	if (*end)
180
		return oo_warning (xin, "Invalid attribute '%s', expected integer, received '%s'",
181
182
				   name, attrs[1]);

183
184
185
186
	*res = tmp;
	return TRUE;
}

187
static gboolean
188
oo_attr_float (GsfXMLIn *xin, xmlChar const * const *attrs,
189
	       int ns_id, char const *name, gnm_float *res)
190
191
192
{
	char *end;
	double tmp;
193

194
195
196
	g_return_val_if_fail (attrs != NULL, FALSE);
	g_return_val_if_fail (attrs[0] != NULL, FALSE);
	g_return_val_if_fail (attrs[1] != NULL, FALSE);
197

198
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
199
		return FALSE;
200

201
	tmp = gnm_strto ((gchar *)attrs[1], &end);
202
	if (*end)
203
		return oo_warning (xin, "Invalid attribute '%s', expected number, received '%s'",
204
				   name, attrs[1]);
205
206
207
	*res = tmp;
	return TRUE;
}
208

209

Morten Welinder's avatar
Morten Welinder committed
210
static GnmColor *
211
oo_parse_color (GsfXMLIn *xin, xmlChar const *str, char const *name)
212
213
214
{
	guint r, g, b;

215
	g_return_val_if_fail (str != NULL, NULL);
216

217
	if (3 == sscanf (str, "#%2x%2x%2x", &r, &g, &b))
218
219
		return style_color_new_i8 (r, g, b);

220
221
222
	if (0 == strcmp (str, "transparent"))
		return NULL;

223
	oo_warning (xin, "Invalid attribute '%s', expected color, received '%s'",
224
		    name, str);
225
226
	return NULL;
}
227
static GnmColor *
228
oo_attr_color (GsfXMLIn *xin, xmlChar const * const *attrs,
229
230
231
232
233
	       int ns_id, char const *name)
{
	g_return_val_if_fail (attrs != NULL, NULL);
	g_return_val_if_fail (attrs[0] != NULL, NULL);

234
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
235
		return NULL;
236
	return oo_parse_color (xin, attrs[1], name);
237
}
238

239
/* returns pts */
240
static char const *
241
oo_parse_distance (GsfXMLIn *xin, xmlChar const *str,
242
		  char const *name, double *pts)
243
244
245
246
{
	double num;
	char *end = NULL;

247
	g_return_val_if_fail (str != NULL, NULL);
248

249
	if (0 == strncmp (str, "none", 2)) {
250
		*pts = 0;
251
		return str + 4;
252
253
	}

254
255
256
	num = strtod (str, &end);
	if (str != (xmlChar const *)end) {
		if (0 == strncmp (end, "mm", 2)) {
257
			num = GO_CM_TO_PT (num/10.);
258
259
			end += 2;
		} else if (0 == strncmp (end, "m", 1)) {
260
			num = GO_CM_TO_PT (num*100.);
261
262
			end ++;
		} else if (0 == strncmp (end, "km", 2)) {
263
			num = GO_CM_TO_PT (num*100000.);
264
265
			end += 2;
		} else if (0 == strncmp (end, "cm", 2)) {
266
			num = GO_CM_TO_PT (num);
267
268
269
270
			end += 2;
		} else if (0 == strncmp (end, "pt", 2)) {
			end += 2;
		} else if (0 == strncmp (end, "pc", 2)) { /* pica 12pt == 1 pica */
271
			num /= 12.;
272
273
			end += 2;
		} else if (0 == strncmp (end, "ft", 2)) {
274
			num = GO_IN_TO_PT (num*12.);
275
276
			end += 2;
		} else if (0 == strncmp (end, "mi", 2)) {
277
			num = GO_IN_TO_PT (num*63360.);
278
279
			end += 2;
		} else if (0 == strncmp (end, "inch", 4)) {
280
			num = GO_IN_TO_PT (num);
281
			end += 4;
282
283
284
		} else if (0 == strncmp (end, "in", 2)) {
			num = GO_IN_TO_PT (num);
			end += 2;
285
		} else {
286
			oo_warning (xin, "Invalid attribute '%s', unknown unit '%s'",
287
288
289
290
				    name, str);
			return NULL;
		}
	} else {
291
		oo_warning (xin, "Invalid attribute '%s', expected distance, received '%s'",
292
293
294
			    name, str);
		return NULL;
	}
295
296

	*pts = num;
297
298
299
300
	return end;
}
/* returns pts */
static char const *
301
oo_attr_distance (GsfXMLIn *xin, xmlChar const * const *attrs,
302
303
304
305
306
307
		  int ns_id, char const *name, double *pts)
{
	g_return_val_if_fail (attrs != NULL, NULL);
	g_return_val_if_fail (attrs[0] != NULL, NULL);
	g_return_val_if_fail (attrs[1] != NULL, NULL);

308
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
309
		return NULL;
310
	return oo_parse_distance (xin, attrs[1], name, pts);
311
312
}

313
314
315
316
317
318
typedef struct {
	char const * const name;
	int val;
} OOEnum;

static gboolean
319
oo_attr_enum (GsfXMLIn *xin, xmlChar const * const *attrs,
320
	      int ns_id, char const *name, OOEnum const *enums, int *res)
321
322
323
324
325
{
	g_return_val_if_fail (attrs != NULL, FALSE);
	g_return_val_if_fail (attrs[0] != NULL, FALSE);
	g_return_val_if_fail (attrs[1] != NULL, FALSE);

326
	if (!gsf_xml_in_namecmp (xin, attrs[0], ns_id, name))
327
328
329
330
331
332
333
		return FALSE;

	for (; enums->name != NULL ; enums++)
		if (!strcmp (enums->name, attrs[1])) {
			*res = enums->val;
			return TRUE;
		}
334
	return oo_warning (xin, "Invalid attribute '%s', unknown enum value '%s'",
335
			   name, attrs[1]);
336
337
}

338
#warning "FIXME: expand this later."
339
340
#define oo_expr_parse_str(str, pp, flags, err)	\
	gnm_expr_parse_str (str, pp,		\
341
		flags, state->exprconv, err)
342

343
/****************************************************************************/
344

345
static void
346
oo_date_convention (GsfXMLIn *xin, xmlChar const **attrs)
347
348
{
	/* <table:null-date table:date-value="1904-01-01"/> */
349
	OOParseState *state = (OOParseState *)xin->user_state;
350
	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
351
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "date-value")) {
352
353
354
355
356
			if (!strncmp (attrs[1], "1904", 4))
				workbook_set_1904 (state->pos.wb, TRUE);
		}
}

357
static void
358
oo_table_start (GsfXMLIn *xin, xmlChar const **attrs)
359
360
{
	/* <table:table table:name="Result" table:style-name="ta1"> */
361
	OOParseState *state = (OOParseState *)xin->user_state;
362

363
	state->pos.eval.col = 0;
364
	state->pos.eval.row = 0;
365
366

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
367
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "name")) {
368
369
370
			state->pos.sheet = workbook_sheet_by_name (state->pos.wb, attrs[1]);
			if (NULL == state->pos.sheet) {
				state->pos.sheet = sheet_new (state->pos.wb, attrs[1]);
371
				workbook_sheet_attach (state->pos.wb, state->pos.sheet);
372
			}
373
374
375
376

			/* store a list of the sheets in the correct order */
			state->sheet_order = g_slist_prepend (state->sheet_order,
							      state->pos.sheet);
377
378
		} else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "style-name"))  {
			/* hidden & rtl vs ltr */
379
380
381
382
		}
}

static void
383
oo_col_start (GsfXMLIn *xin, xmlChar const **attrs)
384
{
385
	OOParseState *state = (OOParseState *)xin->user_state;
Morten Welinder's avatar
Morten Welinder committed
386
	GnmStyle *style = NULL;
JodyGoldberg's avatar
JodyGoldberg committed
387
	double   *width_pts = NULL;
388
389
390
	int repeat_count = 1;

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
391
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "default-cell-style-name"))
392
393
394
			style = g_hash_table_lookup (state->cell_styles, attrs[1]);
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "style-name"))
			width_pts = g_hash_table_lookup (state->col_row_styles, attrs[1]);
395
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-columns-repeated", &repeat_count))
396
397
			;

398
399
400
401
402
403
404
405
406
	if (NULL != style) {
		GnmRange r;
		r.start.col = state->pos.eval.col;
		r.end.col   = state->pos.eval.col + repeat_count - 1;
		r.start.row = 0;
		r.end.row  = SHEET_MAX_ROWS - 1;
		gnm_style_ref (style);
		sheet_style_set_range (state->pos.sheet, &r, style);
	}
407
408
409
	while (repeat_count-- > 0) {
		if (width_pts != NULL)
			sheet_col_set_size_pts (state->pos.sheet,
410
				state->pos.eval.col++, *width_pts, TRUE);
411
	}
412
413
414
}

static void
415
oo_row_start (GsfXMLIn *xin, xmlChar const **attrs)
416
{
417
	OOParseState *state = (OOParseState *)xin->user_state;
418
419
	int	 i, repeat_count = 1;
	double  *height_pts = NULL;
420

421
	state->pos.eval.col = 0;
422

423
	g_return_if_fail (state->pos.eval.row < SHEET_MAX_ROWS);
424
425

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
426
427
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "style-name"))
			height_pts = g_hash_table_lookup (state->col_row_styles, attrs[1]);
428
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-rows-repeated", &repeat_count))
429
			;
430
	}
431
432
433
434
	if (height_pts != NULL)
		for (i = repeat_count; i-- > 0 ; )
			sheet_row_set_size_pts (state->pos.sheet,
				state->pos.eval.row + i, *height_pts, TRUE);
435
436
437
438
439
440
441
	state->row_inc = repeat_count;
}
static void
oo_row_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
{
	OOParseState *state = (OOParseState *)xin->user_state;
	state->pos.eval.row += state->row_inc;
442
}
443

444
static char const *
Morten Welinder's avatar
Morten Welinder committed
445
oo_cellref_parse (GnmCellRef *ref, char const *start, GnmParsePos const *pp)
446
447
{
	char const *tmp1, *tmp2, *ptr = start;
Morten Welinder's avatar
Morten Welinder committed
448
	/* sheet name cannot contain '.' */
449
450
451
452
453
454
455
456
457
458
459
460
461
462
	if (*ptr != '.') {
		char *name;
		if (*ptr == '$') /* ignore abs vs rel sheet name */
			ptr++;
		tmp1 = strchr (ptr, '.');
		if (tmp1 == NULL)
			return start;
		name = g_alloca (tmp1-ptr+1);
		strncpy (name, ptr, tmp1-ptr);
		name[tmp1-ptr] = 0;
		ptr = tmp1+1;

		/* OpenCalc does not pre-declare its sheets, but it does have a
		 * nice unambiguous format.  So if we find a name that has not
463
		 * been added yet add it.  Reorder below.
464
465
466
467
		 */
		ref->sheet = workbook_sheet_by_name (pp->wb, name);
		if (ref->sheet == NULL) {
			ref->sheet = sheet_new (pp->wb, name);
468
			workbook_sheet_attach (pp->wb, ref->sheet);
469
470
471
472
473
474
475
		}
	} else {
		ptr++; /* local ref */
		ref->sheet = NULL;
	}

	tmp1 = col_parse (ptr, &ref->col, &ref->col_relative);
476
	if (!tmp1)
477
478
		return start;
	tmp2 = row_parse (tmp1, &ref->row, &ref->row_relative);
479
	if (!tmp2)
480
481
482
483
484
485
486
487
488
489
		return start;

	if (ref->col_relative)
		ref->col -= pp->eval.col;
	if (ref->row_relative)
		ref->row -= pp->eval.row;
	return tmp2;
}

static char const *
JodyGoldberg's avatar
JodyGoldberg committed
490
491
oo_rangeref_parse (GnmRangeRef *ref, char const *start, GnmParsePos const *pp,
		   GnmExprConventions const *convention)
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
{
	char const *ptr;

	g_return_val_if_fail (start != NULL, start);
	g_return_val_if_fail (pp != NULL, start);

	if (*start != '[')
		return start;
	ptr = oo_cellref_parse (&ref->a, start+1, pp);
	if (*ptr == ':')
		ptr = oo_cellref_parse (&ref->b, ptr+1, pp);
	else
		ref->b = ref->a;

	if (*ptr == ']')
		return ptr + 1;
	return start;
509
510
}

511
512
513
514
515
516
517
518
519
520
static void
oo_cell_content_span_start (GsfXMLIn *xin, xmlChar const **attrs)
{
}

static void
oo_cell_content_span_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
{
}

521
static void
522
oo_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
523
{
524
	OOParseState *state = (OOParseState *)xin->user_state;
525
	GnmExpr const	*expr = NULL;
JodyGoldberg's avatar
JodyGoldberg committed
526
	GnmValue		*val = NULL;
527
	gboolean	 bool_val;
Jody Goldberg's avatar
Jody Goldberg committed
528
	gnm_float	 float_val;
529
	int array_cols = -1, array_rows = -1;
530
	int merge_cols = -1, merge_rows = -1;
Morten Welinder's avatar
Morten Welinder committed
531
	GnmStyle *style = NULL;
532
	char const *expr_string;
533
534
535
536

	state->col_inc = 1;
	state->error_content = FALSE;
	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
537
		if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-columns-repeated", &state->col_inc))
538
			;
539
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "formula")) {
540
			if (attrs[1] == NULL) {
541
				oo_warning (xin, _("Missing expression"));
542
543
				continue;
			}
544
545
546
547
548
549
550
551
552
553
554

			expr_string = attrs[1];
			if (state->ver == OOO_VER_OPENDOC) {
				if (strncmp (expr_string, "oooc:", 5)) {
					oo_warning (xin, _("Missing expression namespace"));
					continue;
				}
				expr_string += 5;
			}

			expr_string = gnm_expr_char_start_p (expr_string);
555
			if (expr_string == NULL)
556
				oo_warning (xin, _("Expression '%s' does not start with a recognized character"), attrs[1]);
557
558
559
560
561
			else if (*expr_string == '\0')
				/* Ick.  They seem to store error cells as
				 * having value date with expr : '=' and the
				 * message in the content.
				 */
562
				state->error_content = TRUE;
563
			else {
Morten Welinder's avatar
Morten Welinder committed
564
				GnmParseError  perr;
565
				parse_error_init (&perr);
566
567
				expr = oo_expr_parse_str (expr_string,
					&state->pos, 0, &perr);
568
				if (expr == NULL) {
569
					oo_warning (xin, _("Unable to parse '%s' because '%s'"),
570
						    attrs[1], perr.err->message);
571
572
573
					parse_error_free (&perr);
				}
			}
574
		} else if (oo_attr_bool (xin, attrs, OO_NS_TABLE, "boolean-value", &bool_val))
575
			val = value_new_bool (bool_val);
576
577
578
		else if (gsf_xml_in_namecmp (xin, attrs[0],
			(state->ver == OOO_VER_OPENDOC) ? OO_NS_OFFICE : OO_NS_TABLE,
			"date-value")) {
579
580
581
582
583
584
			unsigned y, m, d, h, mi;
			float s;
			unsigned n = sscanf (attrs[1], "%u-%u-%uT%u:%u:%g",
					     &y, &m, &d, &h, &mi, &s);

			if (n >= 3) {
585
586
				GDate date;
				g_date_set_dmy (&date, d, m, y);
587
588
589
590
591
592
593
594
595
				if (g_date_valid (&date)) {
					unsigned d_serial = datetime_g_to_serial (&date,
						workbook_date_conv (state->pos.wb));
					if (n >= 6) {
						double time_frac = h + ((double)mi / 60.) + ((double)s / 3600.);
						val = value_new_float (d_serial + time_frac / 24.);
					} else
						val = value_new_int (d_serial);
				}
596
			}
597
598
599
600
		} else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "time-value")) {
			unsigned h, m, s;
			if (3 == sscanf (attrs[1], "PT%uH%uM%uS", &h, &m, &s))
				val = value_new_float (h + ((double)m / 60.) + ((double)s / 3600.));
601
		} else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "string-value"))
602
			val = value_new_string (attrs[1]);
603
604
605
		else if (oo_attr_float (xin, attrs,
			(state->ver == OOO_VER_OPENDOC) ? OO_NS_OFFICE : OO_NS_TABLE,
			"value", &float_val))
606
			val = value_new_float (float_val);
607
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-matrix-columns-spanned", &array_cols))
608
			;
609
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-matrix-rows-spanned", &array_rows))
610
			;
611
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-columns-spanned", &merge_cols))
612
			;
613
		else if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-rows-spanned", &merge_rows))
614
			;
615
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_TABLE, "style-name")) {
616
			style = g_hash_table_lookup (state->cell_styles, attrs[1]);
617
		}
618
619
	}

620
	if (style != NULL) {
JodyGoldberg's avatar
JodyGoldberg committed
621
		gnm_style_ref (style);
622
		if (state->col_inc > 1 || state->row_inc > 1) {
JodyGoldberg's avatar
JodyGoldberg committed
623
			GnmRange tmp;
624
625
626
			range_init (&tmp,
				state->pos.eval.col, state->pos.eval.row,
				state->pos.eval.col + state->col_inc - 1,
627
				state->pos.eval.row + state->row_inc - 1);
628
629
630
631
632
			sheet_style_set_range (state->pos.sheet, &tmp, style);
		} else
			sheet_style_set_pos (state->pos.sheet,
				state->pos.eval.col, state->pos.eval.row,
				style);
633
	}
634
	state->simple_content = FALSE;
635
	if (expr != NULL) {
636
		GnmCell *cell = sheet_cell_fetch (state->pos.sheet,
637
638
639
640
641
			state->pos.eval.col, state->pos.eval.row);

		if (array_cols > 0 || array_rows > 0) {
			if (array_cols < 0) {
				array_cols = 1;
642
				oo_warning (xin, _("Invalid array expression does not specify number of columns."));
643
644
			} else if (array_rows < 0) {
				array_rows = 1;
645
				oo_warning (xin, _("Invalid array expression does not specify number of rows."));
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
			}
			cell_set_array_formula (state->pos.sheet,
				state->pos.eval.col, state->pos.eval.row,
				state->pos.eval.col + array_cols-1,
				state->pos.eval.row + array_rows-1,
				expr);
			if (val != NULL)
				cell_assign_value (cell, val);
		} else {
			if (val != NULL)
				cell_set_expr_and_value (cell, expr, val, TRUE);
			else
				cell_set_expr (cell, expr);
			gnm_expr_unref (expr);
		}
661
	} else if (val != NULL) {
662
		GnmCell *cell = sheet_cell_fetch (state->pos.sheet,
663
664
665
666
667
668
669
			state->pos.eval.col, state->pos.eval.row);

		/* has cell previously been initialized as part of an array */
		if (cell_is_partial_array (cell))
			cell_assign_value (cell, val);
		else
			cell_set_value (cell, val);
670
671
672
	} else if (!state->error_content)
		/* store the content as a string */
		state->simple_content = TRUE;
673
674

	if (merge_cols > 0 && merge_rows > 0) {
JodyGoldberg's avatar
JodyGoldberg committed
675
		GnmRange r;
676
677
678
679
		range_init (&r,
			state->pos.eval.col, state->pos.eval.row,
			state->pos.eval.col + merge_cols - 1,
			state->pos.eval.row + merge_rows - 1);
680
681
		sheet_merge_add (state->pos.sheet, &r, FALSE,
				 NULL);
682
	}
683
684
685
}

static void
686
oo_cell_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
687
{
688
	OOParseState *state = (OOParseState *)xin->user_state;
689
690

	if (state->col_inc > 1) {
691
692
		GnmCell *cell = sheet_cell_get (state->pos.sheet,
			state->pos.eval.col, state->pos.eval.row);
693

694
		if (!cell_is_empty (cell)) {
695
			int i = 1;
696
			GnmCell *next;
697
698
699
			for (; i < state->col_inc ; i++) {
				next = sheet_cell_fetch (state->pos.sheet,
					state->pos.eval.col + i, state->pos.eval.row);
700
				cell_set_value (next, value_dup (cell->value));
701
702
703
704
705
706
707
708
			}
		}
	}

	state->pos.eval.col += state->col_inc;
}

static void
709
oo_covered_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
710
{
711
	OOParseState *state = (OOParseState *)xin->user_state;
712
713
714

	state->col_inc = 1;
	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
715
		if (oo_attr_int (xin, attrs, OO_NS_TABLE, "number-columns-repeated", &state->col_inc))
716
717
718
			;
#if 0
		/* why bother it is covered ? */
719
		else if (!strcmp (attrs[0], OO_NS_TABLE, "style-name"))
720
			style = g_hash_table_lookup (state->cell_styles, attrs[1]);
721
722

	if (style != NULL) {
JodyGoldberg's avatar
JodyGoldberg committed
723
		gnm_style_ref (style);
724
725
726
727
728
729
730
731
		sheet_style_set_pos (state->pos.sheet,
		     state->pos.eval.col, state->pos.eval.row,
		     style);
	}
#endif
}

static void
732
oo_covered_cell_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
733
{
734
	OOParseState *state = (OOParseState *)xin->user_state;
735
	state->pos.eval.col += state->col_inc;
736
737
738
}

static void
739
oo_cell_content_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
740
{
741
	OOParseState *state = (OOParseState *)xin->user_state;
742

743
	if (state->simple_content || state->error_content) {
JodyGoldberg's avatar
JodyGoldberg committed
744
		GnmValue *v;
745
746
		GnmCell *cell = sheet_cell_fetch (state->pos.sheet,
			state->pos.eval.col, state->pos.eval.row);
747
748

		if (state->simple_content)
749
			v = value_new_string (xin->content->str);
750
		else
751
			v = value_new_error (NULL, xin->content->str);
752
		cell_set_value (cell, v);
753
754
	}
}
755

Jody Goldberg's avatar
Jody Goldberg committed
756
static void
757
oo_style (GsfXMLIn *xin, xmlChar const **attrs)
Jody Goldberg's avatar
Jody Goldberg committed
758
{
759
760
761
762
763
764
765
	static OOEnum const style_types [] = {
		{ "table-cell",	  OO_STYLE_CELL },
		{ "table-row",	  OO_STYLE_ROW },
		{ "table-column", OO_STYLE_COL },
		{ "table",	  OO_STYLE_SHEET },
		{ "graphics",	  OO_STYLE_GRAPHICS },
		{ "paragraph",	  OO_STYLE_PARAGRAPH },
766
		{ "text",	  OO_STYLE_TEXT },
767
768
769
		{ NULL,	0 },
	};

770
	OOParseState *state = (OOParseState *)xin->user_state;
771
	xmlChar const *name = NULL;
772
773
	xmlChar const *parent_name = NULL;
	GnmStyle *style;
774
	GOFormat *fmt = NULL;
775
776
777
	int tmp;

	g_return_if_fail (state->cur_style_type == OO_STYLE_UNKNOWN);
778
779

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
780
		if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "family", style_types, &tmp))
781
782
			state->cur_style_type = tmp;
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_STYLE, "name"))
783
			name = attrs[1];
784
785
786
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_STYLE, "parent-style-name"))
			parent_name = attrs[1];
		else if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_STYLE, "data-style-name")) {
787
			GOFormat *tmp = g_hash_table_lookup (state->formats, attrs[1]);
788
789
			if (tmp != NULL)
				fmt = tmp;
790
		}
791

792
	switch (state->cur_style_type) {
Morten Welinder's avatar
Morten Welinder committed
793
	case OO_STYLE_CELL:
794
795
796
797
		style = (parent_name != NULL)
			? g_hash_table_lookup (state->cell_styles, parent_name)
			: NULL;
		state->cur_style.cell = (style != NULL)
JodyGoldberg's avatar
JodyGoldberg committed
798
			? gnm_style_dup (style) : gnm_style_new_default ();
799
		state->h_align_is_valid = state->repeat_content = FALSE;
800
801

		if (fmt != NULL)
JodyGoldberg's avatar
JodyGoldberg committed
802
			gnm_style_set_format (state->cur_style.cell, fmt);
803

804
		if (name != NULL)
Morten Welinder's avatar
Morten Welinder committed
805
806
807
			g_hash_table_replace (state->cell_styles,
					      g_strdup (name),
					      state->cur_style.cell);
808
809
810
811
812
		else if (0 == strcmp (xin->node->id, "DEFAULT_STYLE")) {
			 if (state->default_style_cell)
				 gnm_style_unref (state->default_style_cell);
			 state->default_style_cell = state->cur_style.cell;
		}
813
		break;
814

Morten Welinder's avatar
Morten Welinder committed
815
816
	case OO_STYLE_COL:
	case OO_STYLE_ROW:
817
		state->cur_style.col_row = g_new0 (double, 1);
Morten Welinder's avatar
Morten Welinder committed
818
819
820
821
		if (name)
			g_hash_table_replace (state->col_row_styles,
					      g_strdup (name),
					      state->cur_style.col_row);
822
823
		break;

Morten Welinder's avatar
Morten Welinder committed
824
	default:
825
		break;
826
	}
Jody Goldberg's avatar
Jody Goldberg committed
827
}
828
829

static void
830
oo_style_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
831
{
832
	OOParseState *state = (OOParseState *)xin->user_state;
833

834
	switch (state->cur_style_type) {
835
836
837
838
839
840
841
842
843
844
	case OO_STYLE_CELL : state->cur_style.cell = NULL;
		break;
	case OO_STYLE_COL : 
	case OO_STYLE_ROW : state->cur_style.col_row = NULL;
		break;

	default :
		break;
	}
	state->cur_style_type = OO_STYLE_UNKNOWN;
845
846
}

847
848
849
static void
oo_date_day (GsfXMLIn *xin, xmlChar const **attrs)
{
850
	OOParseState *state = (OOParseState *)xin->user_state;
851
852
853
854
855
856
857
858
859
860
861
862
863
864
	gboolean is_short = TRUE;

	if (state->accum_fmt == NULL)
		return;

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_NUMBER, "style"))
			is_short = 0 == strcmp (attrs[1], "short");

	g_string_append (state->accum_fmt, is_short ? "d" : "dd");
}
static void
oo_date_month (GsfXMLIn *xin, xmlChar const **attrs)
{
865
	OOParseState *state = (OOParseState *)xin->user_state;
866
867
868
869
870
871
872
873
874
	gboolean as_text = FALSE;
	gboolean is_short = TRUE;

	if (state->accum_fmt == NULL)
		return;

	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
		if (gsf_xml_in_namecmp (xin, attrs[0], OO_NS_NUMBER, "style"))
			is_short = 0 == strcmp (attrs[1], "short");
875
		else if (oo_attr_bool (xin, attrs, OO_NS_NUMBER, "textual", &as_text))