1 /*******************************************************************************
2 * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com> *
3 * All rights reserved. *
4 * *
5 * This file is a part of SC-IM *
6 * *
7 * SC-IM is a spreadsheet program that is based on SC. The original authors *
8 * of SC are James Gosling and Mark Weiser, and mods were later added by *
9 * Chuck Martin. *
10 * *
11 * Redistribution and use in source and binary forms, with or without *
12 * modification, are permitted provided that the following conditions are met: *
13 * 1. Redistributions of source code must retain the above copyright *
14 * notice, this list of conditions and the following disclaimer. *
15 * 2. Redistributions in binary form must reproduce the above copyright *
16 * notice, this list of conditions and the following disclaimer in the *
17 * documentation and/or other materials provided with the distribution. *
18 * 3. All advertising materials mentioning features or use of this software *
19 * must display the following acknowledgement: *
20 * This product includes software developed by Andrés Martinelli *
21 * <andmarti@gmail.com>. *
22 * 4. Neither the name of the Andrés Martinelli nor the *
23 * names of other contributors may be used to endorse or promote products *
24 * derived from this software without specific prior written permission. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY *
27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
29 * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY *
30 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
36 *******************************************************************************/
37
38 /**
39 * \file xlsx.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 2017-07-18
42 * \brief TODO Write a tbrief file description.
43 *
44 * \details xlsx import requires:
45 * - libzip-dev
46 * - libxml2-dev
47 *
48 * \details xlsx export requires
49 * - libxlsxwriter
50 */
51
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
55 #include <ctype.h> // for isdigit
56 #include <stdlib.h> // for atoi
57 #include "macros.h"
58 #include "sc.h"
59 #include "cmds.h"
60 #include "tui.h"
61 #include "conf.h"
62 #include "lex.h"
63 #include "utils/string.h"
64
65 #ifdef XLSX
66 #include <zip.h>
67 #include <libxml/parser.h>
68 #include <libxml/tree.h>
69 #include "xlsx.h"
70
71 /**
72 * \brief TODO Document get_xlsx_string()
73 *
74 * \details This function takes the DOM of the sharedStrings file
75 * and based on position, it returns the according string. Note
76 * that 0 is the first string.
77 *
78 * \param[in] doc
79 * \param[in] pos
80 *
81 * \return none
82 */
83
get_xlsx_string(xmlDocPtr doc,int pos)84 char * get_xlsx_string(xmlDocPtr doc, int pos) {
85 xmlNode * cur_node = xmlDocGetRootElement(doc)->xmlChildrenNode;
86 xmlNode * father;
87 char * result = NULL;
88
89 while (pos--) cur_node = cur_node->next;
90
91 father = cur_node;
92 cur_node = father->xmlChildrenNode;
93
94 while (father != NULL) { // traverse children
95 while (cur_node != NULL) { // traverse relatives
96 if ( ! xmlStrcmp(cur_node->name, (const xmlChar *) "t")
97 && cur_node->xmlChildrenNode != NULL
98 && cur_node->xmlChildrenNode->content != NULL
99 ) {
100 result = (char *) cur_node->xmlChildrenNode->content;
101 //sc_debug("%s %s", cur_node->name, result);
102 return result;
103 }
104 cur_node = cur_node->next;
105 }
106
107 father = father->xmlChildrenNode;
108 if (father != NULL) cur_node = father->xmlChildrenNode;
109 }
110
111 return result;
112 }
113
114 /*
115 * this functions takes the DOM of the styles file
116 * and based on a position, it returns the according numFmtId
117 * IMPORTANT: note that 0 is the first "xf".
118 */
119 /**
120 * \brief TODO Document get_xlsx_styles
121 *
122 * \details This function takes the DOM of the styles file
123 * and mased on position, it returns the according numFmtId.
124 * IMPORTANT: Note that 0 is the first "xf".
125 *
126 * \param[in] doc_styles
127 * \param[in] pos
128 *
129 * \return none
130 */
131
get_xlsx_styles(xmlDocPtr doc_styles,int pos)132 char * get_xlsx_styles(xmlDocPtr doc_styles, int pos) {
133 // we go forward up to styles data
134 xmlNode * cur_node = xmlDocGetRootElement(doc_styles)->xmlChildrenNode;
135 while (cur_node != NULL && !(cur_node->type == XML_ELEMENT_NODE && !strcmp((char *) cur_node->name, "cellXfs")))
136 cur_node = cur_node->next;
137
138 cur_node = cur_node->xmlChildrenNode;
139 // we go forward up to desidered numFmtId
140 while (pos--) cur_node = cur_node->next;
141 char * id = (char *) xmlGetProp(cur_node, (xmlChar *) "numFmtId");
142 return id;
143 }
144
145 /**
146 * \brief TODO Document get_xlsx_number_format_by_id()
147 *
148 * \param[in] doc_styles
149 * \param[in] id
150 *
151 * \return none
152 */
153
get_xlsx_number_format_by_id(xmlDocPtr doc_styles,int id)154 char * get_xlsx_number_format_by_id(xmlDocPtr doc_styles, int id) {
155 if (doc_styles == NULL || !((id >= 165 && id <= 180) || id == 100))
156 return NULL;
157
158 // we go forward up to numFmts section
159 xmlNode * cur_node = xmlDocGetRootElement(doc_styles)->xmlChildrenNode;
160 while (cur_node != NULL && !(cur_node->type == XML_ELEMENT_NODE && !strcmp((char *) cur_node->name, "numFmts")))
161 cur_node = cur_node->next;
162
163 cur_node = cur_node->xmlChildrenNode;
164 // we go forward up to desidered format
165 char * idFile = (char *) xmlGetProp(cur_node, (xmlChar *) "numFmtId");
166 while (atoi(idFile) != id) {
167 cur_node = cur_node->next;
168 free(idFile);
169 idFile = (char *) xmlGetProp(cur_node, (xmlChar *) "numFmtId");
170 }
171
172 if (atoi(idFile) == id) {
173 free(idFile);
174 return (char *) xmlGetProp(cur_node, (xmlChar *) "formatCode");
175 } else {
176 free(idFile);
177 return NULL;
178 }
179 }
180
181 /**
182 * \brief TODO Document get_sheet_data()
183 *
184 * \details This function takes the sheetfile DOM and builds the tbl
185 * spreadsheet (SC-IM format)
186 *
187 * \return none
188 */
189
get_sheet_data(xmlDocPtr doc,xmlDocPtr doc_strings,xmlDocPtr doc_styles)190 void get_sheet_data(xmlDocPtr doc, xmlDocPtr doc_strings, xmlDocPtr doc_styles) {
191 xmlNode * cur_node = xmlDocGetRootElement(doc)->xmlChildrenNode;
192 xmlNode * child_node = NULL;
193 wchar_t line_interp[FBUFLEN] = L"";
194 int r, c;
195
196 // we go forward up to sheet data
197 while (cur_node != NULL && !(cur_node->type == XML_ELEMENT_NODE && !strcmp((char *) cur_node->name, "sheetData")))
198 cur_node = cur_node->next;
199
200 cur_node = cur_node->xmlChildrenNode; // this is sheetdata
201 while (cur_node != NULL) {
202 child_node = cur_node->xmlChildrenNode; // this are rows
203 while (child_node != NULL) { // this are cols
204
205 // We get r y c
206 char * row = (char *) xmlGetProp(cur_node, (xmlChar *) "r");
207 r = atoi(row);
208 char * col = (char *) xmlGetProp(child_node, (xmlChar *) "r");
209 while (isdigit(col[strlen(col)-1])) col[strlen(col)-1]='\0';
210 c = atocol(col, strlen(col));
211
212 char * s = (char *) xmlGetProp(child_node, (xmlChar *) "t"); // type
213 char * style = NULL;
214 style = (char *) xmlGetProp(child_node, (xmlChar *) "s"); // style
215 char * fmtId = style == NULL ? NULL : get_xlsx_styles(doc_styles, atoi(style)); // numfmtId by style number
216 char * numberFmt = NULL;
217 char * shared = NULL;
218 if (fmtId != NULL && atoi(fmtId) != 0) {
219 numberFmt = get_xlsx_number_format_by_id(doc_styles, atoi(fmtId));
220 }
221
222 // string
223 if ( s != NULL && ! strcmp(s, "s") ) {
224 char * st = NULL;
225 char * strvalue = NULL;
226 if (child_node->xmlChildrenNode != NULL)
227 strvalue = get_xlsx_string(doc_strings, atoi((char *) child_node-> xmlChildrenNode-> xmlChildrenNode->content));
228 if (strvalue != NULL && strvalue[0] != '\0') {
229 st = str_replace (strvalue, "\"", "''");
230 clean_carrier(st); // we handle padding
231 swprintf(line_interp, FBUFLEN, L"label %s%d=\"%s\"", coltoa(c), r, st);
232 send_to_interp(line_interp);
233 free(st);
234 }
235
236 // inlinestring
237 } else if ( s != NULL && ! strcmp(s, "inlineStr") ) {
238 char * st = NULL;
239 char * strvalue = (char *) child_node->xmlChildrenNode->xmlChildrenNode->xmlChildrenNode->content;
240 if (strvalue != NULL && strvalue[0] != '\0') {
241 st = str_replace (strvalue, "\"", "''");
242 clean_carrier(st); // we handle padding
243 swprintf(line_interp, FBUFLEN, L"label %s%d=\"%s\"", coltoa(c), r, st);
244 send_to_interp(line_interp);
245 free(st);
246 }
247
248 // numbers (can be dates, results from formulas or simple numbers)
249 } else {
250 // date value in v
251 if (fmtId != NULL && child_node->xmlChildrenNode != NULL &&
252 ! strcmp((char *) child_node->xmlChildrenNode->name, "v")
253 && (
254 (atoi(fmtId) >= 14 && atoi(fmtId) <= 17) ||
255 atoi(fmtId) == 278 || atoi(fmtId) == 185 ||
256 atoi(fmtId) == 196 || atoi(fmtId) == 164 ||
257 atoi(fmtId) == 217 || atoi(fmtId) == 326 ||
258 (((atoi(fmtId) >= 165 && atoi(fmtId) <= 180) ||
259 atoi(fmtId) == 100) && numberFmt != NULL // 100,165-180 are user defined formats!!
260 && str_in_str(numberFmt, "/") != -1)
261 )) {
262 long l = strtol((char *) child_node->xmlChildrenNode->xmlChildrenNode->content, (char **) NULL, 10);
263
264 swprintf(line_interp, FBUFLEN, L"let %s%d=%.15ld", coltoa(c), r, (l - 25568) * 86400 - get_conf_int("tm_gmtoff"));
265 send_to_interp(line_interp);
266 struct ent * n = lookat(r, c);
267 n->format = 0;
268 char * stringFormat = scxmalloc((unsigned)(strlen("%d/%m/%Y") + 2));
269 sprintf(stringFormat, "%c", 'd');
270 strcat(stringFormat, "%d/%m/%Y");
271 n->format = stringFormat;
272
273 // time value in v
274 } else if (fmtId != NULL && child_node->xmlChildrenNode != NULL &&
275 ! strcmp((char *) child_node->xmlChildrenNode->name, "v")
276 && (
277 (atoi(fmtId) >= 18 && atoi(fmtId) <= 21)
278 )) {
279 double l = atof((char *) child_node->xmlChildrenNode->xmlChildrenNode->content);
280 swprintf(line_interp, FBUFLEN, L"let %s%d=%.15f", coltoa(c), r, (l - get_conf_int("tm_gmtoff") * 1.0 / 60 / 60 / 24) * 86400);
281 send_to_interp(line_interp);
282 struct ent * n = lookat(r, c);
283 n->format = 0;
284 char * stringFormat = scxmalloc((unsigned)(strlen("%H:%M:%S") + 2));
285 sprintf(stringFormat, "%c", 'd');
286 strcat(stringFormat, "%H:%M:%S");
287 n->format = stringFormat;
288
289 // v - straight int value
290 } else if (//fmtId != NULL &&
291 child_node->xmlChildrenNode != NULL &&
292 ! strcmp((char *) child_node->xmlChildrenNode->name, "v") ){
293 double l = atof((char *) child_node->xmlChildrenNode->xmlChildrenNode->content);
294 swprintf(line_interp, FBUFLEN, L"let %s%d=%.15f", coltoa(c), r, l);
295 send_to_interp(line_interp);
296
297 // f - numeric value that is a result from formula
298 } else if (//fmtId != NULL &&
299 child_node->xmlChildrenNode != NULL && ! strcmp((char *) child_node->xmlChildrenNode->name, "f")) {
300
301 // handle the formula if that is whats desidered!!
302 if (get_conf_int("xlsx_readformulas") &&
303 // dont handle shared formulas right now
304 ! (xmlHasProp(child_node->xmlChildrenNode, (xmlChar *) "t") &&
305 ! strcmp((shared = (char *) xmlGetProp(child_node->xmlChildrenNode, (xmlChar *) "t")), "shared"))
306 ) {
307 char * formula = (char *) child_node->xmlChildrenNode->xmlChildrenNode->content;
308 char * strf;
309
310 // we take some excel common function and adds a @ to them
311 // we replace count sum avg with @count, @sum, @prod, @avg, @min, @max
312 strf = str_replace (formula, "COUNT","@COUNT");
313 strcpy(formula, strf);
314 free(strf);
315 strf = str_replace (formula, "SUM","@SUM");
316 strcpy(formula, strf);
317 free(strf);
318 strf = str_replace (formula, "PRODUCT","@PROD");
319 strcpy(formula, strf);
320 free(strf);
321 strf = str_replace (formula, "AVERAGE","@AVG");
322 strcpy(formula, strf);
323 free(strf);
324 strf = str_replace (formula, "MIN","@MIN");
325 strcpy(formula, strf);
326 free(strf);
327 strf = str_replace (formula, "MAX","@MAX");
328 strcpy(formula, strf);
329 free(strf);
330 strf = str_replace (formula, "ABS","@ABS");
331 strcpy(formula, strf);
332 free(strf);
333 strf = str_replace (formula, "STDEV","@STDDEV");
334 strcpy(formula, strf);
335 free(strf);
336
337 // we send the formula to the interpreter and hope to resolve it!
338 swprintf(line_interp, FBUFLEN, L"let %s%d=%s", coltoa(c), r, formula);
339
340 } else {
341 double l = atof((char *) child_node->last->xmlChildrenNode->content);
342 swprintf(line_interp, FBUFLEN, L"let %s%d=%.15f", coltoa(c), r, l);
343 }
344 send_to_interp(line_interp);
345 }
346 }
347
348 xmlFree(s);
349 xmlFree(fmtId);
350 xmlFree(style);
351 xmlFree(numberFmt);
352 xmlFree(shared);
353
354 child_node = child_node->next;
355 xmlFree(col);
356 xmlFree(row);
357 }
358 cur_node = cur_node->next;
359 }
360 return;
361 }
362
363 /**
364 * \brief TODO Document open_xlsx()
365 *
366 * \param[in] fname
367 * \param[in] encoding
368 *
369 * \return none
370 */
371
open_xlsx(char * fname,char * encoding)372 int open_xlsx(char * fname, char * encoding) {
373 struct zip * za;
374 struct zip_file * zf;
375 struct zip_stat sb, sb_strings, sb_styles, sh_strings;
376 char buf[100];
377 int err;
378 int len;
379
380 // open zip file
381 if ((za = zip_open(fname, 0, &err)) == NULL) {
382 zip_error_to_str(buf, sizeof(buf), err, errno);
383 sc_error("can't open zip archive `%s': %s", fname, buf);
384 return -1;
385 }
386
387 // open xl/sharedStrings.xml
388 char * name = "xl/sharedStrings.xml";
389 zf = zip_fopen(za, name, ZIP_FL_UNCHANGED);
390 char * strings = NULL;
391 if (zf) {
392 // some files may not have strings
393 zip_stat(za, name, ZIP_FL_UNCHANGED, &sb_strings);
394 strings = (char *) malloc(sb_strings.size);
395 len = zip_fread(zf, strings, sb_strings.size);
396 if (len < 0) {
397 sc_error("cannot read file %s.\n", name);
398 free(strings);
399 return -1;
400 }
401 zip_fclose(zf);
402 }
403
404 // open xl/styles.xml
405 name = "xl/styles.xml";
406 zf = zip_fopen(za, name, ZIP_FL_UNCHANGED);
407 if ( ! zf ) {
408 sc_error("cannot open %s file.", name);
409 if (strings != NULL) free(strings);
410 return -1;
411 }
412 zip_stat(za, name, ZIP_FL_UNCHANGED, &sb_styles);
413 char * styles = (char *) malloc(sb_styles.size);
414 len = zip_fread(zf, styles, sb_styles.size);
415 if (len < 0) {
416 sc_error("cannot read file %s.", name);
417 if (strings != NULL) free(strings);
418 free(styles);
419 return -1;
420 }
421 zip_fclose(zf);
422
423
424 // find specified sheet
425 if (get_conf_value("sheet") != NULL){
426
427 //open xml file with sheet names
428 name = "xl/workbook.xml";
429 char namebuf[30];
430 int found = 0;
431 zf = zip_fopen(za, name, ZIP_FL_UNCHANGED);
432
433 if ( zf ) {
434 zip_stat(za, name, ZIP_FL_UNCHANGED, &sh_strings);
435 char * wb_strings = (char *) malloc(sh_strings.size);
436 len = zip_fread(zf, wb_strings, sh_strings.size);
437 if (len < 0) {
438 sc_error("cannot read file %s.", name);
439 free(wb_strings);
440 return -1;
441 }
442 zip_fclose(zf);
443
444 // search workbook xml for sheet with the right name
445 xmlDoc * sheet_search = xmlReadMemory(wb_strings, sh_strings.size, "noname.xml", NULL, XML_PARSE_NOBLANKS);
446 xmlNode * cur_node = xmlDocGetRootElement(sheet_search)->xmlChildrenNode;
447 while (cur_node != NULL && strcmp((char *) cur_node->name,"sheets"))
448 cur_node = cur_node->next;
449 cur_node = cur_node->xmlChildrenNode;
450
451 char * sheet_name = NULL;
452 while (cur_node != NULL && cur_node->next != NULL && sheet_name == NULL) {
453 sheet_name = (char *) xmlGetProp(cur_node, (xmlChar *) "name");
454 if (strcmp(sheet_name, get_conf_value("sheet"))) {
455 xmlFree(sheet_name);
456 sheet_name = NULL;
457 cur_node = cur_node->next;
458 }
459 }
460 if (sheet_name != NULL){
461 char * sheet_id = (char *) xmlGetProp(cur_node, (xmlChar *) "sheetId");
462 snprintf(namebuf,30,"xl/worksheets/sheet%s.xml", sheet_id);
463 name = namebuf;
464 found = 1;
465 xmlFree(sheet_id);
466 xmlFree(sheet_name);
467 }
468 xmlFreeDoc(sheet_search);
469 if (wb_strings != NULL) free(wb_strings);
470 }
471
472 if ( ! found ){
473 // use sheet number if sheet name does not match
474 name = get_conf_value("sheet");
475 int i = strlen(name);
476 while( --i >= 0 && isdigit(name[i]) > 0 );
477 name = i < 0 ? "sheet":"";
478 snprintf(namebuf,30,"xl/worksheets/%s%s.xml",name,get_conf_value("sheet"));
479 name = namebuf;
480 }
481 } else {
482 // select sheet1 if none specified
483 name = "xl/worksheets/sheet1.xml";
484 }
485
486 //open sheet
487 zf = zip_fopen(za, name, ZIP_FL_UNCHANGED);
488 if ( ! zf ) {
489 sc_error("cannot open %s file.", name);
490 if (strings != NULL) free(strings);
491 free(styles);
492 return -1;
493 }
494 zip_stat(za, name, ZIP_FL_UNCHANGED, &sb);
495 char * sheet = (char *) malloc(sb.size);
496 len = zip_fread(zf, sheet, sb.size);
497 if (len < 0) {
498 sc_error("cannot read file %s.", name);
499 if (strings != NULL) free(strings);
500 free(styles);
501 free(sheet);
502 return -1;
503 }
504 zip_fclose(zf);
505
506
507 // XML parse for the sheet file
508 xmlDoc * doc = NULL;
509 xmlDoc * doc_strings = NULL;
510 xmlDoc * doc_styles = NULL;
511
512 // this initialize the library and check potential ABI mismatches
513 // between the version it was compiled for and the actual shared
514 // library used.
515 LIBXML_TEST_VERSION
516
517 // parse the file and get the DOM
518 doc_strings = xmlReadMemory(strings, sb_strings.size, "noname.xml", NULL, XML_PARSE_NOBLANKS);
519 doc_styles = xmlReadMemory(styles, sb_styles.size, "noname.xml", NULL, XML_PARSE_NOBLANKS);
520 doc = xmlReadMemory(sheet, sb.size, "noname.xml", NULL, XML_PARSE_NOBLANKS);
521
522 if (doc == NULL) {
523 sc_error("error: could not parse file");
524 if (strings != NULL) free(strings);
525 free(styles);
526 free(sheet);
527 return -1;
528 }
529
530 get_sheet_data(doc, doc_strings, doc_styles);
531
532 // free the document
533 xmlFreeDoc(doc);
534 xmlFreeDoc(doc_strings);
535 xmlFreeDoc(doc_styles);
536
537 // Free the global variables that may have been allocated by the parser
538 xmlCleanupParser();
539
540 // free both sheet and strings variables
541 if (strings != NULL) free(strings);
542 free(styles);
543 free(sheet);
544
545 // close zip file
546 if (zip_close(za) == -1) {
547 sc_error("cannot close zip archive `%s'", fname);
548 return -1;
549 }
550
551 auto_justify(0, maxcols, DEFWIDTH);
552 deleterow(currow, 1);
553 return 0;
554 }
555 #endif
556
557 #ifdef XLSX_EXPORT
558 #include "xlsxwriter.h"
559 /**
560 * \brief TODO Document export_xlsx()
561 *
562 * \param[in] filename
563 * \param[in] r0
564 * \param[in] c0
565 * \param[in] rn
566 * \param[in] cn
567 *
568 * \return none
569 */
570
export_xlsx(char * filename,int r0,int c0,int rn,int cn)571 int export_xlsx(char * filename, int r0, int c0, int rn, int cn) {
572 int row, col;
573 register struct ent ** pp;
574
575 lxw_workbook * workbook = workbook_new(filename);
576 lxw_worksheet * worksheet = workbook_add_worksheet(workbook, NULL);
577
578 int bkp_currow = currow;
579 currow = 0;
580 insert_row(0); //add a row so that scim formulas apply to excel
581
582 for (row = r0; row <= rn+1; row++)
583 for (pp = ATBL(tbl, row, col = c0); col <= cn; col++, pp++)
584 if (*pp) {
585 // Check format here
586 lxw_format * format = workbook_add_format(workbook);
587
588 // handle alignment
589 if ((*pp)->label && (*pp)->flags & is_label) // center align
590 format_set_align(format, LXW_ALIGN_CENTER);
591 else if ((*pp)->label && (*pp)->flags & is_leftflush) // left align
592 format_set_align(format, LXW_ALIGN_LEFT);
593 else if ((*pp)->label) // right align
594 format_set_align(format, LXW_ALIGN_RIGHT);
595
596 // handle bold, italic and underline
597 if ((*pp)->ucolor != NULL && (*pp)->ucolor->bold)
598 format_set_bold(format);
599 else if ((*pp)->ucolor != NULL && (*pp)->ucolor->italic)
600 format_set_italic(format);
601 else if ((*pp)->ucolor != NULL && (*pp)->ucolor->underline)
602 format_set_underline(format, LXW_UNDERLINE_SINGLE);
603
604 // handle fg color
605 if ((*pp)->ucolor != NULL && (*pp)->ucolor->fg) {
606 int fgcolor;
607 switch ((*pp)->ucolor->fg) {
608 case BLACK:
609 fgcolor = LXW_COLOR_BLACK;
610 break;
611 case RED:
612 fgcolor = LXW_COLOR_RED;
613 break;
614 case GREEN:
615 fgcolor = LXW_COLOR_GREEN;
616 break;
617 case YELLOW:
618 fgcolor = LXW_COLOR_YELLOW;
619 break;
620 case BLUE:
621 fgcolor = LXW_COLOR_BLUE;
622 break;
623 case MAGENTA:
624 fgcolor = LXW_COLOR_MAGENTA;
625 break;
626 case CYAN:
627 fgcolor = LXW_COLOR_CYAN;
628 break;
629 case WHITE:
630 fgcolor = LXW_COLOR_WHITE;
631 break;
632 }
633 format_set_font_color(format, fgcolor);
634 }
635
636 // handle bg color
637 if ((*pp)->ucolor != NULL && (*pp)->ucolor->bg) {
638 int bgcolor;
639 switch ((*pp)->ucolor->bg) {
640 case BLACK:
641 bgcolor = LXW_COLOR_BLACK;
642 break;
643 case RED:
644 bgcolor = LXW_COLOR_RED;
645 break;
646 case GREEN:
647 bgcolor = LXW_COLOR_GREEN;
648 break;
649 case YELLOW:
650 bgcolor = LXW_COLOR_YELLOW;
651 break;
652 case BLUE:
653 bgcolor = LXW_COLOR_BLUE;
654 break;
655 case MAGENTA:
656 bgcolor = LXW_COLOR_MAGENTA;
657 break;
658 case CYAN:
659 bgcolor = LXW_COLOR_CYAN;
660 break;
661 case WHITE:
662 bgcolor = LXW_COLOR_WHITE;
663 break;
664 }
665 format_set_bg_color(format, bgcolor);
666 }
667
668 // dateformat
669 if ((*pp) && (*pp)->format && (*pp)->format[0] == 'd') {
670 char sc_format[BUFFERSIZE];
671 char * st = NULL;
672 strcpy(sc_format, &((*pp)->format[1]));
673
674 st = str_replace(sc_format, "%Y", "yyyy");
675 strcpy(sc_format, st);
676 free(st);
677 st = str_replace(sc_format, "%y", "yy");
678 strcpy(sc_format, st);
679 free(st);
680 st = str_replace(sc_format, "%m", "mm");
681 strcpy(sc_format, st);
682 free(st);
683 st = str_replace(sc_format, "%d", "dd");
684 strcpy(sc_format, st);
685 free(st);
686 format_set_num_format(format, sc_format);
687 worksheet_write_number(worksheet, row-1, col, (((*pp)->v + get_conf_int("tm_gmtoff")) / 86400 + 25568) , format);
688
689 // formula
690 } else if ((*pp) && (*pp)->expr && get_conf_int("xlsx_readformulas")) {
691 linelim = 0;
692 editexp((*pp)->row, (*pp)->col);
693 linelim = -1;
694
695 char * strf;
696 char formula[BUFFERSIZE];
697 strcpy(formula, line);
698
699 strf = str_replace(formula, "@count","count");
700 strcpy(formula, strf);
701 free(strf);
702
703 strf = str_replace(formula, "@sum","sum");
704 strcpy(formula, strf);
705 free(strf);
706
707 strf = str_replace(formula, "@prod","product");
708 strcpy(formula, strf);
709 free(strf);
710
711 strf = str_replace(formula, "@avg","average");
712 strcpy(formula, strf);
713 free(strf);
714
715 strf = str_replace(formula, "@min","min");
716 strcpy(formula, strf);
717 free(strf);
718
719 strf = str_replace(formula, "@max","max");
720 strcpy(formula, strf);
721 free(strf);
722
723 strf = str_replace(formula, "@abs","abs");
724 strcpy(formula, strf);
725 free(strf);
726
727 strf = str_replace(formula, "@stddev","stdev");
728 strcpy(formula, strf);
729 free(strf);
730
731 add_char(formula, '=', 0);
732 worksheet_write_formula(worksheet, row-1, col, formula, NULL);
733
734 // If a numeric value exists
735 } else if ( (*pp)->flags & is_valid) {
736 worksheet_write_number(worksheet, row-1, col, (*pp)->v, format);
737
738 } else if ((*pp)->label) {
739 worksheet_write_string(worksheet, row-1, col, (*pp)->label, format);
740 }
741 /* TODO: handle hidden rows and columns? */
742 }
743 int_deleterow(currow, 1); /* delete the added row */
744 currow = bkp_currow;
745
746 return workbook_close(workbook);
747 }
748 #endif
749