1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2011, 2012, 2013, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/message.h"
20 #include "libpspp/misc.h"
21 #include "libpspp/assertion.h"
23 #include "data/data-in.h"
25 #include "gl/c-strtod.h"
26 #include "gl/minmax.h"
29 #define _(msgid) gettext (msgid)
30 #define N_(msgid) (msgid)
32 #include "ods-reader.h"
33 #include "spreadsheet-reader.h"
38 ods_open_reader (const struct spreadsheet_read_options
*opts
,
39 struct dictionary
**dict
)
41 msg (ME
, _("Support for %s files was not compiled into this installation of PSPP"), "OpenDocument");
48 #include "libpspp/zip-reader.h"
54 #include <libxml/xmlreader.h>
57 #include "data/format.h"
58 #include "data/case.h"
59 #include "data/casereader-provider.h"
60 #include "data/dictionary.h"
61 #include "data/identifier.h"
62 #include "data/value.h"
63 #include "data/variable.h"
64 #include "libpspp/i18n.h"
65 #include "libpspp/str.h"
67 #include "gl/xalloc.h"
69 static void ods_file_casereader_destroy (struct casereader
*, void *);
70 static struct ccase
*ods_file_casereader_read (struct casereader
*, void *);
73 static const struct casereader_class ods_file_casereader_class
=
75 ods_file_casereader_read
,
76 ods_file_casereader_destroy
,
83 /* The name of the sheet (utf8 encoding) */
95 STATE_INIT
= 0, /* Initial state */
96 STATE_SPREADSHEET
, /* Found the start of the spreadsheet doc */
97 STATE_TABLE
, /* Found the sheet that we actually want */
98 STATE_ROW
, /* Found the start of the cell array */
99 STATE_CELL
, /* Found a cell */
100 STATE_CELL_CONTENT
/* Found a the text within a cell */
105 xmlTextReaderPtr xtr
;
107 enum reader_state state
;
111 xmlChar
*current_sheet_name
;
117 state_data_destroy (struct state_data
*sd
)
119 xmlFree (sd
->current_sheet_name
);
120 sd
->current_sheet_name
= NULL
;
122 xmlFreeTextReader (sd
->xtr
);
128 struct spreadsheet spreadsheet
;
129 struct zip_reader
*zreader
;
131 int target_sheet_index
;
132 xmlChar
*target_sheet_name
;
134 /* State data for the meta data */
135 struct state_data msd
;
137 /* State data for the reader */
138 struct state_data rsd
;
145 struct sheet_detail
*sheets
;
146 int n_allocated_sheets
;
148 struct caseproto
*proto
;
149 struct dictionary
*dict
;
150 struct ccase
*first_case
;
151 bool used_first_case
;
154 struct string ods_errs
;
156 struct string zip_errs
;
160 ods_unref (struct spreadsheet
*s
)
162 struct ods_reader
*r
= (struct ods_reader
*) s
;
164 if (--s
->ref_cnt
== 0)
168 state_data_destroy (&r
->msd
);
169 for (i
= 0; i
< r
->n_allocated_sheets
; ++i
)
171 xmlFree (r
->sheets
[i
].name
);
174 dict_destroy (r
->dict
);
176 zip_reader_destroy (r
->zreader
);
186 reading_target_sheet (const struct ods_reader
*r
, const struct state_data
*msd
)
188 if (r
->target_sheet_name
!= NULL
)
190 if ( 0 == xmlStrcmp (r
->target_sheet_name
, msd
->current_sheet_name
))
194 if (r
->target_sheet_index
== msd
->current_sheet
+ 1)
201 static void process_node (struct ods_reader
*or, struct state_data
*r
);
205 ods_get_sheet_name (struct spreadsheet
*s
, int n
)
207 struct ods_reader
*r
= (struct ods_reader
*) s
;
208 struct state_data
*or = &r
->msd
;
210 assert (n
< s
->n_sheets
);
213 (r
->n_allocated_sheets
<= n
)
214 || or->state
!= STATE_SPREADSHEET
217 int ret
= xmlTextReaderRead (or->xtr
);
221 process_node (r
, or);
224 return r
->sheets
[n
].name
;
228 ods_get_sheet_range (struct spreadsheet
*s
, int n
)
230 struct ods_reader
*r
= (struct ods_reader
*) s
;
231 struct state_data
*or = &r
->msd
;
233 assert (n
< s
->n_sheets
);
236 (r
->n_allocated_sheets
<= n
)
237 || (r
->sheets
[n
].stop_row
== -1)
238 || or->state
!= STATE_SPREADSHEET
241 int ret
= xmlTextReaderRead (or->xtr
);
245 process_node (r
, or);
248 return create_cell_range (
249 r
->sheets
[n
].start_col
,
250 r
->sheets
[n
].start_row
,
251 r
->sheets
[n
].stop_col
,
252 r
->sheets
[n
].stop_row
);
257 ods_file_casereader_destroy (struct casereader
*reader UNUSED
, void *r_
)
259 struct ods_reader
*r
= r_
;
263 state_data_destroy (&r
->rsd
);
265 if ( ! ds_is_empty (&r
->ods_errs
))
266 msg (ME
, "%s", ds_cstr (&r
->ods_errs
));
268 ds_destroy (&r
->ods_errs
);
270 if ( r
->first_case
&& ! r
->used_first_case
)
271 case_unref (r
->first_case
);
274 caseproto_unref (r
->proto
);
277 xmlFree (r
->target_sheet_name
);
278 r
->target_sheet_name
= NULL
;
281 ods_unref (&r
->spreadsheet
);
289 process_node (struct ods_reader
*or, struct state_data
*r
)
291 xmlChar
*name
= xmlTextReaderName (r
->xtr
);
293 name
= xmlStrdup (_xml ("--"));
296 r
->node_type
= xmlTextReaderNodeType (r
->xtr
);
301 if (0 == xmlStrcasecmp (name
, _xml("office:spreadsheet")) &&
302 XML_READER_TYPE_ELEMENT
== r
->node_type
)
304 r
->state
= STATE_SPREADSHEET
;
305 r
->current_sheet
= -1;
306 r
->current_sheet_name
= NULL
;
309 case STATE_SPREADSHEET
:
310 if (0 == xmlStrcasecmp (name
, _xml("table:table"))
312 (XML_READER_TYPE_ELEMENT
== r
->node_type
))
314 xmlFree (r
->current_sheet_name
);
315 r
->current_sheet_name
= xmlTextReaderGetAttribute (r
->xtr
, _xml ("table:name"));
319 if (r
->current_sheet
>= or->n_allocated_sheets
)
321 assert (r
->current_sheet
== or->n_allocated_sheets
);
322 or->sheets
= xrealloc (or->sheets
, sizeof (*or->sheets
) * ++or->n_allocated_sheets
);
323 or->sheets
[or->n_allocated_sheets
- 1].start_col
= -1;
324 or->sheets
[or->n_allocated_sheets
- 1].stop_col
= -1;
325 or->sheets
[or->n_allocated_sheets
- 1].start_row
= -1;
326 or->sheets
[or->n_allocated_sheets
- 1].stop_row
= -1;
327 or->sheets
[or->n_allocated_sheets
- 1].name
= CHAR_CAST (char *, xmlStrdup (r
->current_sheet_name
));
333 r
->state
= STATE_TABLE
;
335 else if (0 == xmlStrcasecmp (name
, _xml("office:spreadsheet")) &&
336 XML_READER_TYPE_ELEMENT
== r
->node_type
)
338 r
->state
= STATE_INIT
;
342 if (0 == xmlStrcasecmp (name
, _xml("table:table-row")) &&
343 (XML_READER_TYPE_ELEMENT
== r
->node_type
))
346 xmlTextReaderGetAttribute (r
->xtr
,
347 _xml ("table:number-rows-repeated"));
349 int row_span
= value
? _xmlchar_to_int (value
) : 1;
354 if (! xmlTextReaderIsEmptyElement (r
->xtr
))
355 r
->state
= STATE_ROW
;
359 else if (0 == xmlStrcasecmp (name
, _xml("table:table")) &&
360 (XML_READER_TYPE_END_ELEMENT
== r
->node_type
))
362 r
->state
= STATE_SPREADSHEET
;
366 if ( (0 == xmlStrcasecmp (name
, _xml ("table:table-cell")))
368 (XML_READER_TYPE_ELEMENT
== r
->node_type
))
371 xmlTextReaderGetAttribute (r
->xtr
,
372 _xml ("table:number-columns-repeated"));
374 r
->col_span
= value
? _xmlchar_to_int (value
) : 1;
375 r
->col
+= r
->col_span
;
377 if (! xmlTextReaderIsEmptyElement (r
->xtr
))
378 r
->state
= STATE_CELL
;
382 else if ( (0 == xmlStrcasecmp (name
, _xml ("table:table-row")))
384 (XML_READER_TYPE_END_ELEMENT
== r
->node_type
))
386 r
->state
= STATE_TABLE
;
390 if ( (0 == xmlStrcasecmp (name
, _xml("text:p")))
392 ( XML_READER_TYPE_ELEMENT
== r
->node_type
))
394 if (! xmlTextReaderIsEmptyElement (r
->xtr
))
395 r
->state
= STATE_CELL_CONTENT
;
398 ( (0 == xmlStrcasecmp (name
, _xml("table:table-cell")))
400 (XML_READER_TYPE_END_ELEMENT
== r
->node_type
)
403 r
->state
= STATE_ROW
;
406 case STATE_CELL_CONTENT
:
407 assert (r
->current_sheet
>= 0);
408 assert (r
->current_sheet
< or->n_allocated_sheets
);
410 if (or->sheets
[r
->current_sheet
].start_row
== -1)
411 or->sheets
[r
->current_sheet
].start_row
= r
->row
- 1;
414 (or->sheets
[r
->current_sheet
].start_col
== -1)
416 (or->sheets
[r
->current_sheet
].start_col
>= r
->col
- 1)
418 or->sheets
[r
->current_sheet
].start_col
= r
->col
- 1;
420 or->sheets
[r
->current_sheet
].stop_row
= r
->row
- 1;
422 if ( or->sheets
[r
->current_sheet
].stop_col
< r
->col
- 1)
423 or->sheets
[r
->current_sheet
].stop_col
= r
->col
- 1;
425 if (XML_READER_TYPE_END_ELEMENT
== r
->node_type
)
426 r
->state
= STATE_CELL
;
437 A struct containing the parameters of a cell's value
450 struct xml_value firstval
;
454 /* Determine the width that a xmv should probably have */
456 xmv_to_width (const struct xml_value
*xmv
, int fallback
)
458 int width
= SPREADSHEET_DEFAULT_WIDTH
;
460 /* Non-strings always have zero width */
461 if (xmv
->type
!= NULL
&& 0 != xmlStrcmp (xmv
->type
, _xml("string")))
468 width
= ROUND_UP (xmlStrlen (xmv
->value
),
469 SPREADSHEET_DEFAULT_WIDTH
);
471 width
= ROUND_UP (xmlStrlen (xmv
->text
),
472 SPREADSHEET_DEFAULT_WIDTH
);
478 Sets the VAR of case C, to the value corresponding to the xml data
481 convert_xml_to_value (struct ccase
*c
, const struct variable
*var
,
482 const struct xml_value
*xmv
, int col
, int row
)
484 union value
*v
= case_data_rw (c
, var
);
486 if (xmv
->value
== NULL
&& xmv
->text
== NULL
)
487 value_set_missing (v
, var_get_width (var
));
488 else if ( var_is_alpha (var
))
489 /* Use the text field, because it seems that there is no
490 value field for strings */
491 value_copy_str_rpad (v
, var_get_width (var
), xmv
->text
, ' ');
494 const struct fmt_spec
*fmt
= var_get_write_format (var
);
495 enum fmt_category fc
= fmt_get_category (fmt
->type
);
497 assert ( fc
!= FMT_CAT_STRING
);
499 if ( 0 == xmlStrcmp (xmv
->type
, _xml("float")))
501 v
->f
= c_strtod (CHAR_CAST (const char *, xmv
->value
), NULL
);
505 const char *text
= xmv
->value
?
506 CHAR_CAST (const char *, xmv
->value
) : CHAR_CAST (const char *, xmv
->text
);
508 char *m
= data_in (ss_cstr (text
), "UTF-8",
516 char buf
[FMT_STRING_LEN_MAX
+ 1];
517 char *cell
= create_cell_ref (col
, row
);
519 msg (MW
, _("Cannot convert the value in the spreadsheet cell %s to format (%s): %s"),
520 cell
, fmt_to_string (fmt
, buf
), m
);
529 /* Try to find out how many sheets there are in the "workbook" */
531 get_sheet_count (struct zip_reader
*zreader
)
533 xmlTextReaderPtr mxtr
;
534 struct zip_member
*meta
= NULL
;
535 meta
= zip_member_open (zreader
, "meta.xml");
540 mxtr
= xmlReaderForIO ((xmlInputReadCallback
) zip_member_read
,
541 (xmlInputCloseCallback
) NULL
,
542 meta
, NULL
, NULL
, 0);
544 while (1 == xmlTextReaderRead (mxtr
))
546 xmlChar
*name
= xmlTextReaderName (mxtr
);
547 if ( 0 == xmlStrcmp (name
, _xml("meta:document-statistic")))
549 xmlChar
*attr
= xmlTextReaderGetAttribute (mxtr
, _xml ("meta:table-count"));
553 int s
= _xmlchar_to_int (attr
);
554 xmlFreeTextReader (mxtr
);
564 xmlFreeTextReader (mxtr
);
569 ods_error_handler (void *ctx
, const char *mesg
,
570 UNUSED xmlParserSeverities sev
, xmlTextReaderLocatorPtr loc
)
572 struct ods_reader
*r
= ctx
;
574 msg (MW
, _("There was a problem whilst reading the %s file `%s' (near line %d): `%s'"),
576 r
->spreadsheet
.file_name
,
577 xmlTextReaderLocatorLineNumber (loc
),
582 static xmlTextReaderPtr
583 init_reader (struct ods_reader
*r
, bool report_errors
)
585 struct zip_member
*content
= zip_member_open (r
->zreader
, "content.xml");
586 xmlTextReaderPtr xtr
;
588 if ( content
== NULL
)
591 xtr
= xmlReaderForIO ((xmlInputReadCallback
) zip_member_read
,
592 (xmlInputCloseCallback
) NULL
,
594 report_errors
? 0 : (XML_PARSE_NOERROR
| XML_PARSE_NOWARNING
) );
600 r
->spreadsheet
.type
= SPREADSHEET_ODS
;
603 xmlTextReaderSetErrorHandler (xtr
, ods_error_handler
, r
);
611 ods_probe (const char *filename
, bool report_errors
)
614 struct ods_reader
*r
= xzalloc (sizeof *r
);
615 xmlTextReaderPtr xtr
;
616 struct zip_reader
*zr
;
618 ds_init_empty (&r
->zip_errs
);
620 zr
= zip_reader_create (filename
, &r
->zip_errs
);
626 msg (ME
, _("Cannot open %s as a OpenDocument file: %s"),
627 filename
, ds_cstr (&r
->zip_errs
));
629 ds_destroy (&r
->zip_errs
);
634 sheet_count
= get_sheet_count (zr
);
637 r
->spreadsheet
.ref_cnt
= 1;
639 xtr
= init_reader (r
, report_errors
);
647 r
->msd
.current_sheet
= 0;
648 r
->msd
.state
= STATE_INIT
;
651 r
->spreadsheet
.n_sheets
= sheet_count
;
652 r
->n_allocated_sheets
= 0;
655 r
->spreadsheet
.file_name
= strdup (filename
);
656 return &r
->spreadsheet
;
659 ds_destroy (&r
->zip_errs
);
660 zip_reader_destroy (r
->zreader
);
666 ods_make_reader (struct spreadsheet
*spreadsheet
,
667 const struct spreadsheet_read_options
*opts
)
670 xmlChar
*type
= NULL
;
671 unsigned long int vstart
= 0;
672 casenumber n_cases
= CASENUMBER_MAX
;
674 struct var_spec
*var_spec
= NULL
;
676 xmlTextReaderPtr xtr
;
678 struct ods_reader
*r
= (struct ods_reader
*) spreadsheet
;
679 xmlChar
*val_string
= NULL
;
682 r
->read_names
= opts
->read_names
;
683 ds_init_empty (&r
->ods_errs
);
684 ++r
->spreadsheet
.ref_cnt
;
686 xtr
= init_reader (r
, true);
693 r
->rsd
.current_sheet
= 0;
694 r
->rsd
.state
= STATE_INIT
;
696 r
->used_first_case
= false;
697 r
->first_case
= NULL
;
699 if (opts
->cell_range
)
701 if ( ! convert_cell_ref (opts
->cell_range
,
702 &r
->start_col
, &r
->start_row
,
703 &r
->stop_col
, &r
->stop_row
))
705 msg (SE
, _("Invalid cell range `%s'"),
718 r
->target_sheet_name
= xmlStrdup (BAD_CAST opts
->sheet_name
);
719 r
->target_sheet_index
= opts
->sheet_index
;
721 /* Advance to the start of the cells for the target sheet */
722 while ( ! reading_target_sheet (r
, &r
->rsd
)
723 || r
->rsd
.state
!= STATE_ROW
|| r
->rsd
.row
<= r
->start_row
)
725 if (1 != (ret
= xmlTextReaderRead (r
->rsd
.xtr
)))
728 process_node (r
, &r
->rsd
);
733 msg (MW
, _("Selected sheet or range of spreadsheet `%s' is empty."),
734 spreadsheet
->file_name
);
738 if ( opts
->read_names
)
740 while (1 == xmlTextReaderRead (r
->rsd
.xtr
))
744 process_node (r
, &r
->rsd
);
746 /* If the row is finished then stop for now */
747 if (r
->rsd
.state
== STATE_TABLE
&& r
->rsd
.row
> r
->start_row
)
750 idx
= r
->rsd
.col
- r
->start_col
-1 ;
755 if (r
->stop_col
!= -1 && idx
> r
->stop_col
- r
->start_col
)
758 if (r
->rsd
.state
== STATE_CELL_CONTENT
760 XML_READER_TYPE_TEXT
== r
->rsd
.node_type
)
762 xmlChar
*value
= xmlTextReaderValue (r
->rsd
.xtr
);
764 if ( idx
>= n_var_specs
)
766 var_spec
= xrealloc (var_spec
, sizeof (*var_spec
) * (idx
+ 1));
768 /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
769 memset (var_spec
+ n_var_specs
,
771 (idx
- n_var_specs
+ 1) * sizeof (*var_spec
));
772 n_var_specs
= idx
+ 1;
774 var_spec
[idx
].firstval
.text
= 0;
775 var_spec
[idx
].firstval
.value
= 0;
776 var_spec
[idx
].firstval
.type
= 0;
778 var_spec
[idx
].name
= strdup (CHAR_CAST (const char *, value
));
785 /* Read in the first row of data */
786 while (1 == xmlTextReaderRead (r
->rsd
.xtr
))
789 process_node (r
, &r
->rsd
);
791 if ( ! reading_target_sheet (r
, &r
->rsd
) )
794 /* If the row is finished then stop for now */
795 if (r
->rsd
.state
== STATE_TABLE
&&
796 r
->rsd
.row
> r
->start_row
+ (opts
->read_names
? 1 : 0))
799 idx
= r
->rsd
.col
- r
->start_col
- 1;
803 if (r
->stop_col
!= -1 && idx
> r
->stop_col
- r
->start_col
)
806 if ( r
->rsd
.state
== STATE_CELL
&&
807 XML_READER_TYPE_ELEMENT
== r
->rsd
.node_type
)
809 type
= xmlTextReaderGetAttribute (r
->rsd
.xtr
, _xml ("office:value-type"));
810 val_string
= xmlTextReaderGetAttribute (r
->rsd
.xtr
, _xml ("office:value"));
813 if ( r
->rsd
.state
== STATE_CELL_CONTENT
&&
814 XML_READER_TYPE_TEXT
== r
->rsd
.node_type
)
816 if (idx
>= n_var_specs
)
818 var_spec
= xrealloc (var_spec
, sizeof (*var_spec
) * (idx
+ 1));
819 memset (var_spec
+ n_var_specs
,
821 (idx
- n_var_specs
+ 1) * sizeof (*var_spec
));
823 var_spec
[idx
].name
= NULL
;
824 n_var_specs
= idx
+ 1;
827 var_spec
[idx
].firstval
.type
= type
;
828 var_spec
[idx
].firstval
.text
= xmlTextReaderValue (r
->rsd
.xtr
);
829 var_spec
[idx
].firstval
.value
= val_string
;
837 /* Create the dictionary and populate it */
838 r
->spreadsheet
.dict
= r
->dict
= dict_create (
839 CHAR_CAST (const char *, xmlTextReaderConstEncoding (r
->rsd
.xtr
)));
841 for (i
= 0; i
< n_var_specs
; ++i
)
844 struct variable
*var
= NULL
;
845 char *name
= dict_make_unique_var_name (r
->dict
, var_spec
[i
].name
, &vstart
);
846 int width
= xmv_to_width (&var_spec
[i
].firstval
, opts
->asw
);
847 dict_create_var (r
->dict
, name
, width
);
850 var
= dict_get_var (r
->dict
, i
);
852 if ( 0 == xmlStrcmp (var_spec
[i
].firstval
.type
, _xml("date")))
859 fmt
= fmt_default_for_width (width
);
861 var_set_both_formats (var
, &fmt
);
864 if ( n_var_specs
== 0 )
866 msg (MW
, _("Selected sheet or range of spreadsheet `%s' is empty."),
867 spreadsheet
->file_name
);
871 /* Create the first case, and cache it */
872 r
->proto
= caseproto_ref (dict_get_proto (r
->dict
));
873 r
->first_case
= case_create (r
->proto
);
874 case_set_missing (r
->first_case
);
876 for (i
= 0 ; i
< n_var_specs
; ++i
)
878 const struct variable
*var
= dict_get_var (r
->dict
, i
);
880 convert_xml_to_value (r
->first_case
, var
, &var_spec
[i
].firstval
,
881 r
->rsd
.col
- n_var_specs
+ i
,
885 /* Read in the first row of data */
886 while (1 == xmlTextReaderRead (r
->rsd
.xtr
))
888 process_node (r
, &r
->rsd
);
890 if (r
->rsd
.state
== STATE_ROW
)
895 for ( i
= 0 ; i
< n_var_specs
; ++i
)
897 free (var_spec
[i
].firstval
.type
);
898 free (var_spec
[i
].firstval
.value
);
899 free (var_spec
[i
].firstval
.text
);
900 free (var_spec
[i
].name
);
906 return casereader_create_sequential
910 &ods_file_casereader_class
, r
);
914 for ( i
= 0 ; i
< n_var_specs
; ++i
)
916 free (var_spec
[i
].firstval
.type
);
917 free (var_spec
[i
].firstval
.value
);
918 free (var_spec
[i
].firstval
.text
);
919 free (var_spec
[i
].name
);
924 ods_file_casereader_destroy (NULL
, r
);
930 /* Reads and returns one case from READER's file. Returns a null
931 pointer on failure. */
932 static struct ccase
*
933 ods_file_casereader_read (struct casereader
*reader UNUSED
, void *r_
)
935 struct ccase
*c
= NULL
;
936 struct ods_reader
*r
= r_
;
938 xmlChar
*val_string
= NULL
;
939 xmlChar
*type
= NULL
;
941 if (!r
->used_first_case
)
943 r
->used_first_case
= true;
944 return r
->first_case
;
948 /* Advance to the start of a row. (If there is one) */
949 while (r
->rsd
.state
!= STATE_ROW
950 && 1 == xmlTextReaderRead (r
->rsd
.xtr
)
953 process_node (r
, &r
->rsd
);
957 if ( ! reading_target_sheet (r
, &r
->rsd
)
958 || r
->rsd
.state
< STATE_TABLE
959 || (r
->stop_row
!= -1 && r
->rsd
.row
> r
->stop_row
+ 1)
965 c
= case_create (r
->proto
);
966 case_set_missing (c
);
968 while (1 == xmlTextReaderRead (r
->rsd
.xtr
))
970 process_node (r
, &r
->rsd
);
972 if ( r
->stop_row
!= -1 && r
->rsd
.row
> r
->stop_row
+ 1)
975 if (r
->rsd
.state
== STATE_CELL
&&
976 r
->rsd
.node_type
== XML_READER_TYPE_ELEMENT
)
978 type
= xmlTextReaderGetAttribute (r
->rsd
.xtr
, _xml ("office:value-type"));
979 val_string
= xmlTextReaderGetAttribute (r
->rsd
.xtr
, _xml ("office:value"));
982 if (r
->rsd
.state
== STATE_CELL_CONTENT
&&
983 r
->rsd
.node_type
== XML_READER_TYPE_TEXT
)
986 struct xml_value
*xmv
= xzalloc (sizeof *xmv
);
987 xmv
->text
= xmlTextReaderValue (r
->rsd
.xtr
);
988 xmv
->value
= val_string
;
993 for (col
= 0; col
< r
->rsd
.col_span
; ++col
)
995 const struct variable
*var
;
996 const int idx
= r
->rsd
.col
- col
- r
->start_col
- 1;
999 if (r
->stop_col
!= -1 && idx
> r
->stop_col
- r
->start_col
)
1001 if (idx
>= dict_get_var_cnt (r
->dict
))
1004 var
= dict_get_var (r
->dict
, idx
);
1005 convert_xml_to_value (c
, var
, xmv
, idx
+ r
->start_col
, r
->rsd
.row
- 1);
1008 xmlFree (xmv
->text
);
1009 xmlFree (xmv
->value
);
1010 xmlFree (xmv
->type
);
1013 if ( r
->rsd
.state
<= STATE_TABLE
)
1018 xmlFree (val_string
);