1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * qpro-read.c: Read Quatro Pro files
6 * Copyright (C) 2002 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 * Docs for the format used to be at
25 * www.corel.com/partners_developers/ds/CO32SDK/docs/qp7/Qpf3recd.htm
26 * www.corel.com/partners_developers/ds/CO32SDK/docs/qp7/Qpf2intr.htm
30 #include <gnumeric-config.h>
31 #include <glib/gi18n-lib.h>
38 #include <goffice/goffice.h>
39 #include <workbook-view.h>
46 #include <sheet-style.h>
47 #include <style-color.h>
48 #include <parse-util.h>
49 #include <gnm-plugin.h>
51 #include <gsf/gsf-utils.h>
52 #include <gsf/gsf-input.h>
53 #include <gsf/gsf-infile.h>
54 #include <gsf/gsf-infile-msole.h>
56 GNM_PLUGIN_MODULE_HEADER
;
58 gboolean
qpro_file_probe (GOFileOpener
const *fo
, GsfInput
*input
,
60 void qpro_file_open (GOFileOpener
const *fo
, GOIOContext
*context
,
61 WorkbookView
*new_wb_view
, GsfInput
*input
);
64 qpro_check_signature (GsfInput
*input
)
69 if (gsf_input_seek (input
, 0, G_SEEK_SET
) ||
70 NULL
== (header
= gsf_input_read (input
, 2+2+2, NULL
)) ||
71 GSF_LE_GET_GUINT16 (header
+ 0) != 0 ||
72 GSF_LE_GET_GUINT16 (header
+ 2) != 2)
74 version
= GSF_LE_GET_GUINT16 (header
+ 4);
75 return (version
== 0x1001 || /* 'WB1' format, documented */
76 version
== 0x1002 || /* 'WB2' format, documented */
77 version
== 0x1006 || /* qpro 6.0 ?? */
78 version
== 0x1007); /* qpro 7.0 ?? */
82 qpro_file_probe (GOFileOpener
const *fo
, GsfInput
*input
, GOFileProbeLevel pl
)
88 /* check for >= QPro 6.0 which is OLE based */
89 ole
= gsf_infile_msole_new (input
, NULL
);
91 stream
= gsf_infile_child_by_name (GSF_INFILE (ole
),
92 "PerfectOffice_MAIN");
94 res
= qpro_check_signature (stream
);
95 g_object_unref (stream
);
99 res
= qpro_check_signature (input
);
106 GOIOContext
*io_context
;
115 corrupted (QProReadState
*state
)
117 if (!state
->corrupted
) {
118 state
->corrupted
= TRUE
;
119 g_printerr (_("File is most likely corrupted.\n"));
124 q_condition_barf (QProReadState
*state
, const char *cond
)
127 /* Translation is screwed here. */
128 g_printerr ("Condition \"%s\" failed.\n", cond
);
131 #define Q_CHECK_CONDITION(cond_) \
134 q_condition_barf (state, #cond_); \
141 qpro_new_string (QProReadState
*state
, gchar
const *data
)
143 return value_new_string_nocopy (
144 g_convert_with_iconv (data
, -1, state
->converter
,
149 static guint8
const *
150 qpro_get_record (QProReadState
*state
, guint16
*id
, guint16
*len
)
154 data
= gsf_input_read (state
->input
, 4, NULL
);
155 Q_CHECK_CONDITION (data
!= NULL
);
157 *id
= GSF_LE_GET_GUINT16 (data
+ 0);
158 *len
= GSF_LE_GET_GUINT16 (data
+ 2);
161 g_printerr ("%hd with %hd\n", *id
, *len
);
167 data
= gsf_input_read (state
->input
, *len
, NULL
);
170 case QPRO_UNDOCUMENTED_837
:
171 case QPRO_UNDOCUMENTED_907
:
172 break; /* Nothing. */
174 Q_CHECK_CONDITION (*len
< 0x2000);
177 Q_CHECK_CONDITION (data
!= NULL
);
185 #define validate(f,expected) qpro_validate_len (state, #f, len, expected)
188 qpro_validate_len (QProReadState
*state
, char const *id
, guint16 len
, int expected_len
)
190 if (expected_len
>= 0 && len
!= expected_len
) {
192 g_printerr ("Invalid '%s' record of length %hd instead of %d\n",
193 id
, len
, expected_len
);
200 enum { ARGS_UNKNOWN
= -1, ARGS_COUNT_FOLLOWS
= -2 };
205 } const qpro_functions
[QPRO_OP_LAST_FUNC
- QPRO_OP_FIRST_FUNC
+ 1] = {
206 { "err", ARGS_UNKNOWN
}, /* No args -- returns error. */
222 { "choose", ARGS_COUNT_FOLLOWS
},
230 { "pmt", ARGS_UNKNOWN
}, /* (pv,Rate,Nper) */
231 { "pv", ARGS_UNKNOWN
}, /* (pmt, Rate, Nper) */
232 { "fv", ARGS_UNKNOWN
}, /* (pmt, Rate, Nper) */
250 { "find", ARGS_UNKNOWN
}, /* (subString, String,StartNumber) */
251 { "dateval", ARGS_UNKNOWN
}, /* @datevalue(datestring) */
252 { "timeval", ARGS_UNKNOWN
}, /* @timevalue(timestring) */
253 { "cellptr", ARGS_UNKNOWN
}, /* @cellpointer(attribute) -- the requested attribute of the current cell */
254 { "sum", ARGS_COUNT_FOLLOWS
},
255 { "avg", ARGS_COUNT_FOLLOWS
},
256 { "count", ARGS_COUNT_FOLLOWS
},
257 { "min", ARGS_COUNT_FOLLOWS
},
258 { "max", ARGS_COUNT_FOLLOWS
},
260 { "npv1", ARGS_UNKNOWN
},
261 { "var", ARGS_COUNT_FOLLOWS
},
262 { "std", ARGS_COUNT_FOLLOWS
},
263 { "irr", ARGS_UNKNOWN
}, /* @irr(guess,block) */
264 { "hlookup", 3 }, /* @hlookup(X,Block,Row) */
272 { "index2d", ARGS_UNKNOWN
}, /* Possibly @index(Block, Column, Row, <Page>) */
282 { "cell", ARGS_UNKNOWN
}, /* @cell(attribute,Block) */
285 { "s", ARGS_UNKNOWN
}, /* @s(Block) -- The string value of the upper left cell in Block (blank, if it is a value entry). */
286 { "n", ARGS_UNKNOWN
}, /* @n(block) -- The numberic vlue of the upper lwft cell in Block (0, if it is a label or blank). */
288 { "call", ARGS_UNKNOWN
},
289 { "at", ARGS_UNKNOWN
}, /* @@(Cell) */
290 { "rate", ARGS_UNKNOWN
}, /* @rate(Fv,Pv,Nper) */
291 { "term", ARGS_UNKNOWN
}, /* @term(Pmt,Rate,Fv) */
292 { "cterm", ARGS_UNKNOWN
}, /* @cterm(Rate,Fv,Pv) */
293 { "sln", ARGS_UNKNOWN
}, /* @sln(cost,salvage,life) */
294 { "syd", ARGS_UNKNOWN
}, /* (cost,salvage,life,period) */
295 { "ddb", ARGS_UNKNOWN
}, /* (cost,salvage,life,period) */
296 { "stdp", ARGS_COUNT_FOLLOWS
},
297 { "varp", ARGS_COUNT_FOLLOWS
},
300 { "pval", ARGS_UNKNOWN
}, /* (Rate,Nper,Pmt,<Fv>,<Type>) */
301 { "paymt", ARGS_UNKNOWN
}, /* (Rate,Nper,Pv,<Fv>,<Type>) */
302 { "fval", ARGS_UNKNOWN
}, /* (Rate,Nper,Pmt,<Fv>,<Type>) */
303 { "nper", ARGS_UNKNOWN
}, /* (Rate,Pmt,Pv,<Fv><Type>) */
304 { "irate", ARGS_UNKNOWN
}, /* (Nper,Pmt,Pv,<Fv>,<Type>) */
305 { "ipaymt", ARGS_UNKNOWN
}, /* (Rate,Per,Nper,Pv,<Fv>,<Type>) */
306 { "ppaymt", ARGS_UNKNOWN
}, /* (Rate,Per,Nper,Pv,<Fv>,<Type>) */
308 { "memavail", ARGS_UNKNOWN
}, /* No args. */
309 { "mememsavail", ARGS_UNKNOWN
}, /* No args. Returns NA. */
310 { "fileexists", ARGS_UNKNOWN
}, /* @fileexists(FileName) */
311 { "curvalue", ARGS_UNKNOWN
},
317 { "npv2", ARGS_UNKNOWN
},
318 { "cellindex2d", ARGS_UNKNOWN
},
319 { "version", ARGS_UNKNOWN
},
320 { NULL
, ARGS_UNKNOWN
},
321 { NULL
, ARGS_UNKNOWN
},
322 { NULL
, ARGS_UNKNOWN
},
323 { NULL
, ARGS_UNKNOWN
},
324 { NULL
, ARGS_UNKNOWN
},
325 { NULL
, ARGS_UNKNOWN
},
326 { NULL
, ARGS_UNKNOWN
},
327 { NULL
, ARGS_UNKNOWN
},
328 { "sheets", ARGS_UNKNOWN
},
329 { NULL
, ARGS_UNKNOWN
},
330 { NULL
, ARGS_UNKNOWN
},
331 { "index3d", ARGS_UNKNOWN
},
332 { "cellindex3d", ARGS_UNKNOWN
},
333 { "property", ARGS_UNKNOWN
},
334 { "ddelink", ARGS_UNKNOWN
},
335 { "command", ARGS_UNKNOWN
}
340 dump_missing_functions (void)
342 static gboolean done
= FALSE
;
346 for (i
= QPRO_OP_FIRST_FUNC
; i
<= QPRO_OP_LAST_FUNC
; i
++) {
347 char const *name
= qpro_functions
[i
- QPRO_OP_FIRST_FUNC
].name
;
348 int args
= qpro_functions
[i
- QPRO_OP_FIRST_FUNC
].args
;
352 if (!name
|| args
!= ARGS_UNKNOWN
)
355 f
= gnm_func_lookup (name
, NULL
);
357 g_warning ("%s is not known.", name
);
361 gnm_func_count_args (f
, &dummy
, &dummy
);
363 g_warning ("Function %s has args %s.",
364 name
, f
->arg_names
? f
->arg_names
: "?");
371 static const GnmExpr
*
372 expr_stack_pop (GSList
**pstack
)
377 g_return_val_if_fail (pstack
!= NULL
, NULL
);
379 expr
= (*pstack
)->data
;
380 next
= (*pstack
)->next
;
382 g_slist_free_1 (*pstack
);
389 qpro_parse_formula (QProReadState
*state
, int col
, int row
,
390 guint8
const *data
, guint8
const *end
)
392 guint16 magic
, ref_offset
;
394 int flags
= GSF_LE_GET_GUINT16 (data
+ 8);
395 int length
= GSF_LE_GET_GUINT16 (data
+ 10);
398 GSList
*stack
= NULL
;
399 GnmExprTop
const *texpr
= NULL
;
400 guint8
const *refs
, *fmla
;
403 dump_missing_functions ();
406 Q_CHECK_CONDITION (end
- data
>= 14);
407 magic
= GSF_LE_GET_GUINT16 (data
+ 6) & 0x7ff8;
408 ref_offset
= GSF_LE_GET_GUINT16 (data
+ 12);
411 refs
= fmla
+ ref_offset
;
412 Q_CHECK_CONDITION (refs
<= end
);
415 puts (cell_coord_name (col
, row
));
416 gsf_mem_dump (data
, 14);
417 gsf_mem_dump (fmla
, refs
-fmla
);
418 gsf_mem_dump (refs
, end
-refs
);
421 while (fmla
< refs
&& *fmla
!= QPRO_OP_EOF
) {
422 QProOperators op
= *fmla
++;
423 GnmExpr
const *expr
= NULL
;
425 g_print ("Operator %d.\n", op
);
428 case QPRO_OP_CONST_FLOAT
:
429 Q_CHECK_CONDITION (refs
- fmla
>= 8);
430 expr
= gnm_expr_new_constant (value_new_float (
431 gsf_le_get_double (fmla
)));
435 case QPRO_OP_CELLREF
: {
439 Q_CHECK_CONDITION (end
- refs
>= 6);
440 tmp
= GSF_LE_GET_GUINT16 (refs
+ 4);
442 ref
.col
= *((gint8
*)(refs
+ 2));
443 ref
.col_relative
= (tmp
& 0x4000) ? TRUE
: FALSE
;
444 ref
.row_relative
= (tmp
& 0x2000) ? TRUE
: FALSE
;
445 if (ref
.row_relative
)
446 ref
.row
= (int)(((gint16
)((tmp
& 0x1fff) << 3)) >> 3);
448 ref
.row
= tmp
& 0x1fff;
449 expr
= gnm_expr_new_cellref (&ref
);
454 case QPRO_OP_RANGEREF
: {
458 Q_CHECK_CONDITION (end
- refs
>= 10);
460 tmp
= GSF_LE_GET_GUINT16 (refs
+ 4);
462 a
.col
= *((gint8
*)(refs
+ 2));
463 a
.col_relative
= (tmp
& 0x4000) ? TRUE
: FALSE
;
464 a
.row_relative
= (tmp
& 0x2000) ? TRUE
: FALSE
;
466 a
.row
= (int)(((gint16
)((tmp
& 0x1fff) << 3)) >> 3);
468 a
.row
= tmp
& 0x1fff;
470 tmp
= GSF_LE_GET_GUINT16 (refs
+ 8);
472 b
.col
= *((gint8
*)(refs
+ 6));
473 b
.col_relative
= (tmp
& 0x4000) ? TRUE
: FALSE
;
474 b
.row_relative
= (tmp
& 0x2000) ? TRUE
: FALSE
;
476 b
.row
= (int)(((gint16
)((tmp
& 0x1fff) << 3)) >> 3);
478 b
.row
= tmp
& 0x1fff;
480 expr
= gnm_expr_new_constant (
481 value_new_cellrange_unsafe (&a
, &b
));
489 break; /* Currently just ignore. */
491 case QPRO_OP_CONST_INT
:
492 Q_CHECK_CONDITION (refs
- fmla
>= 2);
493 expr
= gnm_expr_new_constant (
494 value_new_int ((gint16
)GSF_LE_GET_GUINT16 (fmla
)));
498 case QPRO_OP_CONST_STR
:
499 expr
= gnm_expr_new_constant (qpro_new_string (state
, fmla
));
500 fmla
+= strlen (fmla
) + 1;
503 case QPRO_OP_DEFAULT_ARG
:
504 expr
= gnm_expr_new_constant (value_new_empty ());
507 case QPRO_OP_ADD
: case QPRO_OP_SUB
:
508 case QPRO_OP_MULT
: case QPRO_OP_DIV
:
510 case QPRO_OP_EQ
: case QPRO_OP_NE
:
511 case QPRO_OP_LE
: case QPRO_OP_GE
:
512 case QPRO_OP_LT
: case QPRO_OP_GT
:
513 case QPRO_OP_CONCAT
: Q_CHECK_CONDITION (stack
&& stack
->next
); {
514 static GnmExprOp
const binop_map
[] = {
515 GNM_EXPR_OP_ADD
, GNM_EXPR_OP_SUB
,
516 GNM_EXPR_OP_MULT
, GNM_EXPR_OP_DIV
,
518 GNM_EXPR_OP_EQUAL
, GNM_EXPR_OP_NOT_EQUAL
,
519 GNM_EXPR_OP_LTE
, GNM_EXPR_OP_GTE
,
520 GNM_EXPR_OP_LT
, GNM_EXPR_OP_GT
,
524 GnmExpr
const *r
= expr_stack_pop (&stack
);
525 GnmExpr
const *l
= expr_stack_pop (&stack
);
526 expr
= gnm_expr_new_binary (
527 l
, binop_map
[op
- QPRO_OP_ADD
], r
);
532 case QPRO_OP_OR
: Q_CHECK_CONDITION (stack
&& stack
->next
); {
533 GnmFunc
*f
= gnm_func_lookup (op
== QPRO_OP_OR
? "or" : "and",
535 GnmExpr
const *r
= expr_stack_pop (&stack
);
536 GnmExpr
const *l
= expr_stack_pop (&stack
);
537 expr
= gnm_expr_new_funcall2 (f
, l
, r
);
541 case QPRO_OP_NOT
: Q_CHECK_CONDITION (stack
); {
542 GnmFunc
*f
= gnm_func_lookup ("NOT", NULL
);
543 GnmExpr
const *a
= expr_stack_pop (&stack
);
544 expr
= gnm_expr_new_funcall1 (f
, a
);
548 case QPRO_OP_UNARY_NEG
:
549 case QPRO_OP_UNARY_PLUS
:
550 Q_CHECK_CONDITION (stack
);
551 expr
= expr_stack_pop (&stack
);
552 expr
= gnm_expr_new_unary ((op
== QPRO_OP_UNARY_NEG
)
553 ? GNM_EXPR_OP_UNARY_NEG
554 : GNM_EXPR_OP_UNARY_PLUS
,
559 if (QPRO_OP_FIRST_FUNC
<= op
&& op
<= QPRO_OP_LAST_FUNC
) {
560 int idx
= op
- QPRO_OP_FIRST_FUNC
;
561 char const *name
= qpro_functions
[idx
].name
;
562 int args
= qpro_functions
[idx
].args
;
563 GnmExprList
*arglist
= NULL
;
567 g_warning ("QPRO function %d is not known.", op
);
570 /* FIXME : Add support for workbook local functions */
571 f
= gnm_func_lookup (name
, NULL
);
573 g_warning ("QPRO function %s is not supported!",
578 if (args
== ARGS_UNKNOWN
) {
579 g_warning ("QPRO function %s is not supported.",
584 if (args
== ARGS_COUNT_FOLLOWS
) {
585 Q_CHECK_CONDITION (refs
- fmla
>= 1);
590 while (stack
&& args
> 0) {
591 arglist
= g_slist_prepend (arglist
,
592 (gpointer
)expr_stack_pop (&stack
));
596 g_printerr ("File is probably corrupted.\n"
597 "(Expression stack is short by %d arguments)",
600 GnmExpr
const *e
= gnm_expr_new_constant (value_new_empty ());
601 arglist
= g_slist_prepend (arglist
,
606 expr
= gnm_expr_new_funcall (f
, arglist
);
610 g_printerr ("Operator %d encountered.\n", op
);
614 stack
= g_slist_prepend (stack
, (gpointer
)expr
);
617 Q_CHECK_CONDITION (fmla
!= refs
);
618 Q_CHECK_CONDITION (stack
!= NULL
);
619 Q_CHECK_CONDITION (stack
->next
== NULL
);
621 texpr
= gnm_expr_top_new (expr_stack_pop (&stack
));
625 val
= value_new_error_VALUE (NULL
);
629 int new_row
, new_col
;
632 data
= qpro_get_record (state
, &id
, &len
);
633 Q_CHECK_CONDITION (data
!= NULL
);
635 if (id
== QPRO_UNDOCUMENTED_270
) {
637 * poker.wk3 has a few of these. They seem to be
638 * more or less embedding a copy of the formula
642 g_warning ("Encountered 270 record.");
647 Q_CHECK_CONDITION (id
== QPRO_FORMULA_STRING
);
648 Q_CHECK_CONDITION (len
>= 7);
651 new_row
= GSF_LE_GET_GUINT16 (data
+ 2);
654 Q_CHECK_CONDITION (col
== new_col
&& row
== new_row
);
656 val
= qpro_new_string (state
, data
+ 7);
660 val
= value_new_float (gsf_le_get_double (data
));
663 gnm_cell_set_expr_and_value
664 (sheet_cell_fetch (state
->cur_sheet
, col
, row
),
666 gnm_expr_top_unref (texpr
);
673 for (tmp
= stack
; tmp
; tmp
= tmp
->next
) {
674 GnmExpr
*expr
= tmp
->data
;
675 #ifdef DEBUG_EXPR_STACK
680 pp
.sheet
= state
->cur_sheet
;
684 p
= gnm_expr_as_string (expr
, &pp
,
685 gnm_conventions_default
);
686 g_printerr ("Expr: %s\n", p
);
689 gnm_expr_free (expr
);
691 g_slist_free (stack
);
695 gnm_expr_top_unref (texpr
);
699 qpro_get_style (QProReadState
*state
, guint8
const *data
)
702 unsigned attr_id
= GSF_LE_GET_GUINT16 (data
) >> 3;
703 g_printerr ("Get Attr %u\n", attr_id
);
705 return sheet_style_default (state
->cur_sheet
);
709 qpro_read_sheet (QProReadState
*state
)
714 /* We can use col_name as a quick proxy for the defaul q-pro sheet names */
715 char const *def_name
= col_name (workbook_sheet_count (state
->wb
));
716 Sheet
*sheet
= sheet_new (state
->wb
, def_name
, 256, 65536);
718 state
->cur_sheet
= sheet
;
719 workbook_sheet_attach (state
->wb
, sheet
);
720 sheet_flag_recompute_spans (sheet
);
722 g_printerr ("----------> start %s\n", def_name
);
724 while (NULL
!= (data
= qpro_get_record (state
, &id
, &len
))) {
726 case QPRO_BLANK_CELL
:
727 if (validate (QPRO_BLANK_CELL
, 6))
728 sheet_style_set_pos (sheet
,
729 data
[0], GSF_LE_GET_GUINT16 (data
+ 2),
730 qpro_get_style (state
, data
+ 4));
733 case QPRO_INTEGER_CELL
:
734 if (validate (QPRO_INTEGER_CELL
, 8)) {
736 int row
= GSF_LE_GET_GUINT16 (data
+ 2);
737 sheet_style_set_pos (sheet
, col
, row
,
738 qpro_get_style (state
, data
+ 4));
739 gnm_cell_assign_value (sheet_cell_fetch (sheet
, col
, row
),
740 value_new_int (GSF_LE_GET_GUINT16 (data
+ 6)));
744 case QPRO_FLOATING_POINT_CELL
:
745 if (validate (QPRO_FLOATING_POINT_CELL
, 14)) {
747 int row
= GSF_LE_GET_GUINT16 (data
+ 2);
748 sheet_style_set_pos (sheet
, col
, row
,
749 qpro_get_style (state
, data
+ 4));
750 gnm_cell_assign_value (sheet_cell_fetch (sheet
, col
, row
),
751 value_new_float (gsf_le_get_double (data
+ 6)));
755 case QPRO_LABEL_CELL
:
756 if (validate (QPRO_LABEL_CELL
, -1)) {
758 int row
= GSF_LE_GET_GUINT16 (data
+ 2);
759 GnmHAlign align
= GNM_HALIGN_GENERAL
;
760 GnmStyle
*as
= qpro_get_style (state
, data
+ 4);
761 GnmHAlign asa
= gnm_style_is_element_set (as
, MSTYLE_ALIGN_H
)
762 ? gnm_style_get_align_h (as
)
763 : GNM_HALIGN_GENERAL
;
764 if (asa
== GNM_HALIGN_GENERAL
)
765 asa
= GNM_HALIGN_LEFT
;
767 sheet_style_set_pos (sheet
, col
, row
, as
);
769 case '\'': align
= GNM_HALIGN_LEFT
; break;
770 case '^': align
= GNM_HALIGN_CENTER
; break;
771 case '"': align
= GNM_HALIGN_RIGHT
; break;
772 case '\\': break; /* Repeat */
773 case '|': break; /* Page break */
774 case 0: break; /* Nothing */
776 g_printerr ("Ignoring unknown alignment\n");
778 if (align
!= GNM_HALIGN_GENERAL
&& align
!= asa
) {
779 GnmStyle
*s
= gnm_style_new ();
780 gnm_style_set_align_h (s
, align
);
781 sheet_style_apply_pos (sheet
, col
, row
,
785 gnm_cell_assign_value (sheet_cell_fetch (sheet
, col
, row
),
786 qpro_new_string (state
, data
+ 7));
790 case QPRO_FORMULA_CELL
:
791 if (validate (QPRO_FORMULA_CELL
, -1)) {
793 int row
= GSF_LE_GET_GUINT16 (data
+ 2);
794 sheet_style_set_pos (sheet
, col
, row
,
795 qpro_get_style (state
, data
+ 4));
797 qpro_parse_formula (state
, col
, row
,
798 data
+ 6, data
+ len
);
802 case QPRO_END_OF_PAGE
:
805 case QPRO_COLUMN_SIZE
:
806 /* ignore this, we auto generate this info */
809 case QPRO_PROTECTION
:
810 if (validate (QPRO_PROTECTION
, 1))
812 "protected", (data
[0] == 0xff),
817 if (validate (QPRO_PAGE_NAME
, -1)) {
819 g_convert_with_iconv (data
, -1,
822 #warning "This is wrong, but the workbook interface is confused and needs a control."
823 g_object_set (sheet
, "name", utf8name
, NULL
);
828 case QPRO_PAGE_ATTRIBUTE
:
829 /* Documented at 30. Observed at 34. */
830 if (validate (QPRO_PAGE_ATTRIBUTE
, -1)) {
831 #warning TODO, mostly simple
835 case QPRO_DEFAULT_ROW_HEIGHT_PANE1
:
836 case QPRO_DEFAULT_ROW_HEIGHT_PANE2
:
837 if (validate (QPRO_DEFAULT_ROW_HEIGHT
, 2)) {
841 case QPRO_DEFAULT_COL_WIDTH_PANE1
:
842 case QPRO_DEFAULT_COL_WIDTH_PANE2
:
843 if (validate (QPRO_DEFAULT_COL_WIDTH
, 2)) {
847 case QPRO_MAX_FONT_PANE1
:
848 case QPRO_MAX_FONT_PANE2
:
849 /* just ignore for now */
852 case QPRO_PAGE_TAB_COLOR
:
853 if (validate (QPRO_PAGE_TAB_COLOR
, 4)) {
854 GnmColor
*bc
= gnm_color_new_rgb8 (
855 data
[0], data
[1], data
[2]);
857 "tab-background", bc
,
859 style_color_unref (bc
);
863 case QPRO_PAGE_ZOOM_FACTOR
:
864 if (validate (QPRO_PAGE_ZOOM_FACTOR
, 4)) {
865 guint16 low
= GSF_LE_GET_GUINT16 (data
);
866 guint16 high
= GSF_LE_GET_GUINT16 (data
+ 2);
869 if (high
< 10 || high
> 400)
870 go_io_warning (state
->io_context
,
871 _("Invalid zoom %hd %%"), high
);
873 g_object_set (sheet
, "zoom-factor", high
/ 100.0, NULL
);
879 if (id
== QPRO_END_OF_PAGE
)
883 g_printerr ("----------< end\n");
885 state
->cur_sheet
= NULL
;
889 qpro_read_workbook (QProReadState
*state
, GsfInput
*input
)
894 state
->input
= input
;
895 gsf_input_seek (input
, 0, G_SEEK_SET
);
897 while (NULL
!= (data
= qpro_get_record (state
, &id
, &len
))) {
899 case QPRO_BEGINNING_OF_FILE
:
900 if (validate (QPRO_BEGINNING_OF_FILE
, 2)) {
902 version
= GSF_LE_GET_GUINT16 (data
);
906 case QPRO_BEGINNING_OF_PAGE
:
907 qpro_read_sheet (state
);
911 if (id
> QPRO_LAST_SANE_ID
)
912 go_io_warning (state
->io_context
,
913 _("Invalid record %d of length %hd"),
916 if (id
== QPRO_END_OF_FILE
)
922 qpro_file_open (GOFileOpener
const *fo
, GOIOContext
*context
,
923 WorkbookView
*new_wb_view
, GsfInput
*input
)
926 GsfInput
*stream
= NULL
;
929 state
.io_context
= context
;
930 state
.wbv
= new_wb_view
;
931 state
.wb
= wb_view_get_workbook (new_wb_view
);
932 state
.cur_sheet
= NULL
;
933 state
.converter
= g_iconv_open ("UTF-8", "ISO-8859-1");
934 state
.corrupted
= FALSE
;
936 /* check for >= QPro 6.0 which is OLE based */
937 ole
= gsf_infile_msole_new (input
, NULL
);
939 stream
= gsf_infile_child_by_name (GSF_INFILE (ole
),
940 "PerfectOffice_MAIN");
941 if (stream
!= NULL
) {
942 qpro_read_workbook (&state
, stream
);
943 g_object_unref (stream
);
945 go_io_warning (context
,
946 _("Unable to find the PerfectOffice_MAIN stream. Is this really a Quattro Pro file?"));
947 g_object_unref (ole
);
949 qpro_read_workbook (&state
, input
);
951 gsf_iconv_close (state
.converter
);