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