Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: XLSX Translator
4 : * Purpose: Implements OGRXLSXDataSource class
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_xlsx.h"
14 : #include "ogr_p.h"
15 : #include "cpl_conv.h"
16 : #include "cpl_time.h"
17 : #include "cpl_vsi_error.h"
18 :
19 : #include <algorithm>
20 :
21 : namespace OGRXLSX
22 : {
23 :
24 : constexpr int PARSER_BUF_SIZE = 8192;
25 :
26 : constexpr int NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 = 25569;
27 : constexpr int NUMBER_OF_SECONDS_PER_DAY = 86400;
28 :
29 : /************************************************************************/
30 : /* OGRXLSXLayer() */
31 : /************************************************************************/
32 :
33 167 : OGRXLSXLayer::OGRXLSXLayer(OGRXLSXDataSource *poDSIn, const char *pszFilename,
34 167 : const char *pszName, int bUpdatedIn)
35 167 : : OGRMemLayer(pszName, nullptr, wkbNone), bInit(CPL_TO_BOOL(bUpdatedIn)),
36 167 : poDS(poDSIn), osFilename(pszFilename), bUpdated(CPL_TO_BOOL(bUpdatedIn)),
37 334 : bHasHeaderLine(false)
38 : {
39 167 : SetAdvertizeUTF8(true);
40 167 : }
41 :
42 : /************************************************************************/
43 : /* Init() */
44 : /************************************************************************/
45 :
46 8302 : void OGRXLSXLayer::Init()
47 : {
48 8302 : if (!bInit)
49 : {
50 84 : bInit = true;
51 84 : CPLDebug("XLSX", "Init(%s)", GetName());
52 84 : poDS->BuildLayer(this);
53 : }
54 8302 : }
55 :
56 : /************************************************************************/
57 : /* Updated() */
58 : /************************************************************************/
59 :
60 1574 : void OGRXLSXLayer::SetUpdated(bool bUpdatedIn)
61 : {
62 1574 : if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
63 : {
64 12 : bUpdated = true;
65 12 : poDS->SetUpdated();
66 : }
67 1562 : else if (bUpdated && !bUpdatedIn)
68 : {
69 30 : bUpdated = false;
70 : }
71 1574 : }
72 :
73 : /************************************************************************/
74 : /* SyncToDisk() */
75 : /************************************************************************/
76 :
77 0 : OGRErr OGRXLSXLayer::SyncToDisk()
78 : {
79 0 : poDS->FlushCache(false);
80 0 : return OGRERR_NONE;
81 : }
82 :
83 : /************************************************************************/
84 : /* TranslateFIDFromMemLayer() */
85 : /************************************************************************/
86 :
87 : // Translate a FID from MEM convention (0-based) to XLSX convention
88 1679 : GIntBig OGRXLSXLayer::TranslateFIDFromMemLayer(GIntBig nFID) const
89 : {
90 1679 : return nFID + (1 + (bHasHeaderLine ? 1 : 0));
91 : }
92 :
93 : /************************************************************************/
94 : /* TranslateFIDToMemLayer() */
95 : /************************************************************************/
96 :
97 : // Translate a FID from XLSX convention to MEM convention (0-based)
98 77 : GIntBig OGRXLSXLayer::TranslateFIDToMemLayer(GIntBig nFID) const
99 : {
100 77 : if (nFID > 0)
101 59 : return nFID - (1 + (bHasHeaderLine ? 1 : 0));
102 18 : return OGRNullFID;
103 : }
104 :
105 : /************************************************************************/
106 : /* GetNextFeature() */
107 : /************************************************************************/
108 :
109 866 : OGRFeature *OGRXLSXLayer::GetNextFeature()
110 : {
111 866 : Init();
112 :
113 866 : OGRFeature *poFeature = OGRMemLayer::GetNextFeature();
114 866 : if (poFeature)
115 701 : poFeature->SetFID(TranslateFIDFromMemLayer(poFeature->GetFID()));
116 866 : return poFeature;
117 : }
118 :
119 : /************************************************************************/
120 : /* CreateField() */
121 : /************************************************************************/
122 :
123 438 : OGRErr OGRXLSXLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
124 : {
125 438 : Init();
126 :
127 438 : if (GetLayerDefn()->GetFieldCount() >= 2000)
128 : {
129 0 : CPLError(CE_Failure, CPLE_AppDefined,
130 : "Maximum number of fields supported is 2000");
131 0 : return OGRERR_FAILURE;
132 : }
133 438 : SetUpdated();
134 438 : return OGRMemLayer::CreateField(poField, bApproxOK);
135 : }
136 :
137 : /************************************************************************/
138 : /* GetFeature() */
139 : /************************************************************************/
140 :
141 50 : OGRFeature *OGRXLSXLayer::GetFeature(GIntBig nFeatureId)
142 : {
143 50 : Init();
144 :
145 : OGRFeature *poFeature =
146 50 : OGRMemLayer::GetFeature(TranslateFIDToMemLayer(nFeatureId));
147 50 : if (poFeature)
148 22 : poFeature->SetFID(nFeatureId);
149 50 : return poFeature;
150 : }
151 :
152 : /************************************************************************/
153 : /* ISetFeature() */
154 : /************************************************************************/
155 :
156 14 : OGRErr OGRXLSXLayer::ISetFeature(OGRFeature *poFeature)
157 : {
158 14 : Init();
159 :
160 14 : const GIntBig nFIDOrigin = poFeature->GetFID();
161 14 : if (nFIDOrigin > 0)
162 : {
163 5 : const GIntBig nFIDMemLayer = TranslateFIDToMemLayer(nFIDOrigin);
164 5 : if (!GetFeatureRef(nFIDMemLayer))
165 0 : return OGRERR_NON_EXISTING_FEATURE;
166 5 : poFeature->SetFID(nFIDMemLayer);
167 : }
168 : else
169 : {
170 9 : return OGRERR_NON_EXISTING_FEATURE;
171 : }
172 5 : SetUpdated();
173 5 : OGRErr eErr = OGRMemLayer::ISetFeature(poFeature);
174 5 : poFeature->SetFID(nFIDOrigin);
175 5 : return eErr;
176 : }
177 :
178 : /************************************************************************/
179 : /* IUpdateFeature() */
180 : /************************************************************************/
181 :
182 2 : OGRErr OGRXLSXLayer::IUpdateFeature(OGRFeature *poFeature,
183 : int nUpdatedFieldsCount,
184 : const int *panUpdatedFieldsIdx,
185 : int nUpdatedGeomFieldsCount,
186 : const int *panUpdatedGeomFieldsIdx,
187 : bool bUpdateStyleString)
188 : {
189 2 : Init();
190 :
191 2 : const GIntBig nFIDOrigin = poFeature->GetFID();
192 2 : if (nFIDOrigin != OGRNullFID)
193 2 : poFeature->SetFID(TranslateFIDToMemLayer(nFIDOrigin));
194 2 : SetUpdated();
195 2 : OGRErr eErr = OGRMemLayer::IUpdateFeature(
196 : poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
197 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx, bUpdateStyleString);
198 2 : poFeature->SetFID(nFIDOrigin);
199 2 : return eErr;
200 : }
201 :
202 : /************************************************************************/
203 : /* ICreateFeature() */
204 : /************************************************************************/
205 :
206 978 : OGRErr OGRXLSXLayer::ICreateFeature(OGRFeature *poFeature)
207 : {
208 978 : Init();
209 :
210 978 : const GIntBig nFIDOrigin = poFeature->GetFID();
211 978 : if (nFIDOrigin > 0)
212 : {
213 1 : const GIntBig nFIDModified = TranslateFIDToMemLayer(nFIDOrigin);
214 1 : if (GetFeatureRef(nFIDModified))
215 : {
216 0 : SetUpdated();
217 0 : poFeature->SetFID(nFIDModified);
218 0 : OGRErr eErr = OGRMemLayer::ISetFeature(poFeature);
219 0 : poFeature->SetFID(nFIDOrigin);
220 0 : return eErr;
221 : }
222 : }
223 978 : SetUpdated();
224 978 : poFeature->SetFID(OGRNullFID);
225 978 : OGRErr eErr = OGRMemLayer::ICreateFeature(poFeature);
226 978 : poFeature->SetFID(TranslateFIDFromMemLayer(poFeature->GetFID()));
227 978 : return eErr;
228 : }
229 :
230 : /************************************************************************/
231 : /* DeleteFeature() */
232 : /************************************************************************/
233 :
234 19 : OGRErr OGRXLSXLayer::DeleteFeature(GIntBig nFID)
235 : {
236 19 : Init();
237 19 : SetUpdated();
238 19 : return OGRMemLayer::DeleteFeature(TranslateFIDToMemLayer(nFID));
239 : }
240 :
241 : /************************************************************************/
242 : /* GetDataset() */
243 : /************************************************************************/
244 :
245 3 : GDALDataset *OGRXLSXLayer::GetDataset()
246 : {
247 3 : return poDS;
248 : }
249 :
250 : /************************************************************************/
251 : /* TestCapability() */
252 : /************************************************************************/
253 :
254 454 : int OGRXLSXLayer::TestCapability(const char *pszCap) const
255 :
256 : {
257 454 : if (EQUAL(pszCap, OLCUpsertFeature))
258 9 : return false;
259 445 : return OGRMemLayer::TestCapability(pszCap);
260 : }
261 :
262 : /************************************************************************/
263 : /* OGRXLSXDataSource() */
264 : /************************************************************************/
265 :
266 42 : OGRXLSXDataSource::OGRXLSXDataSource(CSLConstList papszOpenOptionsIn)
267 : : pszName(nullptr), bUpdatable(false), bUpdated(false), nLayers(0),
268 : papoLayers(nullptr), bFirstLineIsHeaders(false),
269 42 : bAutodetectTypes(!EQUAL(
270 : CSLFetchNameValueDef(papszOpenOptionsIn, "FIELD_TYPES",
271 : CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "")),
272 : "STRING")),
273 : oParser(nullptr), bStopParsing(false), nWithoutEventCounter(0),
274 : nDataHandlerCounter(0), nCurLine(0), nCurCol(0), poCurLayer(nullptr),
275 84 : nStackDepth(0), nDepth(0), bInCellXFS(false)
276 : {
277 42 : stateStack[0].eVal = STATE_DEFAULT;
278 42 : stateStack[0].nBeginDepth = 0;
279 42 : }
280 :
281 : /************************************************************************/
282 : /* ~OGRXLSXDataSource() */
283 : /************************************************************************/
284 :
285 84 : OGRXLSXDataSource::~OGRXLSXDataSource()
286 :
287 : {
288 42 : OGRXLSXDataSource::Close();
289 84 : }
290 :
291 : /************************************************************************/
292 : /* Close() */
293 : /************************************************************************/
294 :
295 84 : CPLErr OGRXLSXDataSource::Close()
296 : {
297 84 : CPLErr eErr = CE_None;
298 84 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
299 : {
300 42 : if (OGRXLSXDataSource::FlushCache(true) != CE_None)
301 0 : eErr = CE_Failure;
302 :
303 42 : CPLFree(pszName);
304 :
305 197 : for (int i = 0; i < nLayers; i++)
306 155 : delete papoLayers[i];
307 42 : CPLFree(papoLayers);
308 :
309 42 : if (GDALDataset::Close() != CE_None)
310 0 : eErr = CE_Failure;
311 : }
312 84 : return eErr;
313 : }
314 :
315 : /************************************************************************/
316 : /* TestCapability() */
317 : /************************************************************************/
318 :
319 90 : int OGRXLSXDataSource::TestCapability(const char *pszCap) const
320 :
321 : {
322 90 : if (EQUAL(pszCap, ODsCCreateLayer))
323 14 : return bUpdatable;
324 76 : else if (EQUAL(pszCap, ODsCDeleteLayer))
325 2 : return bUpdatable;
326 74 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
327 0 : return bUpdatable;
328 74 : else if (EQUAL(pszCap, ODsCZGeometries))
329 18 : return true;
330 56 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
331 18 : return true;
332 38 : else if (EQUAL(pszCap, ODsCCurveGeometries))
333 18 : return true;
334 : else
335 20 : return false;
336 : }
337 :
338 : /************************************************************************/
339 : /* GetLayer() */
340 : /************************************************************************/
341 :
342 937 : const OGRLayer *OGRXLSXDataSource::GetLayer(int iLayer) const
343 :
344 : {
345 937 : if (iLayer < 0 || iLayer >= nLayers)
346 4 : return nullptr;
347 :
348 933 : return papoLayers[iLayer];
349 : }
350 :
351 : /************************************************************************/
352 : /* GetLayerCount() */
353 : /************************************************************************/
354 :
355 955 : int OGRXLSXDataSource::GetLayerCount() const
356 : {
357 955 : return nLayers;
358 : }
359 :
360 : /************************************************************************/
361 : /* Open() */
362 : /************************************************************************/
363 :
364 34 : int OGRXLSXDataSource::Open(const char *pszFilename,
365 : const char *pszPrefixedFilename,
366 : VSILFILE *fpWorkbook, VSILFILE *fpWorkbookRels,
367 : VSILFILE *fpSharedStrings, VSILFILE *fpStyles,
368 : int bUpdateIn)
369 :
370 : {
371 34 : SetDescription(pszFilename);
372 :
373 34 : bUpdatable = CPL_TO_BOOL(bUpdateIn);
374 :
375 34 : pszName = CPLStrdup(pszFilename);
376 34 : osPrefixedFilename = pszPrefixedFilename;
377 :
378 34 : AnalyseWorkbookRels(fpWorkbookRels);
379 34 : AnalyseWorkbook(fpWorkbook);
380 34 : AnalyseSharedStrings(fpSharedStrings);
381 34 : AnalyseStyles(fpStyles);
382 :
383 : /* Remove empty layers at the end, which tend to be there */
384 46 : while (nLayers > 1)
385 : {
386 30 : papoLayers[nLayers - 1]->Init();
387 59 : if ((papoLayers[nLayers - 1]->m_osCols.empty() ||
388 29 : papoLayers[nLayers - 1]->m_osCols.find("max=\"1025\" min=\"1\"") !=
389 60 : std::string::npos) &&
390 25 : papoLayers[nLayers - 1]->GetFeatureCount(false) == 0)
391 : {
392 12 : delete papoLayers[nLayers - 1];
393 12 : nLayers--;
394 : }
395 : else
396 18 : break;
397 : }
398 :
399 34 : return TRUE;
400 : }
401 :
402 : /************************************************************************/
403 : /* Create() */
404 : /************************************************************************/
405 :
406 8 : int OGRXLSXDataSource::Create(const char *pszFilename,
407 : CPL_UNUSED char **papszOptions)
408 : {
409 8 : bUpdated = true;
410 8 : bUpdatable = true;
411 :
412 8 : pszName = CPLStrdup(pszFilename);
413 :
414 8 : return TRUE;
415 : }
416 :
417 : /************************************************************************/
418 : /* GetUnprefixed() */
419 : /************************************************************************/
420 :
421 13944 : static const char *GetUnprefixed(const char *pszStr)
422 : {
423 13944 : const char *pszColumn = strchr(pszStr, ':');
424 13944 : if (pszColumn)
425 71 : return pszColumn + 1;
426 13873 : return pszStr;
427 : }
428 :
429 : /************************************************************************/
430 : /* startElementCbk() */
431 : /************************************************************************/
432 :
433 4173 : static void XMLCALL startElementCbk(void *pUserData, const char *pszNameIn,
434 : const char **ppszAttr)
435 : {
436 4173 : ((OGRXLSXDataSource *)pUserData)->startElementCbk(pszNameIn, ppszAttr);
437 4173 : }
438 :
439 4173 : void OGRXLSXDataSource::startElementCbk(const char *pszNameIn,
440 : const char **ppszAttr)
441 : {
442 4173 : if (bStopParsing)
443 0 : return;
444 :
445 4173 : pszNameIn = GetUnprefixed(pszNameIn);
446 :
447 4173 : nWithoutEventCounter = 0;
448 4173 : switch (stateStack[nStackDepth].eVal)
449 : {
450 1051 : case STATE_DEFAULT:
451 1051 : startElementDefault(pszNameIn, ppszAttr);
452 1051 : break;
453 248 : case STATE_COLS:
454 248 : startElementCols(pszNameIn, ppszAttr);
455 248 : break;
456 842 : case STATE_SHEETDATA:
457 842 : startElementTable(pszNameIn, ppszAttr);
458 842 : break;
459 1012 : case STATE_ROW:
460 1012 : startElementRow(pszNameIn, ppszAttr);
461 1012 : break;
462 1020 : case STATE_CELL:
463 1020 : startElementCell(pszNameIn, ppszAttr);
464 1020 : break;
465 0 : case STATE_TEXTV:
466 0 : break;
467 0 : default:
468 0 : break;
469 : }
470 4173 : nDepth++;
471 : }
472 :
473 : /************************************************************************/
474 : /* endElementCbk() */
475 : /************************************************************************/
476 :
477 4173 : static void XMLCALL endElementCbk(void *pUserData, const char *pszNameIn)
478 : {
479 4173 : ((OGRXLSXDataSource *)pUserData)->endElementCbk(pszNameIn);
480 4173 : }
481 :
482 4173 : void OGRXLSXDataSource::endElementCbk(const char *pszNameIn)
483 : {
484 4173 : if (bStopParsing)
485 0 : return;
486 :
487 4173 : pszNameIn = GetUnprefixed(pszNameIn);
488 :
489 4173 : nWithoutEventCounter = 0;
490 :
491 4173 : nDepth--;
492 4173 : switch (stateStack[nStackDepth].eVal)
493 : {
494 888 : case STATE_DEFAULT:
495 888 : break;
496 84 : case STATE_SHEETDATA:
497 84 : endElementTable(pszNameIn);
498 84 : break;
499 327 : case STATE_COLS:
500 327 : endElementCols(pszNameIn);
501 327 : break;
502 842 : case STATE_ROW:
503 842 : endElementRow(pszNameIn);
504 842 : break;
505 1068 : case STATE_CELL:
506 1068 : endElementCell(pszNameIn);
507 1068 : break;
508 964 : case STATE_TEXTV:
509 964 : break;
510 0 : default:
511 0 : break;
512 : }
513 :
514 4173 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
515 3065 : nStackDepth--;
516 : }
517 :
518 : /************************************************************************/
519 : /* dataHandlerCbk() */
520 : /************************************************************************/
521 :
522 3070 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
523 : {
524 3070 : ((OGRXLSXDataSource *)pUserData)->dataHandlerCbk(data, nLen);
525 3070 : }
526 :
527 3070 : void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
528 : {
529 3070 : if (bStopParsing)
530 0 : return;
531 :
532 3070 : nDataHandlerCounter++;
533 3070 : if (nDataHandlerCounter >= PARSER_BUF_SIZE)
534 : {
535 0 : CPLError(CE_Failure, CPLE_AppDefined,
536 : "File probably corrupted (million laugh pattern)");
537 0 : XML_StopParser(oParser, XML_FALSE);
538 0 : bStopParsing = true;
539 0 : return;
540 : }
541 :
542 3070 : nWithoutEventCounter = 0;
543 :
544 3070 : switch (stateStack[nStackDepth].eVal)
545 : {
546 605 : case STATE_DEFAULT:
547 605 : break;
548 146 : case STATE_SHEETDATA:
549 146 : break;
550 445 : case STATE_ROW:
551 445 : break;
552 800 : case STATE_CELL:
553 800 : break;
554 964 : case STATE_TEXTV:
555 964 : dataHandlerTextV(data, nLen);
556 964 : break;
557 110 : default:
558 110 : break;
559 : }
560 : }
561 :
562 : /************************************************************************/
563 : /* PushState() */
564 : /************************************************************************/
565 :
566 4174 : void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
567 : {
568 4174 : if (nStackDepth + 1 == STACK_SIZE)
569 : {
570 0 : bStopParsing = true;
571 0 : return;
572 : }
573 4174 : nStackDepth++;
574 4174 : stateStack[nStackDepth].eVal = eVal;
575 4174 : stateStack[nStackDepth].nBeginDepth = nDepth;
576 : }
577 :
578 : /************************************************************************/
579 : /* GetAttributeValue() */
580 : /************************************************************************/
581 :
582 11819 : static const char *GetAttributeValue(const char **ppszAttr, const char *pszKey,
583 : const char *pszDefaultVal)
584 : {
585 11819 : while (*ppszAttr)
586 : {
587 11398 : if (strcmp(ppszAttr[0], pszKey) == 0)
588 4977 : return ppszAttr[1];
589 6421 : ppszAttr += 2;
590 : }
591 421 : return pszDefaultVal;
592 : }
593 :
594 : /************************************************************************/
595 : /* GetOGRFieldType() */
596 : /************************************************************************/
597 :
598 1018 : OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char *pszValue,
599 : const char *pszValueType,
600 : OGRFieldSubType &eSubType)
601 : {
602 1018 : eSubType = OFSTNone;
603 1018 : if (!bAutodetectTypes || pszValueType == nullptr)
604 32 : return OFTString;
605 986 : else if (strcmp(pszValueType, "string") == 0)
606 420 : return OFTString;
607 566 : else if (strcmp(pszValueType, "float") == 0)
608 : {
609 405 : CPLValueType eValueType = CPLGetValueType(pszValue);
610 405 : if (eValueType == CPL_VALUE_STRING)
611 14 : return OFTString;
612 391 : else if (eValueType == CPL_VALUE_INTEGER)
613 : {
614 260 : GIntBig nVal = CPLAtoGIntBig(pszValue);
615 260 : if (!CPL_INT64_FITS_ON_INT32(nVal))
616 1 : return OFTInteger64;
617 : else
618 259 : return OFTInteger;
619 : }
620 : else
621 131 : return OFTReal;
622 : }
623 161 : else if (strcmp(pszValueType, "datetime") == 0 ||
624 106 : strcmp(pszValueType, "datetime_ms") == 0)
625 : {
626 56 : return OFTDateTime;
627 : }
628 105 : else if (strcmp(pszValueType, "date") == 0)
629 : {
630 56 : return OFTDate;
631 : }
632 49 : else if (strcmp(pszValueType, "time") == 0)
633 : {
634 14 : return OFTTime;
635 : }
636 35 : else if (strcmp(pszValueType, "bool") == 0)
637 : {
638 1 : eSubType = OFSTBoolean;
639 1 : return OFTInteger;
640 : }
641 : else
642 34 : return OFTString;
643 : }
644 :
645 : /************************************************************************/
646 : /* SetField() */
647 : /************************************************************************/
648 :
649 841 : static void SetField(OGRFeature *poFeature, int i, const char *pszValue,
650 : const char *pszCellType)
651 : {
652 841 : if (pszValue[0] == '\0')
653 43 : return;
654 :
655 798 : OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();
656 :
657 798 : if (strcmp(pszCellType, "time") == 0 || strcmp(pszCellType, "date") == 0 ||
658 748 : strcmp(pszCellType, "datetime") == 0 ||
659 706 : strcmp(pszCellType, "datetime_ms") == 0)
660 : {
661 : struct tm sTm;
662 93 : const double dfNumberOfDaysSince1900 = CPLAtof(pszValue);
663 93 : if (!(std::fabs(dfNumberOfDaysSince1900) < 365.0 * 10000))
664 0 : return;
665 93 : double dfNumberOfSecsSince1900 =
666 : dfNumberOfDaysSince1900 * NUMBER_OF_SECONDS_PER_DAY;
667 93 : if (std::fabs(dfNumberOfSecsSince1900 -
668 93 : std::round(dfNumberOfSecsSince1900)) < 1e-3)
669 92 : dfNumberOfSecsSince1900 = std::round(dfNumberOfSecsSince1900);
670 93 : const GIntBig nUnixTime =
671 93 : static_cast<GIntBig>(dfNumberOfSecsSince1900) -
672 : static_cast<GIntBig>(NUMBER_OF_DAYS_BETWEEN_1900_AND_1970) *
673 : NUMBER_OF_SECONDS_PER_DAY;
674 93 : CPLUnixTimeToYMDHMS(nUnixTime, &sTm);
675 :
676 93 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
677 : {
678 73 : double fFracSec = fmod(dfNumberOfSecsSince1900, 1);
679 73 : poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1,
680 : sTm.tm_mday, sTm.tm_hour, sTm.tm_min,
681 73 : static_cast<float>(sTm.tm_sec + fFracSec), 0);
682 : }
683 20 : else if (strcmp(pszCellType, "time") == 0)
684 : {
685 4 : poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d", sTm.tm_hour,
686 : sTm.tm_min, sTm.tm_sec));
687 : }
688 16 : else if (strcmp(pszCellType, "date") == 0)
689 : {
690 8 : poFeature->SetField(i,
691 8 : CPLSPrintf("%04d/%02d/%02d", sTm.tm_year + 1900,
692 8 : sTm.tm_mon + 1, sTm.tm_mday));
693 : }
694 : else /* if (strcmp(pszCellType, "datetime") == 0) */
695 : {
696 8 : double fFracSec = fmod(dfNumberOfSecsSince1900, 1);
697 8 : poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1,
698 : sTm.tm_mday, sTm.tm_hour, sTm.tm_min,
699 8 : static_cast<float>(sTm.tm_sec + fFracSec), 0);
700 93 : }
701 : }
702 : else
703 705 : poFeature->SetField(i, pszValue);
704 : }
705 :
706 : /************************************************************************/
707 : /* DetectHeaderLine() */
708 : /************************************************************************/
709 :
710 60 : void OGRXLSXDataSource::DetectHeaderLine()
711 :
712 : {
713 60 : bool bHeaderLineCandidate = true;
714 :
715 281 : for (size_t i = 0; i < apoFirstLineTypes.size(); i++)
716 : {
717 244 : if (apoFirstLineTypes[i] != "string")
718 : {
719 : /* If the values in the first line are not text, then it is */
720 : /* not a header line */
721 23 : bHeaderLineCandidate = false;
722 23 : break;
723 : }
724 : }
725 :
726 60 : size_t nCountTextOnCurLine = 0;
727 60 : size_t nCountNonEmptyOnCurLine = 0;
728 267 : for (size_t i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
729 : {
730 207 : if (apoCurLineTypes[i] == "string")
731 : {
732 : /* If there are only text values on the second line, then we cannot
733 : */
734 : /* know if it is a header line or just a regular line */
735 63 : nCountTextOnCurLine++;
736 : }
737 144 : else if (apoCurLineTypes[i] != "")
738 : {
739 130 : nCountNonEmptyOnCurLine++;
740 : }
741 : }
742 :
743 : const char *pszXLSXHeaders =
744 60 : CSLFetchNameValueDef(papszOpenOptions, "HEADERS",
745 : CPLGetConfigOption("OGR_XLSX_HEADERS", ""));
746 60 : bFirstLineIsHeaders = false;
747 60 : if (EQUAL(pszXLSXHeaders, "FORCE"))
748 1 : bFirstLineIsHeaders = true;
749 59 : else if (EQUAL(pszXLSXHeaders, "DISABLE"))
750 3 : bFirstLineIsHeaders = false;
751 62 : else if (bHeaderLineCandidate && !apoFirstLineTypes.empty() &&
752 50 : apoFirstLineTypes.size() >= apoCurLineTypes.size() &&
753 112 : nCountTextOnCurLine != apoFirstLineTypes.size() &&
754 : nCountNonEmptyOnCurLine != 0)
755 : {
756 19 : bFirstLineIsHeaders = true;
757 : }
758 60 : CPLDebug("XLSX", "%s %s", poCurLayer ? poCurLayer->GetName() : "NULL layer",
759 60 : bFirstLineIsHeaders ? "has header line" : "has no header line");
760 60 : }
761 :
762 : /************************************************************************/
763 : /* startElementDefault() */
764 : /************************************************************************/
765 :
766 1051 : void OGRXLSXDataSource::startElementDefault(const char *pszNameIn,
767 : CPL_UNUSED const char **ppszAttr)
768 : {
769 1051 : if (strcmp(pszNameIn, "cols") == 0)
770 : {
771 79 : PushState(STATE_COLS);
772 79 : m_osCols = "<cols>";
773 : }
774 972 : else if (strcmp(pszNameIn, "sheetData") == 0)
775 : {
776 84 : apoFirstLineValues.resize(0);
777 84 : apoFirstLineTypes.resize(0);
778 84 : nCurLine = 0;
779 84 : PushState(STATE_SHEETDATA);
780 : }
781 1051 : }
782 :
783 : /************************************************************************/
784 : /* startElementCols() */
785 : /************************************************************************/
786 :
787 248 : void OGRXLSXDataSource::startElementCols(const char *pszNameIn,
788 : const char **ppszAttr)
789 : {
790 248 : m_osCols.append("<");
791 248 : m_osCols.append(pszNameIn);
792 1542 : for (const char **iter = ppszAttr; iter && iter[0] && iter[1]; iter += 2)
793 : {
794 1294 : m_osCols.append(" ");
795 1294 : m_osCols.append(iter[0]);
796 1294 : m_osCols.append("=\"");
797 1294 : char *pszXML = OGRGetXML_UTF8_EscapedString(iter[1]);
798 1294 : m_osCols.append(pszXML);
799 1294 : CPLFree(pszXML);
800 1294 : m_osCols.append("\"");
801 : }
802 248 : m_osCols.append(">");
803 248 : }
804 :
805 : /************************************************************************/
806 : /* endElementCell() */
807 : /************************************************************************/
808 :
809 327 : void OGRXLSXDataSource::endElementCols(const char *pszNameIn)
810 : {
811 327 : m_osCols.append("</");
812 327 : m_osCols.append(pszNameIn);
813 327 : m_osCols.append(">");
814 327 : }
815 :
816 : /************************************************************************/
817 : /* startElementTable() */
818 : /************************************************************************/
819 :
820 842 : void OGRXLSXDataSource::startElementTable(const char *pszNameIn,
821 : const char **ppszAttr)
822 : {
823 842 : if (strcmp(pszNameIn, "row") == 0)
824 : {
825 842 : PushState(STATE_ROW);
826 :
827 842 : nCurCol = 0;
828 842 : apoCurLineValues.clear();
829 842 : apoCurLineTypes.clear();
830 :
831 : int nNewCurLine;
832 842 : if (const char *pszR = GetAttributeValue(ppszAttr, "r", nullptr))
833 : {
834 839 : nNewCurLine = atoi(pszR);
835 839 : if (nNewCurLine <= 0)
836 : {
837 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid row: %d",
838 : nNewCurLine);
839 0 : return;
840 : }
841 839 : nNewCurLine--;
842 : }
843 : else
844 : {
845 3 : nNewCurLine = nCurLine;
846 : }
847 : const int nFields = std::max(
848 2526 : static_cast<int>(apoFirstLineValues.size()),
849 842 : poCurLayer != nullptr ? poCurLayer->GetLayerDefn()->GetFieldCount()
850 842 : : 0);
851 842 : if (nNewCurLine > nCurLine &&
852 16 : (nNewCurLine - nCurLine > 10000 ||
853 10 : (nFields > 0 && nNewCurLine - nCurLine > 100000 / nFields)))
854 : {
855 0 : CPLError(CE_Failure, CPLE_AppDefined,
856 : "Invalid row: %d. Too big gap with previous valid row",
857 : nNewCurLine);
858 0 : return;
859 : }
860 917 : for (; nCurLine < nNewCurLine;)
861 : {
862 75 : const int nCurLineBefore = nCurLine;
863 75 : endElementRow("row");
864 :
865 75 : nCurCol = 0;
866 75 : apoCurLineValues.clear();
867 75 : apoCurLineTypes.clear();
868 75 : if (nCurLineBefore == nCurLine)
869 0 : break;
870 : }
871 : }
872 : }
873 :
874 : /************************************************************************/
875 : /* endElementTable() */
876 : /************************************************************************/
877 :
878 84 : void OGRXLSXDataSource::endElementTable(CPL_UNUSED const char *pszNameIn)
879 : {
880 84 : if (stateStack[nStackDepth].nBeginDepth == nDepth && poCurLayer != nullptr)
881 : {
882 84 : CPLAssert(strcmp(pszNameIn, "sheetData") == 0);
883 :
884 84 : if (nCurLine == 0 || (nCurLine == 1 && apoFirstLineValues.empty()))
885 : {
886 : // no rows
887 : }
888 68 : else if (nCurLine == 1)
889 : {
890 : /* If we have only one single line in the sheet */
891 50 : for (size_t i = 0; i < apoFirstLineValues.size(); i++)
892 : {
893 42 : const char *pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
894 42 : OGRFieldSubType eSubType = OFSTNone;
895 : OGRFieldType eType =
896 42 : GetOGRFieldType(apoFirstLineValues[i].c_str(),
897 42 : apoFirstLineTypes[i].c_str(), eSubType);
898 42 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
899 42 : oFieldDefn.SetSubType(eSubType);
900 42 : if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
901 : {
902 0 : return;
903 : }
904 : }
905 :
906 8 : OGRFeature *poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
907 50 : for (size_t i = 0; i < apoFirstLineValues.size(); i++)
908 : {
909 84 : SetField(poFeature, static_cast<int>(i),
910 42 : apoFirstLineValues[i].c_str(),
911 42 : apoFirstLineTypes[i].c_str());
912 : }
913 8 : CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
914 8 : delete poFeature;
915 : }
916 :
917 84 : if (poCurLayer)
918 : {
919 84 : poCurLayer->SetUpdatable(CPL_TO_BOOL(bUpdatable));
920 84 : poCurLayer->SetUpdated(false);
921 : }
922 :
923 84 : poCurLayer = nullptr;
924 : }
925 : }
926 :
927 : /************************************************************************/
928 : /* startElementRow() */
929 : /************************************************************************/
930 :
931 1012 : void OGRXLSXDataSource::startElementRow(const char *pszNameIn,
932 : const char **ppszAttr)
933 : {
934 1012 : if (strcmp(pszNameIn, "c") == 0)
935 : {
936 1012 : PushState(STATE_CELL);
937 :
938 1012 : const char *pszR = GetAttributeValue(ppszAttr, "r", nullptr);
939 1012 : if (pszR && pszR[0] >= 'A' && pszR[0] <= 'Z')
940 : {
941 : /* Convert col number from base 26 */
942 : /*
943 : A Z AA AZ BA BZ ZA ZZ AAA ZZZ AAAA
944 : 0 25 26 51 52 77 676 701 702 18277 18278
945 : */
946 1007 : int nNewCurCol = (pszR[0] - 'A');
947 1007 : int i = 1;
948 1010 : while (pszR[i] >= 'A' && pszR[i] <= 'Z' && nNewCurCol <= 2000)
949 : {
950 : // We wouldn't need the +1 if this was a proper base 26
951 3 : nNewCurCol = (nNewCurCol + 1) * 26 + (pszR[i] - 'A');
952 3 : i++;
953 : }
954 1007 : if (nNewCurCol > 2000)
955 : {
956 0 : CPLError(CE_Warning, CPLE_AppDefined,
957 : "Limiting number of columns to 2000");
958 0 : nNewCurCol = 2000;
959 : }
960 1195 : for (; nCurCol < nNewCurCol; nCurCol++)
961 : {
962 188 : apoCurLineValues.push_back("");
963 188 : apoCurLineTypes.push_back("");
964 : }
965 : }
966 :
967 1012 : osValueType = "float";
968 :
969 1012 : const char *pszS = GetAttributeValue(ppszAttr, "s", "-1");
970 1012 : int nS = atoi(pszS);
971 1012 : if (nS >= 0 && nS < (int)apoStyles.size())
972 : {
973 771 : XLSXFieldTypeExtended eType = apoStyles[nS];
974 771 : if (eType.eType == OFTDateTime)
975 : {
976 43 : if (eType.bHasMS)
977 1 : osValueType = "datetime_ms";
978 : else
979 42 : osValueType = "datetime";
980 : }
981 728 : else if (eType.eType == OFTDate)
982 42 : osValueType = "date";
983 686 : else if (eType.eType == OFTTime)
984 10 : osValueType = "time";
985 : }
986 241 : else if (nS != -1)
987 0 : CPLDebug("XLSX", "Cannot find style %d", nS);
988 :
989 1012 : const char *pszT = GetAttributeValue(ppszAttr, "t", "");
990 1012 : if (EQUAL(pszT, "s"))
991 501 : osValueType = "stringLookup";
992 511 : else if (EQUAL(pszT, "inlineStr"))
993 28 : osValueType = "string";
994 483 : else if (EQUAL(pszT, "b"))
995 1 : osValueType = "bool";
996 :
997 1012 : osValue = "";
998 : }
999 1012 : }
1000 :
1001 : /************************************************************************/
1002 : /* endElementRow() */
1003 : /************************************************************************/
1004 :
1005 917 : void OGRXLSXDataSource::endElementRow(CPL_UNUSED const char *pszNameIn)
1006 : {
1007 917 : if (stateStack[nStackDepth].nBeginDepth == nDepth && poCurLayer != nullptr)
1008 : {
1009 917 : CPLAssert(strcmp(pszNameIn, "row") == 0);
1010 :
1011 : /* Backup first line values and types in special arrays */
1012 917 : if (nCurLine == 0)
1013 : {
1014 68 : apoFirstLineTypes = apoCurLineTypes;
1015 68 : apoFirstLineValues = apoCurLineValues;
1016 :
1017 : #if skip_leading_empty_rows
1018 : if (apoFirstLineTypes.empty())
1019 : {
1020 : /* Skip leading empty rows */
1021 : apoFirstLineTypes.resize(0);
1022 : apoFirstLineValues.resize(0);
1023 : return;
1024 : }
1025 : #endif
1026 : }
1027 :
1028 917 : if (nCurLine == 1)
1029 : {
1030 60 : DetectHeaderLine();
1031 :
1032 60 : poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);
1033 :
1034 60 : if (bFirstLineIsHeaders)
1035 : {
1036 186 : for (size_t i = 0; i < apoFirstLineValues.size(); i++)
1037 : {
1038 166 : const char *pszFieldName = apoFirstLineValues[i].c_str();
1039 166 : if (pszFieldName[0] == '\0')
1040 0 : pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
1041 166 : bool bUnknownType = true;
1042 166 : OGRFieldType eType = OFTString;
1043 166 : OGRFieldSubType eSubType = OFSTNone;
1044 331 : if (i < apoCurLineValues.size() &&
1045 165 : !apoCurLineValues[i].empty())
1046 : {
1047 160 : eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
1048 160 : apoCurLineTypes[i].c_str(),
1049 : eSubType);
1050 160 : bUnknownType = false;
1051 : }
1052 166 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
1053 166 : oFieldDefn.SetSubType(eSubType);
1054 166 : if (bUnknownType)
1055 : {
1056 6 : poCurLayer->oSetFieldsOfUnknownType.insert(
1057 6 : poCurLayer->GetLayerDefn()->GetFieldCount());
1058 : }
1059 166 : if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
1060 : {
1061 0 : return;
1062 : }
1063 : }
1064 : }
1065 : else
1066 : {
1067 132 : for (size_t i = 0; i < apoFirstLineValues.size(); i++)
1068 : {
1069 : const char *pszFieldName =
1070 92 : CPLSPrintf("Field%d", (int)i + 1);
1071 92 : OGRFieldSubType eSubType = OFSTNone;
1072 : OGRFieldType eType =
1073 92 : GetOGRFieldType(apoFirstLineValues[i].c_str(),
1074 92 : apoFirstLineTypes[i].c_str(), eSubType);
1075 92 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
1076 92 : oFieldDefn.SetSubType(eSubType);
1077 92 : if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
1078 : {
1079 0 : return;
1080 : }
1081 : }
1082 :
1083 : OGRFeature *poFeature =
1084 40 : new OGRFeature(poCurLayer->GetLayerDefn());
1085 132 : for (size_t i = 0; i < apoFirstLineValues.size(); i++)
1086 : {
1087 184 : SetField(poFeature, static_cast<int>(i),
1088 92 : apoFirstLineValues[i].c_str(),
1089 92 : apoFirstLineTypes[i].c_str());
1090 : }
1091 40 : CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
1092 40 : delete poFeature;
1093 : }
1094 : }
1095 :
1096 917 : if (nCurLine >= 1)
1097 : {
1098 : /* Add new fields found on following lines. */
1099 849 : if (apoCurLineValues.size() >
1100 849 : (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
1101 : {
1102 34 : GIntBig nFeatureCount = poCurLayer->GetFeatureCount(false);
1103 68 : if (nFeatureCount > 0 &&
1104 : static_cast<size_t>(
1105 34 : apoCurLineValues.size() -
1106 34 : poCurLayer->GetLayerDefn()->GetFieldCount()) >
1107 34 : static_cast<size_t>(100000 / nFeatureCount))
1108 : {
1109 0 : CPLError(CE_Failure, CPLE_NotSupported,
1110 : "Adding too many columns to too many "
1111 : "existing features");
1112 0 : return;
1113 : }
1114 61 : for (size_t i =
1115 34 : (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
1116 95 : i < apoCurLineValues.size(); i++)
1117 : {
1118 : const char *pszFieldName =
1119 61 : CPLSPrintf("Field%d", (int)i + 1);
1120 61 : OGRFieldSubType eSubType = OFSTNone;
1121 : OGRFieldType eType =
1122 61 : GetOGRFieldType(apoCurLineValues[i].c_str(),
1123 61 : apoCurLineTypes[i].c_str(), eSubType);
1124 61 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
1125 61 : oFieldDefn.SetSubType(eSubType);
1126 61 : if (poCurLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
1127 : {
1128 0 : return;
1129 : }
1130 : }
1131 : }
1132 :
1133 : /* Update field type if necessary */
1134 849 : if (bAutodetectTypes)
1135 : {
1136 1671 : for (size_t i = 0; i < apoCurLineValues.size(); i++)
1137 : {
1138 832 : if (!apoCurLineValues[i].empty())
1139 : {
1140 663 : OGRFieldSubType eValSubType = OFSTNone;
1141 1326 : OGRFieldType eValType = GetOGRFieldType(
1142 663 : apoCurLineValues[i].c_str(),
1143 663 : apoCurLineTypes[i].c_str(), eValSubType);
1144 663 : OGRLayer *poCurLayerAsLayer = poCurLayer;
1145 : OGRFieldDefn *poFieldDefn =
1146 663 : poCurLayerAsLayer->GetLayerDefn()->GetFieldDefn(
1147 663 : static_cast<int>(i));
1148 663 : const OGRFieldType eFieldType = poFieldDefn->GetType();
1149 663 : auto oIter = poCurLayer->oSetFieldsOfUnknownType.find(
1150 663 : static_cast<int>(i));
1151 663 : if (oIter != poCurLayer->oSetFieldsOfUnknownType.end())
1152 : {
1153 4 : poCurLayer->oSetFieldsOfUnknownType.erase(oIter);
1154 :
1155 : auto oTemporaryUnsealer(
1156 8 : poFieldDefn->GetTemporaryUnsealer());
1157 4 : poFieldDefn->SetType(eValType);
1158 4 : poFieldDefn->SetSubType(eValSubType);
1159 : }
1160 659 : else if (eFieldType == OFTDateTime &&
1161 28 : (eValType == OFTDate || eValType == OFTTime))
1162 : {
1163 : /* ok */
1164 : }
1165 659 : else if (eFieldType == OFTReal &&
1166 72 : (eValType == OFTInteger ||
1167 : eValType == OFTInteger64))
1168 : {
1169 : /* ok */;
1170 : }
1171 653 : else if (eFieldType == OFTInteger64 &&
1172 : eValType == OFTInteger)
1173 : {
1174 : /* ok */;
1175 : }
1176 652 : else if (eFieldType != OFTString &&
1177 : eValType != eFieldType)
1178 : {
1179 36 : OGRFieldDefn oNewFieldDefn(poFieldDefn);
1180 18 : oNewFieldDefn.SetSubType(OFSTNone);
1181 18 : if ((eFieldType == OFTDate ||
1182 11 : eFieldType == OFTTime) &&
1183 : eValType == OFTDateTime)
1184 4 : oNewFieldDefn.SetType(OFTDateTime);
1185 14 : else if ((eFieldType == OFTInteger ||
1186 7 : eFieldType == OFTInteger64) &&
1187 : eValType == OFTReal)
1188 6 : oNewFieldDefn.SetType(OFTReal);
1189 8 : else if (eFieldType == OFTInteger &&
1190 : eValType == OFTInteger64)
1191 1 : oNewFieldDefn.SetType(OFTInteger64);
1192 : else
1193 7 : oNewFieldDefn.SetType(OFTString);
1194 18 : poCurLayer->AlterFieldDefn(static_cast<int>(i),
1195 : &oNewFieldDefn,
1196 18 : ALTER_TYPE_FLAG);
1197 : }
1198 98 : else if (eFieldType == OFTInteger &&
1199 98 : poFieldDefn->GetSubType() == OFSTBoolean &&
1200 732 : eValType == OFTInteger &&
1201 0 : eValSubType != OFSTBoolean)
1202 : {
1203 0 : poFieldDefn->SetSubType(OFSTNone);
1204 : }
1205 : }
1206 : }
1207 : }
1208 :
1209 : /* Add feature for current line */
1210 849 : OGRFeature *poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
1211 1749 : for (size_t i = 0; i < apoCurLineValues.size(); i++)
1212 : {
1213 900 : if (!apoCurLineValues[i].empty())
1214 : {
1215 1414 : SetField(poFeature, static_cast<int>(i),
1216 707 : apoCurLineValues[i].c_str(),
1217 707 : apoCurLineTypes[i].c_str());
1218 : }
1219 : }
1220 849 : CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
1221 849 : delete poFeature;
1222 : }
1223 :
1224 917 : nCurLine++;
1225 : }
1226 : }
1227 :
1228 : /************************************************************************/
1229 : /* startElementCell() */
1230 : /************************************************************************/
1231 :
1232 1020 : void OGRXLSXDataSource::startElementCell(const char *pszNameIn,
1233 : CPL_UNUSED const char **ppszAttr)
1234 : {
1235 1020 : if (osValue.empty() && strcmp(pszNameIn, "v") == 0)
1236 : {
1237 936 : PushState(STATE_TEXTV);
1238 : }
1239 84 : else if (osValue.empty() && strcmp(pszNameIn, "t") == 0)
1240 : {
1241 28 : PushState(STATE_TEXTV);
1242 : }
1243 1020 : }
1244 :
1245 : /************************************************************************/
1246 : /* endElementCell() */
1247 : /************************************************************************/
1248 :
1249 1068 : void OGRXLSXDataSource::endElementCell(CPL_UNUSED const char *pszNameIn)
1250 : {
1251 1068 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1252 : {
1253 1012 : CPLAssert(strcmp(pszNameIn, "c") == 0);
1254 :
1255 1012 : if (osValueType == "stringLookup")
1256 : {
1257 501 : int nIndex = atoi(osValue);
1258 501 : if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
1259 501 : osValue = apoSharedStrings[nIndex];
1260 : else
1261 0 : CPLDebug("XLSX", "Cannot find string %d", nIndex);
1262 501 : osValueType = "string";
1263 : }
1264 :
1265 1012 : apoCurLineValues.push_back(osValue);
1266 1012 : apoCurLineTypes.push_back(osValueType);
1267 :
1268 1012 : nCurCol += 1;
1269 : }
1270 1068 : }
1271 :
1272 : /************************************************************************/
1273 : /* dataHandlerTextV() */
1274 : /************************************************************************/
1275 :
1276 964 : void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
1277 : {
1278 964 : osValue.append(data, nLen);
1279 964 : }
1280 :
1281 : /************************************************************************/
1282 : /* BuildLayer() */
1283 : /************************************************************************/
1284 :
1285 84 : void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer *poLayer)
1286 : {
1287 84 : poCurLayer = poLayer;
1288 :
1289 84 : const char *pszSheetFilename = poLayer->GetFilename().c_str();
1290 84 : VSILFILE *fp = VSIFOpenL(pszSheetFilename, "rb");
1291 84 : if (fp == nullptr)
1292 : {
1293 0 : CPLDebug("XLSX", "Cannot open file %s for sheet %s", pszSheetFilename,
1294 : poLayer->GetName());
1295 0 : return;
1296 : }
1297 :
1298 84 : const bool bUpdatedBackup = bUpdated;
1299 :
1300 84 : oParser = OGRCreateExpatXMLParser();
1301 84 : m_osCols.clear();
1302 84 : XML_SetElementHandler(oParser, OGRXLSX::startElementCbk,
1303 : OGRXLSX::endElementCbk);
1304 84 : XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerCbk);
1305 84 : XML_SetUserData(oParser, this);
1306 :
1307 84 : VSIFSeekL(fp, 0, SEEK_SET);
1308 :
1309 84 : bStopParsing = false;
1310 84 : nWithoutEventCounter = 0;
1311 84 : nDataHandlerCounter = 0;
1312 84 : nStackDepth = 0;
1313 84 : nDepth = 0;
1314 84 : stateStack[0].eVal = STATE_DEFAULT;
1315 84 : stateStack[0].nBeginDepth = 0;
1316 :
1317 168 : std::vector<char> aBuf(PARSER_BUF_SIZE);
1318 84 : int nDone = 0;
1319 8 : do
1320 : {
1321 92 : nDataHandlerCounter = 0;
1322 : unsigned int nLen =
1323 92 : (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp);
1324 92 : nDone = (nLen < aBuf.size());
1325 92 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1326 : {
1327 0 : CPLError(CE_Failure, CPLE_AppDefined,
1328 : "XML parsing of %s file failed : %s at line %d, column %d",
1329 : pszSheetFilename,
1330 : XML_ErrorString(XML_GetErrorCode(oParser)),
1331 0 : (int)XML_GetCurrentLineNumber(oParser),
1332 0 : (int)XML_GetCurrentColumnNumber(oParser));
1333 0 : bStopParsing = true;
1334 : }
1335 92 : nWithoutEventCounter++;
1336 92 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1337 :
1338 84 : XML_ParserFree(oParser);
1339 84 : oParser = nullptr;
1340 :
1341 84 : if (nWithoutEventCounter == 10)
1342 : {
1343 0 : CPLError(CE_Failure, CPLE_AppDefined,
1344 : "Too much data inside one element. File probably corrupted");
1345 0 : bStopParsing = true;
1346 : }
1347 :
1348 84 : VSIFCloseL(fp);
1349 :
1350 84 : bUpdated = bUpdatedBackup;
1351 84 : poLayer->m_osCols = m_osCols;
1352 : }
1353 :
1354 : /************************************************************************/
1355 : /* startElementSSCbk() */
1356 : /************************************************************************/
1357 :
1358 1249 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszNameIn,
1359 : const char **ppszAttr)
1360 : {
1361 1249 : ((OGRXLSXDataSource *)pUserData)->startElementSSCbk(pszNameIn, ppszAttr);
1362 1249 : }
1363 :
1364 1249 : void OGRXLSXDataSource::startElementSSCbk(const char *pszNameIn,
1365 : CPL_UNUSED const char **ppszAttr)
1366 : {
1367 1249 : if (bStopParsing)
1368 0 : return;
1369 :
1370 1249 : pszNameIn = GetUnprefixed(pszNameIn);
1371 :
1372 1249 : nWithoutEventCounter = 0;
1373 1249 : switch (stateStack[nStackDepth].eVal)
1374 : {
1375 627 : case STATE_DEFAULT:
1376 : {
1377 627 : if (strcmp(pszNameIn, "si") == 0)
1378 : {
1379 595 : PushState(STATE_SI);
1380 595 : osCurrentString = "";
1381 : }
1382 627 : break;
1383 : }
1384 622 : case STATE_SI:
1385 : {
1386 622 : if (strcmp(pszNameIn, "t") == 0)
1387 : {
1388 598 : PushState(STATE_T);
1389 : }
1390 622 : break;
1391 : }
1392 0 : default:
1393 0 : break;
1394 : }
1395 1249 : nDepth++;
1396 : }
1397 :
1398 : /************************************************************************/
1399 : /* endElementSSCbk() */
1400 : /************************************************************************/
1401 :
1402 1249 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszNameIn)
1403 : {
1404 1249 : ((OGRXLSXDataSource *)pUserData)->endElementSSCbk(pszNameIn);
1405 1249 : }
1406 :
1407 1249 : void OGRXLSXDataSource::endElementSSCbk(const char * /*pszNameIn*/)
1408 : {
1409 1249 : if (bStopParsing)
1410 0 : return;
1411 :
1412 : // If we were to use pszNameIn, then we need:
1413 : // pszNameIn = GetUnprefixed(pszNameIn);
1414 :
1415 1249 : nWithoutEventCounter = 0;
1416 :
1417 1249 : nDepth--;
1418 1249 : switch (stateStack[nStackDepth].eVal)
1419 : {
1420 32 : case STATE_DEFAULT:
1421 32 : break;
1422 598 : case STATE_T:
1423 598 : break;
1424 619 : case STATE_SI:
1425 : {
1426 619 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1427 : {
1428 595 : apoSharedStrings.push_back(osCurrentString);
1429 : }
1430 619 : break;
1431 : }
1432 0 : default:
1433 0 : break;
1434 : }
1435 :
1436 1249 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1437 1225 : nStackDepth--;
1438 : }
1439 :
1440 : /************************************************************************/
1441 : /* dataHandlerSSCbk() */
1442 : /************************************************************************/
1443 :
1444 1104 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data,
1445 : int nLen)
1446 : {
1447 1104 : ((OGRXLSXDataSource *)pUserData)->dataHandlerSSCbk(data, nLen);
1448 1104 : }
1449 :
1450 1104 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
1451 : {
1452 1104 : if (bStopParsing)
1453 0 : return;
1454 :
1455 1104 : nDataHandlerCounter++;
1456 1104 : if (nDataHandlerCounter >= PARSER_BUF_SIZE)
1457 : {
1458 0 : CPLError(CE_Failure, CPLE_AppDefined,
1459 : "File probably corrupted (million laugh pattern)");
1460 0 : XML_StopParser(oParser, XML_FALSE);
1461 0 : bStopParsing = true;
1462 0 : return;
1463 : }
1464 :
1465 1104 : nWithoutEventCounter = 0;
1466 :
1467 1104 : switch (stateStack[nStackDepth].eVal)
1468 : {
1469 162 : case STATE_DEFAULT:
1470 162 : break;
1471 298 : case STATE_SI:
1472 298 : break;
1473 644 : case STATE_T:
1474 644 : osCurrentString.append(data, nLen);
1475 644 : break;
1476 0 : default:
1477 0 : break;
1478 : }
1479 : }
1480 :
1481 : /************************************************************************/
1482 : /* AnalyseSharedStrings() */
1483 : /************************************************************************/
1484 :
1485 34 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE *fpSharedStrings)
1486 : {
1487 34 : if (fpSharedStrings == nullptr)
1488 2 : return;
1489 :
1490 32 : oParser = OGRCreateExpatXMLParser();
1491 32 : XML_SetElementHandler(oParser, OGRXLSX::startElementSSCbk,
1492 : OGRXLSX::endElementSSCbk);
1493 32 : XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerSSCbk);
1494 32 : XML_SetUserData(oParser, this);
1495 :
1496 32 : VSIFSeekL(fpSharedStrings, 0, SEEK_SET);
1497 :
1498 32 : bStopParsing = false;
1499 32 : nWithoutEventCounter = 0;
1500 32 : nDataHandlerCounter = 0;
1501 32 : nStackDepth = 0;
1502 32 : nDepth = 0;
1503 32 : stateStack[0].eVal = STATE_DEFAULT;
1504 32 : stateStack[0].nBeginDepth = 0;
1505 :
1506 64 : std::vector<char> aBuf(PARSER_BUF_SIZE);
1507 32 : int nDone = 0;
1508 0 : do
1509 : {
1510 32 : nDataHandlerCounter = 0;
1511 32 : unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
1512 32 : fpSharedStrings);
1513 32 : nDone = (nLen < aBuf.size());
1514 32 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1515 : {
1516 0 : CPLError(CE_Failure, CPLE_AppDefined,
1517 : "XML parsing of %s file failed : %s at line %d, column %d",
1518 : "sharedStrings.xml",
1519 : XML_ErrorString(XML_GetErrorCode(oParser)),
1520 0 : (int)XML_GetCurrentLineNumber(oParser),
1521 0 : (int)XML_GetCurrentColumnNumber(oParser));
1522 0 : bStopParsing = true;
1523 : }
1524 32 : nWithoutEventCounter++;
1525 32 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1526 :
1527 32 : XML_ParserFree(oParser);
1528 32 : oParser = nullptr;
1529 :
1530 32 : if (nWithoutEventCounter == 10)
1531 : {
1532 0 : CPLError(CE_Failure, CPLE_AppDefined,
1533 : "Too much data inside one element. File probably corrupted");
1534 0 : bStopParsing = true;
1535 : }
1536 :
1537 32 : VSIFCloseL(fpSharedStrings);
1538 : }
1539 :
1540 : /************************************************************************/
1541 : /* startElementWBRelsCbk() */
1542 : /************************************************************************/
1543 :
1544 251 : static void XMLCALL startElementWBRelsCbk(void *pUserData,
1545 : const char *pszNameIn,
1546 : const char **ppszAttr)
1547 : {
1548 : ((OGRXLSXDataSource *)pUserData)
1549 251 : ->startElementWBRelsCbk(pszNameIn, ppszAttr);
1550 251 : }
1551 :
1552 251 : void OGRXLSXDataSource::startElementWBRelsCbk(const char *pszNameIn,
1553 : const char **ppszAttr)
1554 : {
1555 251 : if (bStopParsing)
1556 0 : return;
1557 :
1558 251 : pszNameIn = GetUnprefixed(pszNameIn);
1559 :
1560 251 : nWithoutEventCounter = 0;
1561 251 : if (strcmp(pszNameIn, "Relationship") == 0)
1562 : {
1563 217 : const char *pszId = GetAttributeValue(ppszAttr, "Id", nullptr);
1564 217 : const char *pszType = GetAttributeValue(ppszAttr, "Type", nullptr);
1565 217 : const char *pszTarget = GetAttributeValue(ppszAttr, "Target", nullptr);
1566 217 : if (pszId && pszType && pszTarget &&
1567 217 : strstr(pszType, "/worksheet") != nullptr)
1568 : {
1569 149 : oMapRelsIdToTarget[pszId] = pszTarget;
1570 : }
1571 : }
1572 : }
1573 :
1574 : /************************************************************************/
1575 : /* AnalyseWorkbookRels() */
1576 : /************************************************************************/
1577 :
1578 34 : void OGRXLSXDataSource::AnalyseWorkbookRels(VSILFILE *fpWorkbookRels)
1579 : {
1580 34 : oParser = OGRCreateExpatXMLParser();
1581 34 : XML_SetElementHandler(oParser, OGRXLSX::startElementWBRelsCbk, nullptr);
1582 34 : XML_SetUserData(oParser, this);
1583 :
1584 34 : VSIFSeekL(fpWorkbookRels, 0, SEEK_SET);
1585 :
1586 34 : bStopParsing = false;
1587 34 : nWithoutEventCounter = 0;
1588 34 : nDataHandlerCounter = 0;
1589 :
1590 68 : std::vector<char> aBuf(PARSER_BUF_SIZE);
1591 34 : int nDone = 0;
1592 0 : do
1593 : {
1594 34 : nDataHandlerCounter = 0;
1595 34 : unsigned int nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(),
1596 34 : fpWorkbookRels);
1597 34 : nDone = (nLen < aBuf.size());
1598 34 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1599 : {
1600 0 : CPLError(CE_Failure, CPLE_AppDefined,
1601 : "XML parsing of %s file failed : %s at line %d, column %d",
1602 : "xl/_rels/workbook.xml.rels",
1603 : XML_ErrorString(XML_GetErrorCode(oParser)),
1604 0 : (int)XML_GetCurrentLineNumber(oParser),
1605 0 : (int)XML_GetCurrentColumnNumber(oParser));
1606 0 : bStopParsing = true;
1607 : }
1608 34 : nWithoutEventCounter++;
1609 34 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1610 :
1611 34 : XML_ParserFree(oParser);
1612 34 : oParser = nullptr;
1613 :
1614 34 : if (nWithoutEventCounter == 10)
1615 : {
1616 0 : CPLError(CE_Failure, CPLE_AppDefined,
1617 : "Too much data inside one element. File probably corrupted");
1618 0 : bStopParsing = true;
1619 : }
1620 :
1621 34 : VSIFCloseL(fpWorkbookRels);
1622 34 : }
1623 :
1624 : /************************************************************************/
1625 : /* startElementWBCbk() */
1626 : /************************************************************************/
1627 :
1628 404 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszNameIn,
1629 : const char **ppszAttr)
1630 : {
1631 404 : ((OGRXLSXDataSource *)pUserData)->startElementWBCbk(pszNameIn, ppszAttr);
1632 404 : }
1633 :
1634 404 : void OGRXLSXDataSource::startElementWBCbk(const char *pszNameIn,
1635 : const char **ppszAttr)
1636 : {
1637 404 : if (bStopParsing)
1638 0 : return;
1639 :
1640 404 : pszNameIn = GetUnprefixed(pszNameIn);
1641 :
1642 404 : nWithoutEventCounter = 0;
1643 404 : if (strcmp(pszNameIn, "sheet") == 0)
1644 : {
1645 149 : const char *pszSheetName = GetAttributeValue(ppszAttr, "name", nullptr);
1646 149 : const char *pszId = GetAttributeValue(ppszAttr, "r:id", nullptr);
1647 298 : if (pszSheetName && pszId &&
1648 596 : oMapRelsIdToTarget.find(pszId) != oMapRelsIdToTarget.end() &&
1649 298 : m_oSetSheetId.find(pszId) == m_oSetSheetId.end())
1650 : {
1651 149 : const auto &osTarget(oMapRelsIdToTarget[pszId]);
1652 149 : m_oSetSheetId.insert(pszId);
1653 149 : CPLString osFilename;
1654 149 : if (osTarget.empty())
1655 0 : return;
1656 149 : if (osTarget[0] == '/')
1657 : {
1658 2 : int nIdx = 1;
1659 2 : while (osTarget[nIdx] == '/')
1660 0 : nIdx++;
1661 2 : if (osTarget[nIdx] == '\0')
1662 0 : return;
1663 : // Is it an "absolute" path ?
1664 2 : osFilename = osPrefixedFilename + osTarget;
1665 : }
1666 : else
1667 : {
1668 : // or relative to the /xl subdirectory
1669 147 : osFilename = osPrefixedFilename + CPLString("/xl/") + osTarget;
1670 : }
1671 298 : papoLayers = (OGRXLSXLayer **)CPLRealloc(
1672 149 : papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
1673 149 : papoLayers[nLayers++] =
1674 149 : new OGRXLSXLayer(this, osFilename, pszSheetName);
1675 : }
1676 : }
1677 : }
1678 :
1679 : /************************************************************************/
1680 : /* AnalyseWorkbook() */
1681 : /************************************************************************/
1682 :
1683 34 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE *fpWorkbook)
1684 : {
1685 34 : oParser = OGRCreateExpatXMLParser();
1686 34 : XML_SetElementHandler(oParser, OGRXLSX::startElementWBCbk, nullptr);
1687 34 : XML_SetUserData(oParser, this);
1688 :
1689 34 : VSIFSeekL(fpWorkbook, 0, SEEK_SET);
1690 :
1691 34 : bStopParsing = false;
1692 34 : nWithoutEventCounter = 0;
1693 34 : nDataHandlerCounter = 0;
1694 :
1695 68 : std::vector<char> aBuf(PARSER_BUF_SIZE);
1696 34 : int nDone = 0;
1697 0 : do
1698 : {
1699 34 : nDataHandlerCounter = 0;
1700 : unsigned int nLen =
1701 34 : (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpWorkbook);
1702 34 : nDone = (nLen < aBuf.size());
1703 34 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1704 : {
1705 0 : CPLError(CE_Failure, CPLE_AppDefined,
1706 : "XML parsing of %s file failed : %s at line %d, column %d",
1707 : "workbook.xml", XML_ErrorString(XML_GetErrorCode(oParser)),
1708 0 : (int)XML_GetCurrentLineNumber(oParser),
1709 0 : (int)XML_GetCurrentColumnNumber(oParser));
1710 0 : bStopParsing = true;
1711 : }
1712 34 : nWithoutEventCounter++;
1713 34 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1714 :
1715 34 : XML_ParserFree(oParser);
1716 34 : oParser = nullptr;
1717 :
1718 34 : if (nWithoutEventCounter == 10)
1719 : {
1720 0 : CPLError(CE_Failure, CPLE_AppDefined,
1721 : "Too much data inside one element. File probably corrupted");
1722 0 : bStopParsing = true;
1723 : }
1724 :
1725 34 : VSIFCloseL(fpWorkbook);
1726 34 : }
1727 :
1728 : /************************************************************************/
1729 : /* startElementStylesCbk() */
1730 : /************************************************************************/
1731 :
1732 1847 : static void XMLCALL startElementStylesCbk(void *pUserData,
1733 : const char *pszNameIn,
1734 : const char **ppszAttr)
1735 : {
1736 : ((OGRXLSXDataSource *)pUserData)
1737 1847 : ->startElementStylesCbk(pszNameIn, ppszAttr);
1738 1847 : }
1739 :
1740 1847 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszNameIn,
1741 : const char **ppszAttr)
1742 : {
1743 1847 : if (bStopParsing)
1744 0 : return;
1745 :
1746 1847 : pszNameIn = GetUnprefixed(pszNameIn);
1747 :
1748 1847 : nWithoutEventCounter = 0;
1749 1847 : if (strcmp(pszNameIn, "numFmt") == 0)
1750 : {
1751 : const char *pszFormatCode =
1752 176 : GetAttributeValue(ppszAttr, "formatCode", nullptr);
1753 176 : const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1754 176 : int nNumFmtId = atoi(pszNumFmtId);
1755 176 : if (pszFormatCode && nNumFmtId >= 164)
1756 : {
1757 465 : int bHasDate = strstr(pszFormatCode, "DD") != nullptr ||
1758 113 : strstr(pszFormatCode, "dd") != nullptr ||
1759 402 : strstr(pszFormatCode, "YY") != nullptr ||
1760 113 : strstr(pszFormatCode, "yy") != nullptr;
1761 291 : int bHasTime = strstr(pszFormatCode, "HH") != nullptr ||
1762 115 : strstr(pszFormatCode, "hh") != nullptr;
1763 176 : if (bHasDate && bHasTime)
1764 37 : apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(
1765 : OFTDateTime,
1766 62 : strstr(pszFormatCode, "SS.000") != nullptr ||
1767 25 : strstr(pszFormatCode, "ss.000") != nullptr);
1768 139 : else if (bHasDate)
1769 26 : apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDate);
1770 113 : else if (bHasTime)
1771 24 : apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTTime);
1772 : else
1773 89 : apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTReal);
1774 : }
1775 : }
1776 1671 : else if (strcmp(pszNameIn, "cellXfs") == 0)
1777 : {
1778 32 : bInCellXFS = true;
1779 : }
1780 1639 : else if (bInCellXFS && strcmp(pszNameIn, "xf") == 0)
1781 : {
1782 219 : const char *pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1783 219 : int nNumFmtId = atoi(pszNumFmtId);
1784 219 : XLSXFieldTypeExtended eType(OFTReal);
1785 219 : if (nNumFmtId >= 0)
1786 : {
1787 219 : if (nNumFmtId < 164)
1788 : {
1789 : // From
1790 : // http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
1791 7 : if (nNumFmtId >= 14 && nNumFmtId <= 17)
1792 0 : eType = XLSXFieldTypeExtended(OFTDate);
1793 7 : else if (nNumFmtId >= 18 && nNumFmtId <= 21)
1794 0 : eType = XLSXFieldTypeExtended(OFTTime);
1795 7 : else if (nNumFmtId == 22)
1796 0 : eType = XLSXFieldTypeExtended(OFTDateTime);
1797 : }
1798 : else
1799 : {
1800 : std::map<int, XLSXFieldTypeExtended>::iterator oIter =
1801 212 : apoMapStyleFormats.find(nNumFmtId);
1802 212 : if (oIter != apoMapStyleFormats.end())
1803 212 : eType = oIter->second;
1804 : else
1805 0 : CPLDebug("XLSX",
1806 : "Cannot find entry in <numFmts> with numFmtId=%d",
1807 : nNumFmtId);
1808 : }
1809 : }
1810 : #if DEBUG_VERBOSE
1811 : printf("style[%lu] = %d\n", /*ok*/
1812 : apoStyles.size(), static_cast<int>(eType.eType));
1813 : #endif
1814 :
1815 219 : apoStyles.push_back(eType);
1816 : }
1817 : }
1818 :
1819 : /************************************************************************/
1820 : /* endElementStylesCbk() */
1821 : /************************************************************************/
1822 :
1823 1847 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszNameIn)
1824 : {
1825 1847 : ((OGRXLSXDataSource *)pUserData)->endElementStylesCbk(pszNameIn);
1826 1847 : }
1827 :
1828 1847 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszNameIn)
1829 : {
1830 1847 : if (bStopParsing)
1831 0 : return;
1832 :
1833 1847 : pszNameIn = GetUnprefixed(pszNameIn);
1834 :
1835 1847 : nWithoutEventCounter = 0;
1836 1847 : if (strcmp(pszNameIn, "cellXfs") == 0)
1837 : {
1838 32 : bInCellXFS = false;
1839 : }
1840 : }
1841 :
1842 : /************************************************************************/
1843 : /* AnalyseStyles() */
1844 : /************************************************************************/
1845 :
1846 34 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE *fpStyles)
1847 : {
1848 34 : if (fpStyles == nullptr)
1849 2 : return;
1850 :
1851 32 : oParser = OGRCreateExpatXMLParser();
1852 32 : XML_SetElementHandler(oParser, OGRXLSX::startElementStylesCbk,
1853 : OGRXLSX::endElementStylesCbk);
1854 32 : XML_SetUserData(oParser, this);
1855 :
1856 32 : VSIFSeekL(fpStyles, 0, SEEK_SET);
1857 :
1858 32 : bStopParsing = false;
1859 32 : nWithoutEventCounter = 0;
1860 32 : nDataHandlerCounter = 0;
1861 32 : bInCellXFS = false;
1862 :
1863 64 : std::vector<char> aBuf(PARSER_BUF_SIZE);
1864 32 : int nDone = 0;
1865 0 : do
1866 : {
1867 32 : nDataHandlerCounter = 0;
1868 : unsigned int nLen =
1869 32 : (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpStyles);
1870 32 : nDone = (nLen < aBuf.size());
1871 32 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
1872 : {
1873 0 : CPLError(CE_Failure, CPLE_AppDefined,
1874 : "XML parsing of %s file failed : %s at line %d, column %d",
1875 : "styles.xml", XML_ErrorString(XML_GetErrorCode(oParser)),
1876 0 : (int)XML_GetCurrentLineNumber(oParser),
1877 0 : (int)XML_GetCurrentColumnNumber(oParser));
1878 0 : bStopParsing = true;
1879 : }
1880 32 : nWithoutEventCounter++;
1881 32 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1882 :
1883 32 : XML_ParserFree(oParser);
1884 32 : oParser = nullptr;
1885 :
1886 32 : if (nWithoutEventCounter == 10)
1887 : {
1888 0 : CPLError(CE_Failure, CPLE_AppDefined,
1889 : "Too much data inside one element. File probably corrupted");
1890 0 : bStopParsing = true;
1891 : }
1892 :
1893 32 : VSIFCloseL(fpStyles);
1894 : }
1895 :
1896 : /************************************************************************/
1897 : /* ICreateLayer() */
1898 : /************************************************************************/
1899 :
1900 : OGRLayer *
1901 18 : OGRXLSXDataSource::ICreateLayer(const char *pszLayerName,
1902 : const OGRGeomFieldDefn * /*poGeomFieldDefn*/,
1903 : CSLConstList papszOptions)
1904 :
1905 : {
1906 : /* -------------------------------------------------------------------- */
1907 : /* Verify we are in update mode. */
1908 : /* -------------------------------------------------------------------- */
1909 18 : if (!bUpdatable)
1910 : {
1911 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1912 : "Data source %s opened read-only.\n"
1913 : "New layer %s cannot be created.\n",
1914 : pszName, pszLayerName);
1915 :
1916 0 : return nullptr;
1917 : }
1918 :
1919 : /* -------------------------------------------------------------------- */
1920 : /* Do we already have this layer? If so, should we blow it */
1921 : /* away? */
1922 : /* -------------------------------------------------------------------- */
1923 50 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
1924 : {
1925 32 : if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
1926 : {
1927 0 : if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
1928 0 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
1929 : {
1930 0 : DeleteLayer(pszLayerName);
1931 : }
1932 : else
1933 : {
1934 0 : CPLError(CE_Failure, CPLE_AppDefined,
1935 : "Layer %s already exists, CreateLayer failed.\n"
1936 : "Use the layer creation option OVERWRITE=YES to "
1937 : "replace it.",
1938 : pszLayerName);
1939 0 : return nullptr;
1940 : }
1941 : }
1942 : }
1943 :
1944 : /* -------------------------------------------------------------------- */
1945 : /* Create the layer object. */
1946 : /* -------------------------------------------------------------------- */
1947 : OGRXLSXLayer *poLayer =
1948 : new OGRXLSXLayer(this,
1949 36 : CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
1950 18 : pszName, nLayers + 1),
1951 18 : pszLayerName, TRUE);
1952 :
1953 36 : papoLayers = (OGRXLSXLayer **)CPLRealloc(
1954 18 : papoLayers, (nLayers + 1) * sizeof(OGRXLSXLayer *));
1955 18 : papoLayers[nLayers] = poLayer;
1956 18 : nLayers++;
1957 :
1958 18 : bUpdated = true;
1959 :
1960 18 : return poLayer;
1961 : }
1962 :
1963 : /************************************************************************/
1964 : /* DeleteLayer() */
1965 : /************************************************************************/
1966 :
1967 0 : void OGRXLSXDataSource::DeleteLayer(const char *pszLayerName)
1968 :
1969 : {
1970 : /* -------------------------------------------------------------------- */
1971 : /* Verify we are in update mode. */
1972 : /* -------------------------------------------------------------------- */
1973 0 : if (!bUpdatable)
1974 : {
1975 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1976 : "Data source %s opened read-only.\n"
1977 : "Layer %s cannot be deleted.\n",
1978 : pszName, pszLayerName);
1979 :
1980 0 : return;
1981 : }
1982 :
1983 : /* -------------------------------------------------------------------- */
1984 : /* Try to find layer. */
1985 : /* -------------------------------------------------------------------- */
1986 0 : int iLayer = 0;
1987 0 : for (; iLayer < nLayers; iLayer++)
1988 : {
1989 0 : if (EQUAL(pszLayerName, papoLayers[iLayer]->GetName()))
1990 0 : break;
1991 : }
1992 :
1993 0 : if (iLayer == nLayers)
1994 : {
1995 0 : CPLError(
1996 : CE_Failure, CPLE_AppDefined,
1997 : "Attempt to delete layer '%s', but this layer is not known to OGR.",
1998 : pszLayerName);
1999 0 : return;
2000 : }
2001 :
2002 0 : DeleteLayer(iLayer);
2003 : }
2004 :
2005 : /************************************************************************/
2006 : /* DeleteLayer() */
2007 : /************************************************************************/
2008 :
2009 0 : OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
2010 : {
2011 0 : if (iLayer < 0 || iLayer >= nLayers)
2012 : {
2013 0 : CPLError(CE_Failure, CPLE_AppDefined,
2014 : "Layer %d not in legal range of 0 to %d.", iLayer,
2015 0 : nLayers - 1);
2016 0 : return OGRERR_FAILURE;
2017 : }
2018 :
2019 : /* -------------------------------------------------------------------- */
2020 : /* Blow away our OGR structures related to the layer. This is */
2021 : /* pretty dangerous if anything has a reference to this layer! */
2022 : /* -------------------------------------------------------------------- */
2023 :
2024 0 : delete papoLayers[iLayer];
2025 0 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
2026 0 : sizeof(void *) * (nLayers - iLayer - 1));
2027 0 : nLayers--;
2028 :
2029 0 : bUpdated = true;
2030 :
2031 0 : return OGRERR_NONE;
2032 : }
2033 :
2034 : /************************************************************************/
2035 : /* WriteOverride() */
2036 : /************************************************************************/
2037 :
2038 114 : static void WriteOverride(VSILFILE *fp, const char *pszPartName,
2039 : const char *pszContentType)
2040 : {
2041 114 : VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
2042 : pszPartName, pszContentType);
2043 114 : }
2044 :
2045 : static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2046 : static const char MAIN_NS[] =
2047 : "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"";
2048 : static const char SCHEMA_OD[] =
2049 : "http://schemas.openxmlformats.org/officeDocument/2006";
2050 : static const char SCHEMA_OD_RS[] =
2051 : "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
2052 : static const char SCHEMA_PACKAGE[] =
2053 : "http://schemas.openxmlformats.org/package/2006";
2054 : static const char SCHEMA_PACKAGE_RS[] =
2055 : "http://schemas.openxmlformats.org/package/2006/relationships";
2056 :
2057 : /************************************************************************/
2058 : /* WriteContentTypes() */
2059 : /************************************************************************/
2060 :
2061 12 : static bool WriteContentTypes(const char *pszName, int nLayers)
2062 : {
2063 : CPLString osTmpFilename(
2064 24 : CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName));
2065 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2066 12 : if (!fp)
2067 0 : return false;
2068 : // TODO(schwehr): Convert all strlen(XML_HEADER) to constexpr with
2069 : // switch to C++11 or newer.
2070 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2071 12 : VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
2072 12 : WriteOverride(fp, "/_rels/.rels",
2073 : "application/vnd.openxmlformats-package.relationships+xml");
2074 12 : WriteOverride(fp, "/docProps/core.xml",
2075 : "application/vnd.openxmlformats-package.core-properties+xml");
2076 12 : WriteOverride(fp, "/docProps/app.xml",
2077 : "application/"
2078 : "vnd.openxmlformats-officedocument.extended-properties+xml");
2079 12 : WriteOverride(fp, "/xl/_rels/workbook.xml.rels",
2080 : "application/vnd.openxmlformats-package.relationships+xml");
2081 42 : for (int i = 0; i < nLayers; i++)
2082 : {
2083 30 : WriteOverride(
2084 : fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i + 1),
2085 : "application/"
2086 : "vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
2087 : }
2088 12 : WriteOverride(fp, "/xl/styles.xml",
2089 : "application/"
2090 : "vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
2091 12 : WriteOverride(
2092 : fp, "/xl/workbook.xml",
2093 : "application/"
2094 : "vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
2095 12 : WriteOverride(
2096 : fp, "/xl/sharedStrings.xml",
2097 : "application/"
2098 : "vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
2099 12 : VSIFPrintfL(fp, "</Types>\n");
2100 12 : VSIFCloseL(fp);
2101 12 : return true;
2102 : }
2103 :
2104 : /************************************************************************/
2105 : /* WriteApp() */
2106 : /************************************************************************/
2107 :
2108 12 : static bool WriteApp(const char *pszName)
2109 : {
2110 24 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName));
2111 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2112 12 : if (!fp)
2113 0 : return false;
2114 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2115 12 : VSIFPrintfL(fp,
2116 : "<Properties xmlns=\"%s/extended-properties\" "
2117 : "xmlns:vt=\"%s/docPropsVTypes\">\n",
2118 : SCHEMA_OD, SCHEMA_OD);
2119 12 : VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
2120 12 : VSIFPrintfL(fp, "</Properties>\n");
2121 12 : VSIFCloseL(fp);
2122 12 : return true;
2123 : }
2124 :
2125 : /************************************************************************/
2126 : /* WriteCore() */
2127 : /************************************************************************/
2128 :
2129 12 : static bool WriteCore(const char *pszName)
2130 : {
2131 : CPLString osTmpFilename(
2132 24 : CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName));
2133 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2134 12 : if (!fp)
2135 0 : return false;
2136 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2137 12 : VSIFPrintfL(fp,
2138 : "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
2139 : "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
2140 : "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
2141 : "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
2142 : "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n",
2143 : SCHEMA_PACKAGE);
2144 12 : VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
2145 12 : VSIFPrintfL(fp, "</cp:coreProperties>\n");
2146 12 : VSIFCloseL(fp);
2147 12 : return true;
2148 : }
2149 :
2150 : /************************************************************************/
2151 : /* WriteWorkbook() */
2152 : /************************************************************************/
2153 :
2154 12 : static bool WriteWorkbook(const char *pszName, GDALDataset *poDS)
2155 : {
2156 24 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName));
2157 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2158 12 : if (!fp)
2159 0 : return false;
2160 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2161 12 : VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
2162 12 : VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
2163 : /*
2164 : VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\"
2165 : date1904=\"false\"/>\n"); VSIFPrintfL(fp, "<workbookProtection/>\n");
2166 : VSIFPrintfL(fp, "<bookViews>\n");
2167 : VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\"
2168 : showHorizontalScroll=\"true\" " "showSheetTabs=\"true\"
2169 : showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
2170 : "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
2171 : VSIFPrintfL(fp, "</bookViews>\n");
2172 : */
2173 12 : VSIFPrintfL(fp, "<sheets>\n");
2174 42 : for (int i = 0; i < poDS->GetLayerCount(); i++)
2175 : {
2176 30 : auto poLayer = poDS->GetLayer(i);
2177 30 : const char *pszLayerName = poLayer->GetName();
2178 30 : char *pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
2179 30 : VSIFPrintfL(fp,
2180 : "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" "
2181 : "r:id=\"rId%d\"/>\n",
2182 : pszXML, i + 1, i + 2);
2183 30 : CPLFree(pszXML);
2184 : }
2185 12 : VSIFPrintfL(fp, "</sheets>\n");
2186 12 : VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" "
2187 : "iterate=\"false\" iterateDelta=\"0.001\"/>\n");
2188 12 : VSIFPrintfL(fp, "</workbook>\n");
2189 12 : VSIFCloseL(fp);
2190 12 : return true;
2191 : }
2192 :
2193 : /************************************************************************/
2194 : /* BuildColString() */
2195 : /************************************************************************/
2196 :
2197 338 : static CPLString BuildColString(int nCol)
2198 : {
2199 : /*
2200 : A Z AA AZ BA BZ ZA ZZ AAA ZZZ AAAA
2201 : 0 25 26 51 52 77 676 701 702 18277 18278
2202 : */
2203 338 : CPLString osRet;
2204 338 : osRet += (nCol % 26) + 'A';
2205 342 : while (nCol >= 26)
2206 : {
2207 4 : nCol /= 26;
2208 : // We would not need a decrement if this was a proper base 26
2209 : // numeration scheme.
2210 4 : nCol--;
2211 4 : osRet += (nCol % 26) + 'A';
2212 : }
2213 338 : const size_t nSize = osRet.size();
2214 342 : for (size_t l = 0; l < nSize / 2; l++)
2215 : {
2216 4 : char chTmp = osRet[nSize - 1 - l];
2217 4 : osRet[nSize - 1 - l] = osRet[l];
2218 4 : osRet[l] = chTmp;
2219 : }
2220 338 : return osRet;
2221 : }
2222 :
2223 : /************************************************************************/
2224 : /* WriteLayer() */
2225 : /************************************************************************/
2226 :
2227 30 : static bool WriteLayer(const char *pszName, OGRXLSXLayer *poLayer, int iLayer,
2228 : std::map<std::string, int> &oStringMap,
2229 : std::vector<std::string> &oStringList)
2230 : {
2231 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml",
2232 60 : pszName, iLayer + 1));
2233 30 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2234 30 : if (!fp)
2235 0 : return false;
2236 30 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2237 30 : VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
2238 : /*
2239 : VSIFPrintfL(fp, "<sheetViews>\n");
2240 : VSIFPrintfL(fp, "<sheetView colorId=\"64\" defaultGridColor=\"true\"
2241 : rightToLeft=\"false\" showFormulas=\"false\" showGridLines=\"true\"
2242 : showOutlineSymbols=\"true\" showRowColHeaders=\"true\" showZeros=\"true\"
2243 : tabSelected=\"%s\" topLeftCell=\"A1\" view=\"normal\"
2244 : windowProtection=\"false\" workbookViewId=\"0\" zoomScale=\"100\"
2245 : zoomScaleNormal=\"100\" zoomScalePageLayoutView=\"60\">\n", (i == 0) ?
2246 : "true" : "false"); VSIFPrintfL(fp, "<selection activeCell=\"A1\"
2247 : activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n"); VSIFPrintfL(fp,
2248 : "</sheetView>\n"); VSIFPrintfL(fp, "</sheetViews>\n");*/
2249 :
2250 30 : poLayer->ResetReading();
2251 :
2252 30 : OGRFeature *poFeature = poLayer->GetNextFeature();
2253 :
2254 30 : const OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
2255 30 : bool bHasHeaders = false;
2256 30 : int iRow = 1;
2257 :
2258 30 : const int nFields = poFDefn->GetFieldCount();
2259 30 : if (nFields > 0)
2260 : {
2261 28 : VSIFPrintfL(fp, "<cols>\n");
2262 141 : for (int j = 0; j < nFields; j++)
2263 : {
2264 113 : int nWidth = 15;
2265 113 : if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
2266 9 : nWidth = 29;
2267 113 : VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
2268 : j + 1, 1024, nWidth);
2269 :
2270 113 : if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
2271 113 : CPLSPrintf("Field%d", j + 1)) != 0)
2272 42 : bHasHeaders = true;
2273 : }
2274 28 : VSIFPrintfL(fp, "</cols>\n");
2275 : }
2276 2 : else if (!poLayer->GetCols().empty())
2277 2 : VSIFPrintfL(fp, "%s\n", poLayer->GetCols().c_str());
2278 :
2279 30 : VSIFPrintfL(fp, "<sheetData>\n");
2280 :
2281 30 : if (bHasHeaders && poFeature != nullptr)
2282 : {
2283 8 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
2284 48 : for (int j = 0; j < nFields; j++)
2285 : {
2286 40 : const char *pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
2287 : std::map<std::string, int>::iterator oIter =
2288 40 : oStringMap.find(pszVal);
2289 40 : int nStringIndex = 0;
2290 40 : if (oIter != oStringMap.end())
2291 3 : nStringIndex = oIter->second;
2292 : else
2293 : {
2294 37 : nStringIndex = (int)oStringList.size();
2295 37 : oStringMap[pszVal] = nStringIndex;
2296 37 : oStringList.push_back(pszVal);
2297 : }
2298 :
2299 80 : CPLString osCol = BuildColString(j);
2300 :
2301 40 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(), iRow);
2302 40 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
2303 40 : VSIFPrintfL(fp, "</c>\n");
2304 : }
2305 8 : VSIFPrintfL(fp, "</row>\n");
2306 :
2307 8 : iRow++;
2308 : }
2309 :
2310 173 : while (poFeature != nullptr)
2311 : {
2312 143 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
2313 755 : for (int j = 0; j < nFields; j++)
2314 : {
2315 612 : if (poFeature->IsFieldSetAndNotNull(j))
2316 : {
2317 596 : CPLString osCol = BuildColString(j);
2318 :
2319 298 : const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(j);
2320 298 : OGRFieldType eType = poFieldDefn->GetType();
2321 :
2322 298 : if (eType == OFTReal)
2323 : {
2324 66 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
2325 66 : VSIFPrintfL(fp, "<v>%.16g</v>\n",
2326 : poFeature->GetFieldAsDouble(j));
2327 66 : VSIFPrintfL(fp, "</c>\n");
2328 : }
2329 232 : else if (eType == OFTInteger)
2330 : {
2331 34 : OGRFieldSubType eSubType = poFieldDefn->GetSubType();
2332 34 : if (eSubType == OFSTBoolean)
2333 1 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"b\" s=\"5\">\n",
2334 : osCol.c_str(), iRow);
2335 : else
2336 33 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(),
2337 : iRow);
2338 34 : VSIFPrintfL(fp, "<v>%d</v>\n",
2339 : poFeature->GetFieldAsInteger(j));
2340 34 : VSIFPrintfL(fp, "</c>\n");
2341 : }
2342 198 : else if (eType == OFTInteger64)
2343 : {
2344 33 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", osCol.c_str(), iRow);
2345 33 : VSIFPrintfL(fp, "<v>" CPL_FRMT_GIB "</v>\n",
2346 : poFeature->GetFieldAsInteger64(j));
2347 33 : VSIFPrintfL(fp, "</c>\n");
2348 : }
2349 165 : else if (eType == OFTDate || eType == OFTDateTime ||
2350 : eType == OFTTime)
2351 : {
2352 15 : int nYear = 0;
2353 15 : int nMonth = 0;
2354 15 : int nDay = 0;
2355 15 : int nHour = 0;
2356 15 : int nMinute = 0;
2357 15 : int nTZFlag = 0;
2358 15 : float fSecond = 0.0f;
2359 15 : poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
2360 : &nHour, &nMinute, &fSecond,
2361 : &nTZFlag);
2362 : struct tm brokendowntime;
2363 15 : memset(&brokendowntime, 0, sizeof(brokendowntime));
2364 15 : brokendowntime.tm_year =
2365 15 : (eType == OFTTime) ? 70 : nYear - 1900;
2366 15 : brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
2367 15 : brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
2368 15 : brokendowntime.tm_hour = nHour;
2369 15 : brokendowntime.tm_min = nMinute;
2370 15 : brokendowntime.tm_sec = (int)fSecond;
2371 15 : GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
2372 15 : double dfNumberOfDaysSince1900 =
2373 15 : (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
2374 15 : dfNumberOfDaysSince1900 +=
2375 15 : fmod(fSecond, 1) / NUMBER_OF_SECONDS_PER_DAY;
2376 28 : int s = (eType == OFTDate) ? 1
2377 13 : : (eType == OFTDateTime) ? 2
2378 : : 3;
2379 15 : if (eType == OFTDateTime && OGR_GET_MS(fSecond))
2380 1 : s = 4;
2381 15 : VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", osCol.c_str(),
2382 : iRow, s);
2383 15 : if (eType != OFTTime)
2384 13 : dfNumberOfDaysSince1900 +=
2385 : NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
2386 15 : if (eType == OFTDate)
2387 2 : VSIFPrintfL(fp, "<v>%d</v>\n",
2388 2 : (int)(dfNumberOfDaysSince1900 + 0.1));
2389 : else
2390 13 : VSIFPrintfL(fp, "<v>%.16g</v>\n",
2391 : dfNumberOfDaysSince1900);
2392 15 : VSIFPrintfL(fp, "</c>\n");
2393 : }
2394 : else
2395 : {
2396 150 : const char *pszVal = poFeature->GetFieldAsString(j);
2397 : std::map<std::string, int>::iterator oIter =
2398 150 : oStringMap.find(pszVal);
2399 150 : int nStringIndex = 0;
2400 150 : if (oIter != oStringMap.end())
2401 38 : nStringIndex = oIter->second;
2402 : else
2403 : {
2404 112 : nStringIndex = (int)oStringList.size();
2405 112 : oStringMap[pszVal] = nStringIndex;
2406 112 : oStringList.push_back(pszVal);
2407 : }
2408 150 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", osCol.c_str(),
2409 : iRow);
2410 150 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
2411 150 : VSIFPrintfL(fp, "</c>\n");
2412 : }
2413 : }
2414 : }
2415 143 : VSIFPrintfL(fp, "</row>\n");
2416 :
2417 143 : iRow++;
2418 143 : delete poFeature;
2419 143 : poFeature = poLayer->GetNextFeature();
2420 : }
2421 30 : VSIFPrintfL(fp, "</sheetData>\n");
2422 30 : VSIFPrintfL(fp, "</worksheet>\n");
2423 30 : VSIFCloseL(fp);
2424 30 : return true;
2425 : }
2426 :
2427 : /************************************************************************/
2428 : /* WriteSharedStrings() */
2429 : /************************************************************************/
2430 :
2431 12 : static bool WriteSharedStrings(const char *pszName,
2432 : std::vector<std::string> &oStringList)
2433 : {
2434 : CPLString osTmpFilename(
2435 24 : CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName));
2436 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2437 12 : if (!fp)
2438 0 : return false;
2439 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2440 12 : VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n", MAIN_NS,
2441 12 : (int)oStringList.size());
2442 161 : for (int i = 0; i < (int)oStringList.size(); i++)
2443 : {
2444 149 : VSIFPrintfL(fp, "<si>\n");
2445 149 : char *pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
2446 149 : VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
2447 149 : CPLFree(pszXML);
2448 149 : VSIFPrintfL(fp, "</si>\n");
2449 : }
2450 12 : VSIFPrintfL(fp, "</sst>\n");
2451 12 : VSIFCloseL(fp);
2452 12 : return true;
2453 : }
2454 :
2455 : /************************************************************************/
2456 : /* WriteStyles() */
2457 : /************************************************************************/
2458 :
2459 12 : static bool WriteStyles(const char *pszName)
2460 : {
2461 24 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName));
2462 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2463 12 : if (!fp)
2464 0 : return false;
2465 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2466 12 : VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
2467 12 : VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
2468 12 : VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
2469 12 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
2470 12 : VSIFPrintfL(
2471 : fp,
2472 : "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
2473 12 : VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
2474 12 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS.000\" "
2475 : "numFmtId=\"168\"/>\n");
2476 12 : VSIFPrintfL(fp, "<numFmt "
2477 : "formatCode=\""TRUE";"TRUE";""
2478 : "FALSE"\" numFmtId=\"169\"/>\n");
2479 12 : VSIFPrintfL(fp, "</numFmts>\n");
2480 12 : VSIFPrintfL(fp, "<fonts count=\"1\">\n");
2481 12 : VSIFPrintfL(fp, "<font>\n");
2482 12 : VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
2483 12 : VSIFPrintfL(fp, "<family val=\"2\"/>\n");
2484 12 : VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
2485 12 : VSIFPrintfL(fp, "</font>\n");
2486 12 : VSIFPrintfL(fp, "</fonts>\n");
2487 12 : VSIFPrintfL(fp, "<fills count=\"1\">\n");
2488 12 : VSIFPrintfL(fp, "<fill>\n");
2489 12 : VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
2490 12 : VSIFPrintfL(fp, "</fill>\n");
2491 12 : VSIFPrintfL(fp, "</fills>\n");
2492 12 : VSIFPrintfL(fp, "<borders count=\"1\">\n");
2493 12 : VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
2494 12 : VSIFPrintfL(fp, "<left/>\n");
2495 12 : VSIFPrintfL(fp, "<right/>\n");
2496 12 : VSIFPrintfL(fp, "<top/>\n");
2497 12 : VSIFPrintfL(fp, "<bottom/>\n");
2498 12 : VSIFPrintfL(fp, "<diagonal/>\n");
2499 12 : VSIFPrintfL(fp, "</border>\n");
2500 12 : VSIFPrintfL(fp, "</borders>\n");
2501 12 : VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
2502 12 : VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
2503 12 : VSIFPrintfL(fp, "</xf>\n");
2504 12 : VSIFPrintfL(fp, "</cellStyleXfs>\n");
2505 12 : VSIFPrintfL(fp, "<cellXfs count=\"6\">\n");
2506 12 : VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
2507 12 : VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
2508 12 : VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
2509 12 : VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
2510 12 : VSIFPrintfL(fp, "<xf numFmtId=\"168\" xfId=\"0\"/>\n");
2511 12 : VSIFPrintfL(fp, "<xf numFmtId=\"169\" xfId=\"0\"/>\n");
2512 12 : VSIFPrintfL(fp, "</cellXfs>\n");
2513 12 : VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
2514 12 : VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" "
2515 : "name=\"Normal\" xfId=\"0\"/>\n");
2516 12 : VSIFPrintfL(fp, "</cellStyles>\n");
2517 12 : VSIFPrintfL(fp, "</styleSheet>\n");
2518 12 : VSIFCloseL(fp);
2519 12 : return true;
2520 : }
2521 :
2522 : /************************************************************************/
2523 : /* WriteWorkbookRels() */
2524 : /************************************************************************/
2525 :
2526 12 : static bool WriteWorkbookRels(const char *pszName, int nLayers)
2527 : {
2528 : CPLString osTmpFilename(
2529 24 : CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName));
2530 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2531 12 : if (!fp)
2532 0 : return false;
2533 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2534 12 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
2535 12 : VSIFPrintfL(fp,
2536 : "<Relationship Id=\"rId1\" Type=\"%s/styles\" "
2537 : "Target=\"styles.xml\"/>\n",
2538 : SCHEMA_OD_RS);
2539 42 : for (int i = 0; i < nLayers; i++)
2540 : {
2541 30 : VSIFPrintfL(fp,
2542 : "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" "
2543 : "Target=\"worksheets/sheet%d.xml\"/>\n",
2544 : 2 + i, SCHEMA_OD_RS, 1 + i);
2545 : }
2546 12 : VSIFPrintfL(fp,
2547 : "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" "
2548 : "Target=\"sharedStrings.xml\"/>\n",
2549 : 2 + nLayers, SCHEMA_OD_RS);
2550 12 : VSIFPrintfL(fp, "</Relationships>\n");
2551 12 : VSIFCloseL(fp);
2552 12 : return true;
2553 : }
2554 :
2555 : /************************************************************************/
2556 : /* WriteDotRels() */
2557 : /************************************************************************/
2558 :
2559 12 : static bool WriteDotRels(const char *pszName)
2560 : {
2561 24 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName));
2562 12 : VSILFILE *fp = VSIFOpenL(osTmpFilename, "wb");
2563 12 : if (!fp)
2564 0 : return false;
2565 12 : VSIFWriteL(XML_HEADER, strlen(XML_HEADER), 1, fp);
2566 12 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
2567 12 : VSIFPrintfL(fp,
2568 : "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" "
2569 : "Target=\"xl/workbook.xml\"/>\n",
2570 : SCHEMA_OD_RS);
2571 12 : VSIFPrintfL(
2572 : fp,
2573 : "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" "
2574 : "Target=\"docProps/core.xml\"/>\n",
2575 : SCHEMA_PACKAGE_RS);
2576 12 : VSIFPrintfL(fp,
2577 : "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" "
2578 : "Target=\"docProps/app.xml\"/>\n",
2579 : SCHEMA_OD_RS);
2580 12 : VSIFPrintfL(fp, "</Relationships>\n");
2581 12 : VSIFCloseL(fp);
2582 12 : return true;
2583 : }
2584 :
2585 : /************************************************************************/
2586 : /* FlushCache() */
2587 : /************************************************************************/
2588 :
2589 47 : CPLErr OGRXLSXDataSource::FlushCache(bool /* bAtClosing */)
2590 : {
2591 47 : if (!bUpdated)
2592 35 : return CE_None;
2593 :
2594 : /* Cause all layers to be loaded */
2595 42 : for (int i = 0; i < nLayers; i++)
2596 : {
2597 30 : ((OGRXLSXLayer *)papoLayers[i])->GetLayerDefn();
2598 : }
2599 :
2600 : VSIStatBufL sStat;
2601 12 : if (VSIStatL(pszName, &sStat) == 0)
2602 : {
2603 4 : if (VSIUnlink(pszName) != 0)
2604 : {
2605 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s", pszName);
2606 0 : return CE_Failure;
2607 : }
2608 : }
2609 :
2610 24 : CPLConfigOptionSetter oZip64Disable("CPL_CREATE_ZIP64", "NO", false);
2611 :
2612 : /* Maintain new ZIP files opened */
2613 24 : CPLString osTmpFilename(CPLSPrintf("/vsizip/%s", pszName));
2614 12 : VSILFILE *fpZIP = VSIFOpenExL(osTmpFilename, "wb", true);
2615 12 : if (fpZIP == nullptr)
2616 : {
2617 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszName,
2618 : VSIGetLastErrorMsg());
2619 0 : return CE_Failure;
2620 : }
2621 :
2622 12 : bool bOK = WriteContentTypes(pszName, nLayers);
2623 :
2624 : // VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
2625 12 : bOK &= WriteApp(pszName);
2626 12 : bOK &= WriteCore(pszName);
2627 :
2628 : // VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
2629 12 : bOK &= WriteWorkbook(pszName, this);
2630 :
2631 24 : std::map<std::string, int> oStringMap;
2632 12 : std::vector<std::string> oStringList;
2633 :
2634 : // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
2635 42 : for (int i = 0; i < nLayers; i++)
2636 : {
2637 30 : bOK &= WriteLayer(pszName, papoLayers[i], i, oStringMap, oStringList);
2638 : }
2639 :
2640 12 : bOK &= WriteSharedStrings(pszName, oStringList);
2641 12 : bOK &= WriteStyles(pszName);
2642 :
2643 : // VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
2644 12 : bOK &= WriteWorkbookRels(pszName, nLayers);
2645 :
2646 : // VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
2647 12 : bOK &= WriteDotRels(pszName);
2648 :
2649 : /* Now close ZIP file */
2650 12 : if (VSIFCloseL(fpZIP) != 0)
2651 0 : bOK = false;
2652 :
2653 : /* Reset updated flag at datasource and layer level */
2654 12 : bUpdated = false;
2655 42 : for (int i = 0; i < nLayers; i++)
2656 : {
2657 30 : ((OGRXLSXLayer *)papoLayers[i])->SetUpdated(false);
2658 : }
2659 :
2660 12 : if (!bOK)
2661 : {
2662 0 : CPLError(CE_Failure, CPLE_FileIO, "Failure when saving %s", pszName);
2663 : }
2664 :
2665 12 : return bOK ? CE_None : CE_Failure;
2666 : }
2667 :
2668 : } // namespace OGRXLSX
|