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