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