[gnumeric] Lotus: initial support for Works.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] Lotus: initial support for Works.
- Date: Wed, 4 May 2011 19:27:26 +0000 (UTC)
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]