[gnumeric] Lotus: initial support for Works.



commit 3bc8fe37ae8ed6393cb1948e20bf794194b5aeba
Author: Morten Welinder <terra gnome org>
Date:   Wed May 4 15:27:04 2011 -0400

    Lotus: initial support for Works.

 NEWS                              |    3 +
 plugins/lotus-123/ChangeLog       |    5 +
 plugins/lotus-123/boot.c          |   18 ++-
 plugins/lotus-123/lotus-formula.c |    2 +
 plugins/lotus-123/lotus-types.h   |   11 +
 plugins/lotus-123/lotus.c         |  446 ++++++++++++++++++++++++++++++++++++-
 plugins/lotus-123/lotus.h         |    1 +
 7 files changed, 477 insertions(+), 9 deletions(-)
---
diff --git a/NEWS b/NEWS
index 8f80f31..837c9c3 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,9 @@ Morten:
 	* Fix minor problem reading old xls objects.
 	* Reduce memory usage when new glib is used.  [#644197]
 
+Urmas:
+	* Initial support for Works files.  [#647492]
+
 --------------------------------------------------------------------------
 Gnumeric 1.10.14
 
diff --git a/plugins/lotus-123/ChangeLog b/plugins/lotus-123/ChangeLog
index 79ce7e5..9f5c0bc 100644
--- a/plugins/lotus-123/ChangeLog
+++ b/plugins/lotus-123/ChangeLog
@@ -1,3 +1,8 @@
+2011-05-04  Morten Welinder  <terra gnome org>
+
+	* lotus.c: Add partial works supports.  Patch from Urmas
+	(davian818 gmail com)
+
 2011-03-24  Morten Welinder <terra gnome org>
 
 	* Release 1.10.14
diff --git a/plugins/lotus-123/boot.c b/plugins/lotus-123/boot.c
index 6f1a04c..7a8e14b 100644
--- a/plugins/lotus-123/boot.c
+++ b/plugins/lotus-123/boot.c
@@ -23,9 +23,9 @@
 
 GNM_PLUGIN_MODULE_HEADER;
 
-gboolean lotus_file_probe (GOFileOpener const *fo, GsfInput *input,
+G_MODULE_EXPORT gboolean lotus_file_probe (GOFileOpener const *fo, GsfInput *input,
                            GOFileProbeLevel pl);
-void     lotus_file_open (GOFileOpener const *fo, GOIOContext *io_context,
+G_MODULE_EXPORT void     lotus_file_open (GOFileOpener const *fo, GOIOContext *io_context,
                           WorkbookView *wb_view, GsfInput *input);
 
 
@@ -40,7 +40,8 @@ lotus_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
 		h = gsf_input_read (input, 6, NULL);
 
 	if (h == NULL ||
-	    GSF_LE_GET_GUINT16 (h + 0) != LOTUS_BOF)
+	    (GSF_LE_GET_GUINT16 (h + 0) != LOTUS_BOF &&
+		GSF_LE_GET_GUINT16 (h + 0) != WORKS_BOF))
 		return FALSE;
 
 	len = GSF_LE_GET_GUINT16 (h + 2);
@@ -77,10 +78,21 @@ lotus_file_open (GOFileOpener const *fo, GOIOContext *io_context,
 	state.wb	 = wb_view_get_workbook (wb_view);
 	state.sheet	 = NULL;
 	state.sheet_area_error = FALSE;
+	state.style_pool = NULL;
+	state.fonts      = NULL;
 
 	if (!lotus_read (&state))
 		go_io_error_string (io_context,
 			_("Error while reading lotus workbook."));
+
+	if (state.style_pool) {
+		g_hash_table_destroy (state.style_pool);
+		state.style_pool = NULL;
+	}
+	if (state.fonts) {
+		g_hash_table_destroy (state.fonts);
+		state.fonts = NULL;
+	}
 }
 
 
diff --git a/plugins/lotus-123/lotus-formula.c b/plugins/lotus-123/lotus-formula.c
index a6aeb9d..dde9dc1 100644
--- a/plugins/lotus-123/lotus-formula.c
+++ b/plugins/lotus-123/lotus-formula.c
@@ -560,6 +560,8 @@ lotus_parse_formula_old (LotusState *state, GnmParsePos *orig,
 			i++;
 			break;
 		case 0x17: HANDLE_UNARY (GNM_EXPR_OP_UNARY_PLUS);
+		/* Works */
+		case 0x18: HANDLE_BINARY (GNM_EXPR_OP_CAT);
 
 		default:
 			i += make_function (&stack, data + i, orig);
diff --git a/plugins/lotus-123/lotus-types.h b/plugins/lotus-123/lotus-types.h
index 81f92d8..1b9a751 100644
--- a/plugins/lotus-123/lotus-types.h
+++ b/plugins/lotus-123/lotus-types.h
@@ -109,4 +109,15 @@
 #define LOTUS_NAMED_STYLE_DB         0xa80
 #define LOTUS_END_DATA               0x2af6
 
+/* -------------------------------------------------------------------------- */
+
+#define WORKS_VERSION_3 0x0404
+
+#define WORKS_BOF 0xff
+#define WORKS_FONT  0x5456
+#define WORKS_STYLE 0x545a
+#define WORKS_SMALL_FLOAT 0x545b
+
+/* -------------------------------------------------------------------------- */
+
 #endif
diff --git a/plugins/lotus-123/lotus.c b/plugins/lotus-123/lotus.c
index 11f4a19..8891224 100644
--- a/plugins/lotus-123/lotus.c
+++ b/plugins/lotus-123/lotus.c
@@ -784,6 +784,8 @@ typedef struct {
 	guint8 const *data;
 } record_t;
 
+static gboolean lotus_read_works (LotusState *state, record_t *r);
+
 static GnmValue *lotus_get_strval (const record_t *r, int ofs, int def_group);
 
 static void
@@ -2027,8 +2029,6 @@ lotus_read_new (LotusState *state, record_t *r)
 
 #define FACEBIT(bitval,attr)							\
 	if (facemask & bitval) {						\
-                if (facebits & bitval)						\
-			g_print ("Bit value %d set.\n", bitval);		\
 		gnm_style_set_font_ ## attr (style, (facebits & bitval) != 0);	\
 	}
 				FACEBIT (0x0001,bold);
@@ -2364,9 +2364,6 @@ lotus_read_new (LotusState *state, record_t *r)
 		rldb = NULL;
 	}
 
-	g_hash_table_destroy (state->style_pool);
-	state->style_pool = NULL;
-
 	return result;
 }
 
@@ -2376,7 +2373,9 @@ lotus_read (LotusState *state)
 	record_t r;
 	r.input = state->input;
 
-	if (record_next (&r) && r.type == LOTUS_BOF) {
+	if (!record_next (&r)) return FALSE;
+
+	if (r.type == LOTUS_BOF) {
 		state->version = GSF_LE_GET_GUINT16 (r.data);
 #if LOTUS_DEBUG > 0
 		g_print ("Version=%x\n", state->version);
@@ -2396,6 +2395,13 @@ lotus_read (LotusState *state)
 		case LOTUS_VERSION_123SS98:
 			return lotus_read_new (state, &r);
 		}
+	} else if (r.type == WORKS_BOF) {
+		state->version = GSF_LE_GET_GUINT16 (r.data);
+#if LOTUS_DEBUG > 0
+		g_print ("Version=%x\n", state->version);
+#endif
+		if (state->version == WORKS_VERSION_3)
+			return lotus_read_works (state, &r);
 	}
 
 	return FALSE;
@@ -2411,3 +2417,431 @@ lmbcs_shutdown (void)
 {
 	gsf_iconv_close (lmbcs_12_iconv);
 }
+
+typedef struct {
+	char *typeface;
+	int variant;
+	int size;
+	int codepage;
+} WksFontEntry;
+
+static WksFontEntry *
+wks_font_new (void)
+{
+	return g_new0 (WksFontEntry, 1);
+}
+
+static void
+wks_font_dtor (WksFontEntry *s)
+{
+	g_free (s->typeface);
+	g_free (s);
+}
+
+static void
+cell_set_fmt (LotusState *state, GnmCell *cell, int fmt)
+{
+	GnmRange range;
+	GnmStyle *style;
+
+	range.start = cell->pos;
+	range.end = range.start;
+	
+	style = g_hash_table_lookup (state->style_pool, 
+				     GINT_TO_POINTER (fmt));
+
+	if (style) {
+		gnm_style_ref (style);
+		sheet_apply_style (state->sheet, &range, style);
+	}
+}
+
+static const guint8 works_color_table[16][3]={
+	{0,0,0}, /* dummy */
+	{0,0,0},
+	{0,0,255},
+	{0,255,255},
+	{0,255,0},
+	{255,0,255},
+	{255,0,0},
+	{255,255,0},
+	{128,128,128},
+	{255,255,255},
+	{0,0,128},
+	{0,128,128},
+	{0,128,0},
+	{128,0,128},
+	{128,0,0},
+	{192,192,192}
+};
+
+static GnmColor *
+works_color (guint i)
+{
+	if (i == 0) return style_color_auto_font();
+	if (i < G_N_ELEMENTS (works_color_table))
+		return style_color_new_i8 (works_color_table[i][0],
+					   works_color_table[i][1],
+					   works_color_table[i][2]);
+	return NULL;
+}
+
+static const gchar* works_data_fmts[]=
+{
+	"dd.mm.yyyy",
+	"d mmmm yyyy",
+	"dd.yyyy",
+	"mmmm yyyy",
+	"dd.mm",
+	"d mmmm",
+	"dd-mm-yy", /* looks unused */
+	"mmmm"
+};
+
+static const gchar* works_time_fmts[]=
+{
+	"h:mm AM/PM",
+	"h:mm:ss AM/PM",
+	"h:mm",
+	"h:mm:ss"
+};
+
+static const gchar* works_frac_fmts[]=
+{
+	"# ?" "?/?" "?" /* silly trick to avoid using a trigraph */,
+	"# ?/4",
+	"#", /* unused ? */
+	"# ?/8",
+	"# ?/10",
+	"# ?/16",
+	"# ?/32",
+	"# ?/100"
+};
+
+static char *
+works_format_string (guint8 arg)
+{
+	int type, prec;
+	GString *str;
+
+	type = arg & 0xf;
+	prec = (arg >> 5) & 7;
+
+	str = g_string_new(NULL);
+
+	switch (type) {
+		case 0: /* fixed */
+		case 2: /* currency */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			break;
+		case 1: /* exp */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			g_string_append(str,"E+00");
+			break;
+		case 3: /* percent */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			g_string_append(str,"%");
+			break;
+		case 4: /* thousands */
+			g_string_append(str,"# ##0");
+			append_precision(str,prec);
+			break;
+		case 5:
+
+			switch (prec) {
+				case 0: /* general */
+				case 1: /* boolean */
+					break;
+				case 2:
+				case 3:
+				case 4:
+				case 5:
+					g_string_append(str,works_time_fmts[prec-2]);
+					break;
+			};
+			break;
+		case 6:
+			g_string_append(str,works_data_fmts[prec]);
+			break;
+		case 10: /* fixed */
+			g_string_append_len(str,"000000000",prec+1);
+			break;
+		case 11: /* fraction (no ...) */
+		case 12: /* fraction */
+			if (type == 11) {
+				if (prec == 0) {
+					g_string_append(str,"# ?/2");
+					break;
+				} else if (prec == 1) {
+					g_string_append(str,"# ?/3");
+					break;
+				}
+			}
+			g_string_append(str,works_frac_fmts[prec]);
+			break;
+		case 13: /* currency + red */
+		case 14: /* thousands + red */
+			g_string_append(str,"# ##0");
+			append_precision(str,prec);
+			g_string_append(str,";[Red]-# ##0"); /* l10n??? */
+			append_precision(str,prec);
+			break;
+	}
+	return g_string_free(str, FALSE);
+}
+
+static gboolean
+lotus_read_works (LotusState *state, record_t *r)
+{
+	gboolean result = TRUE;
+	int sheetidx = 0;
+	int styleidx = 0;
+	int fontidx  = 0;
+	GnmCell    *cell;
+
+	state->style_pool = g_hash_table_new_full
+	(g_direct_hash,
+	 g_direct_equal,
+	 NULL,
+	 (GDestroyNotify)gnm_style_unref);
+
+	state->fonts = g_hash_table_new_full
+	(g_direct_hash,
+	 g_direct_equal,
+	 NULL,
+	 (GDestroyNotify)wks_font_dtor);
+
+	state->lmbcs_group = 1;
+
+	do {
+		switch (r->type) {
+		case WORKS_BOF:
+			state->sheet = attach_sheet (state->wb, sheetidx++);
+			break;
+
+		case LOTUS_EOF:
+			state->sheet = NULL;
+			break;
+
+		case WORKS_SMALL_FLOAT: CHECK_RECORD_SIZE (>= 6) {
+			int row = GSF_LE_GET_GUINT16 (r->data + 2);
+			int col = r->data[0];
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			guint32 raw = GSF_LE_GET_GUINT32 (r->data + 6);
+			char flag = raw & 1;
+			float x;
+			GnmValue *v;
+
+			raw = (raw&0xfc000000)|((raw&0x3fffffe)<<3);
+			x = gsf_le_get_float (&raw);
+			v = lotus_value (flag ? x/100.0 : x);
+			(void)insert_value (state, state->sheet, col, row, v);
+			break;
+		}
+#if 0
+		case LOTUS_INTEGER: CHECK_RECORD_SIZE (>= 7) {
+			GnmValue *v = value_new_int (GSF_LE_GET_GINT16 (r->data + 5));
+			guint8 fmt = GSF_LE_GET_GUINT8 (r->data);
+			int i = GSF_LE_GET_GUINT16 (r->data + 1);
+			int j = GSF_LE_GET_GUINT16 (r->data + 3);
+
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_format_from_lotus_format (cell, fmt);
+			break;
+		}
+#endif
+
+		case LOTUS_NUMBER: CHECK_RECORD_SIZE (>= 14) {
+			GnmValue *v = lotus_value (gsf_le_get_double (r->data + 6));
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_LABEL: CHECK_RECORD_SIZE (>= 8) {
+			GnmValue *v = lotus_get_strval (r, 6, state->lmbcs_group);
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_BLANK: CHECK_RECORD_SIZE (>= 6) {
+			GnmValue *v = value_new_empty();
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_FORMULA: CHECK_RECORD_SIZE (>= 16) {
+			int col = GSF_LE_GET_GUINT16 (r->data + 0);
+			int row = GSF_LE_GET_GUINT16 (r->data + 2);
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int len = GSF_LE_GET_GINT16 (r->data + 14);
+			GnmExprTop const *texpr;
+			GnmParsePos pp;
+			GnmValue *v = NULL;
+
+			if (r->len < (15 + len))
+				break;
+
+			pp.eval.col = col;
+			pp.eval.row = row;
+			pp.sheet = state->sheet;
+			pp.wb = pp.sheet->workbook;
+			texpr = lotus_parse_formula (state, &pp,
+						     r->data + 16, len);
+
+			if (0x7ff0 == (GSF_LE_GET_GUINT16 (r->data + 12) & 0x7ff8)) {
+				if (LOTUS_STRING == record_peek_next (r)) {
+					record_next (r);
+					v = lotus_get_strval (r, 6, state->lmbcs_group);
+				} else
+					v = value_new_error_VALUE (NULL);
+			} else
+				v = lotus_value (gsf_le_get_double (r->data + 6));
+			cell = lotus_cell_fetch (state, state->sheet, col, row);
+			if (cell) {
+				gnm_cell_set_expr_and_value (cell, texpr, v, TRUE);
+				cell_set_fmt (state, cell, fmt);
+			} else
+				value_release (v);
+			gnm_expr_top_unref (texpr);
+			break;
+		}
+
+		case WORKS_FONT: CHECK_RECORD_SIZE (>= 38) {
+			WksFontEntry *font = wks_font_new();
+			int fid = fontidx++;
+			int l;
+			font->variant = GSF_LE_GET_GUINT16(r->data + 0);
+			l=strlen(r->data + 2);
+			if (l > 34) l = 34;
+			font->typeface = g_malloc(l + 1);
+			/* verify UTF-8? */
+			memcpy(font->typeface, r->data + 2, l);
+			((char*)font->typeface)[l] = 0;
+			font->size = r->data[36];
+			g_hash_table_insert (state->fonts,
+			     GUINT_TO_POINTER ((guint)fid),
+			     font);
+			break;
+		}
+
+		case WORKS_STYLE: CHECK_RECORD_SIZE (>= 10) {
+			GnmStyle *style;
+			GnmColor *color;
+			WksFontEntry *font;
+			char *fmt;
+			int tmp;
+
+			guint16 styleid = styleidx++;
+			guint8 fontid = GSF_LE_GET_GUINT8 (r->data + 4);
+			/*guint16 fontsize = GSF_LE_GET_GUINT16 (r->data + 10);
+			guint16 textbg = GSF_LE_GET_GUINT16 (r->data + 14);*/
+			guint16 facebits;
+			guint8 align = GSF_LE_GET_GUINT8 (r->data + 1);
+			/*guint16 angle = GSF_LE_GET_GUINT16 (r->data + 22);
+			guint16 intfg = GSF_LE_GET_GUINT16 (r->data + 24);
+			guint16 intbg = GSF_LE_GET_GUINT16 (r->data + 26);
+			guint8 intpat = GSF_LE_GET_GUINT8 (r->data + 28);*/
+
+			style = gnm_style_new ();
+
+			font = g_hash_table_lookup(state->fonts,GUINT_TO_POINTER ((guint)fontid));
+
+			if (font) {
+				facebits = font->variant;
+#define FACEBIT(bitval,attr)							\
+		gnm_style_set_font_ ## attr (style, (facebits & bitval) != 0);
+				FACEBIT (0x0001,bold);
+				FACEBIT (0x0002,italic);
+				FACEBIT (0x0004,uline);
+				FACEBIT (0x0008,strike);
+#undef FACEBIT
+				if (font->size != 0)
+					gnm_style_set_font_size (style, font->size / 2.0);
+
+				if (font->variant & 0xF0) {
+					color = works_color((font->variant >> 4) & 0xF);
+					if (color)
+						gnm_style_set_font_color (style, color);
+				}
+
+				if (font->typeface)
+					gnm_style_set_font_name(style, font->typeface);
+			}
+
+			tmp = (align >> 2) & 7;
+			switch (tmp) {
+				case 1:
+					tmp = HALIGN_LEFT;
+					break;
+				case 2:
+					tmp = HALIGN_CENTER;
+					break;
+				case 3:
+					tmp = HALIGN_RIGHT;
+					break;
+				case 4:
+					tmp = HALIGN_FILL;
+					break;
+				default:
+					tmp = HALIGN_GENERAL;
+			}
+			gnm_style_set_align_h(style, tmp);
+
+			tmp = (align >> 6) & 3;
+			switch (tmp) {
+				case 0:
+					tmp = VALIGN_BOTTOM;
+					break;
+				case 1:
+					tmp = VALIGN_CENTER;
+					break;
+				case 2:
+					tmp = VALIGN_TOP;
+					break;
+			}
+			gnm_style_set_align_v(style, tmp);
+
+			if (align & 0x20) {
+				gnm_style_set_wrap_text(style, TRUE);
+			}
+
+			fmt = works_format_string(r->data[0]);
+			if (fmt) {
+				if (*fmt) gnm_style_set_format_text(style, fmt);
+				g_free(fmt);
+			}
+
+#ifdef DEBUG_STYLE
+			g_print ("Defining style 0x%04x:\n", styleid);
+			gnm_style_dump (style);
+#endif
+			g_hash_table_insert (state->style_pool,
+					     GUINT_TO_POINTER ((guint)styleid),
+					     style);
+		}
+
+		default:
+			g_warning ("Unknown record 0x%x of length %d.", r->type, r->len);
+			break;
+		}
+	} while (record_next (r));
+
+	return result;
+}
diff --git a/plugins/lotus-123/lotus.h b/plugins/lotus-123/lotus.h
index 8e31341..7967d31 100644
--- a/plugins/lotus-123/lotus.h
+++ b/plugins/lotus-123/lotus.h
@@ -25,6 +25,7 @@ typedef struct {
 
         GHashTable      *style_pool;
 	gboolean        sheet_area_error;
+	GHashTable      *fonts;
 } LotusState;
 
 Sheet	 *lotus_get_sheet  (Workbook *wb, int i);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]