1 : /******************************************************************************
2 : * $Id: ogrxlsxdatasource.cpp 24173 2012-03-29 21:09:52Z rouault $
3 : *
4 : * Project: XLSX Translator
5 : * Purpose: Implements OGRXLSXDataSource class
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2012, Even Rouault <even dot rouault at mines dash paris dot org>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_xlsx.h"
31 : #include "ogr_p.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_time.h"
34 :
35 : CPL_CVSID("$Id: ogrxlsxdatasource.cpp 24173 2012-03-29 21:09:52Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRXLSXLayer() */
39 : /************************************************************************/
40 :
41 212 : OGRXLSXLayer::OGRXLSXLayer( OGRXLSXDataSource* poDSIn,
42 : int nSheetIdIn,
43 : const char * pszName,
44 : int bUpdatedIn) :
45 212 : OGRMemLayer(pszName, NULL, wkbNone)
46 : {
47 212 : bInit = FALSE;
48 212 : nSheetId = nSheetIdIn;
49 212 : poDS = poDSIn;
50 212 : bUpdated = bUpdatedIn;
51 212 : bHasHeaderLine = FALSE;
52 212 : }
53 :
54 : /************************************************************************/
55 : /* Init() */
56 : /************************************************************************/
57 :
58 5528 : void OGRXLSXLayer::Init()
59 : {
60 5528 : if (!bInit)
61 : {
62 126 : bInit = TRUE;
63 126 : CPLDebug("XLSX", "Init(%s)", GetName());
64 126 : poDS->BuildLayer(this, nSheetId);
65 : }
66 5528 : }
67 :
68 : /************************************************************************/
69 : /* Updated() */
70 : /************************************************************************/
71 :
72 1930 : void OGRXLSXLayer::SetUpdated(int bUpdatedIn)
73 : {
74 1930 : if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
75 : {
76 6 : bUpdated = TRUE;
77 6 : poDS->SetUpdated();
78 : }
79 1924 : else if (bUpdated && !bUpdatedIn)
80 : {
81 22 : bUpdated = FALSE;
82 : }
83 1930 : }
84 :
85 : /************************************************************************/
86 : /* SyncToDisk() */
87 : /************************************************************************/
88 :
89 0 : OGRErr OGRXLSXLayer::SyncToDisk()
90 : {
91 0 : return poDS->SyncToDisk();
92 : }
93 :
94 : /************************************************************************/
95 : /* GetNextFeature() */
96 : /************************************************************************/
97 :
98 714 : OGRFeature* OGRXLSXLayer::GetNextFeature()
99 : {
100 714 : Init();
101 714 : OGRFeature* poFeature = OGRMemLayer::GetNextFeature();
102 714 : if (poFeature)
103 598 : poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine);
104 714 : return poFeature;
105 : }
106 :
107 : /************************************************************************/
108 : /* GetFeature() */
109 : /************************************************************************/
110 :
111 4 : OGRFeature* OGRXLSXLayer::GetFeature( long nFeatureId )
112 : {
113 4 : Init();
114 4 : OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine));
115 4 : if (poFeature)
116 4 : poFeature->SetFID(nFeatureId);
117 4 : return poFeature;
118 : }
119 :
120 : /************************************************************************/
121 : /* SetFeature() */
122 : /************************************************************************/
123 :
124 632 : OGRErr OGRXLSXLayer::SetFeature( OGRFeature *poFeature )
125 : {
126 632 : Init();
127 632 : if (poFeature == NULL)
128 0 : return OGRMemLayer::SetFeature(poFeature);
129 :
130 632 : long nFID = poFeature->GetFID();
131 632 : if (nFID != OGRNullFID)
132 2 : poFeature->SetFID(nFID - (1 + bHasHeaderLine));
133 632 : SetUpdated();
134 632 : OGRErr eErr = OGRMemLayer::SetFeature(poFeature);
135 632 : poFeature->SetFID(nFID);
136 632 : return eErr;
137 : }
138 :
139 : /************************************************************************/
140 : /* DeleteFeature() */
141 : /************************************************************************/
142 :
143 0 : OGRErr OGRXLSXLayer::DeleteFeature( long nFID )
144 : {
145 0 : Init();
146 0 : SetUpdated();
147 0 : return OGRMemLayer::DeleteFeature(nFID - (1 + bHasHeaderLine));
148 : }
149 :
150 : /************************************************************************/
151 : /* OGRXLSXDataSource() */
152 : /************************************************************************/
153 :
154 26 : OGRXLSXDataSource::OGRXLSXDataSource()
155 :
156 : {
157 26 : pszName = NULL;
158 26 : bUpdatable = FALSE;
159 26 : bUpdated = FALSE;
160 :
161 26 : nLayers = 0;
162 26 : papoLayers = NULL;
163 :
164 26 : bFirstLineIsHeaders = FALSE;
165 :
166 26 : oParser = NULL;
167 26 : bStopParsing = FALSE;
168 26 : nWithoutEventCounter = 0;
169 26 : nDataHandlerCounter = 0;
170 26 : nStackDepth = 0;
171 26 : nDepth = 0;
172 26 : nCurLine = 0;
173 26 : nCurCol = 0;
174 26 : stateStack[0].eVal = STATE_DEFAULT;
175 26 : stateStack[0].nBeginDepth = 0;
176 26 : bInCellXFS = FALSE;
177 :
178 26 : poCurLayer = NULL;
179 :
180 : const char* pszXLSXFieldTypes =
181 26 : CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "");
182 26 : bAutodetectTypes = !EQUAL(pszXLSXFieldTypes, "STRING");
183 26 : }
184 :
185 : /************************************************************************/
186 : /* ~OGRXLSXDataSource() */
187 : /************************************************************************/
188 :
189 26 : OGRXLSXDataSource::~OGRXLSXDataSource()
190 :
191 : {
192 26 : SyncToDisk();
193 :
194 26 : CPLFree( pszName );
195 :
196 220 : for(int i=0;i<nLayers;i++)
197 194 : delete papoLayers[i];
198 26 : CPLFree( papoLayers );
199 26 : }
200 :
201 : /************************************************************************/
202 : /* TestCapability() */
203 : /************************************************************************/
204 :
205 20 : int OGRXLSXDataSource::TestCapability( const char * pszCap )
206 :
207 : {
208 20 : if( EQUAL(pszCap,ODsCCreateLayer) )
209 16 : return bUpdatable;
210 4 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
211 0 : return bUpdatable;
212 : else
213 4 : return FALSE;
214 : }
215 :
216 : /************************************************************************/
217 : /* GetLayer() */
218 : /************************************************************************/
219 :
220 502 : OGRLayer *OGRXLSXDataSource::GetLayer( int iLayer )
221 :
222 : {
223 502 : if (iLayer < 0 || iLayer >= nLayers)
224 0 : return NULL;
225 :
226 502 : return papoLayers[iLayer];
227 : }
228 :
229 : /************************************************************************/
230 : /* GetLayerCount() */
231 : /************************************************************************/
232 :
233 480 : int OGRXLSXDataSource::GetLayerCount()
234 : {
235 480 : return nLayers;
236 : }
237 :
238 : /************************************************************************/
239 : /* Open() */
240 : /************************************************************************/
241 :
242 24 : int OGRXLSXDataSource::Open( const char * pszFilename,
243 : VSILFILE* fpWorkbook,
244 : VSILFILE* fpSharedStrings,
245 : VSILFILE* fpStyles,
246 : int bUpdateIn )
247 :
248 : {
249 24 : bUpdatable = bUpdateIn;
250 :
251 24 : pszName = CPLStrdup( pszFilename );
252 :
253 24 : AnalyseWorkbook(fpWorkbook);
254 24 : AnalyseSharedStrings(fpSharedStrings);
255 24 : AnalyseStyles(fpStyles);
256 :
257 : /* Remove empty layers at the end, which tend to be there */
258 24 : while(nLayers > 1)
259 : {
260 40 : if (papoLayers[nLayers-1]->GetFeatureCount() == 0)
261 : {
262 18 : delete papoLayers[nLayers-1];
263 18 : nLayers --;
264 : }
265 : else
266 22 : break;
267 : }
268 :
269 24 : return TRUE;
270 : }
271 :
272 : /************************************************************************/
273 : /* Create() */
274 : /************************************************************************/
275 :
276 2 : int OGRXLSXDataSource::Create( const char * pszFilename, char **papszOptions )
277 : {
278 2 : bUpdated = TRUE;
279 2 : bUpdatable = TRUE;
280 :
281 2 : pszName = CPLStrdup( pszFilename );
282 :
283 2 : return TRUE;
284 : }
285 :
286 : /************************************************************************/
287 : /* startElementCbk() */
288 : /************************************************************************/
289 :
290 4358 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
291 : const char **ppszAttr)
292 : {
293 4358 : ((OGRXLSXDataSource*)pUserData)->startElementCbk(pszName, ppszAttr);
294 4358 : }
295 :
296 4358 : void OGRXLSXDataSource::startElementCbk(const char *pszName,
297 : const char **ppszAttr)
298 : {
299 4358 : if (bStopParsing) return;
300 :
301 4358 : nWithoutEventCounter = 0;
302 4358 : switch(stateStack[nStackDepth].eVal)
303 : {
304 1670 : case STATE_DEFAULT: startElementDefault(pszName, ppszAttr); break;
305 436 : case STATE_SHEETDATA: startElementTable(pszName, ppszAttr); break;
306 1118 : case STATE_ROW: startElementRow(pszName, ppszAttr); break;
307 1134 : case STATE_CELL: startElementCell(pszName, ppszAttr); break;
308 : case STATE_TEXTV: break;
309 : default: break;
310 : }
311 4358 : nDepth++;
312 : }
313 :
314 : /************************************************************************/
315 : /* endElementCbk() */
316 : /************************************************************************/
317 :
318 4358 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
319 : {
320 4358 : ((OGRXLSXDataSource*)pUserData)->endElementCbk(pszName);
321 4358 : }
322 :
323 4358 : void OGRXLSXDataSource::endElementCbk(const char *pszName)
324 : {
325 4358 : if (bStopParsing) return;
326 :
327 4358 : nWithoutEventCounter = 0;
328 :
329 4358 : nDepth--;
330 4358 : switch(stateStack[nStackDepth].eVal)
331 : {
332 1572 : case STATE_DEFAULT: break;
333 98 : case STATE_SHEETDATA: endElementTable(pszName); break;
334 436 : case STATE_ROW: endElementRow(pszName); break;
335 1206 : case STATE_CELL: endElementCell(pszName); break;
336 : case STATE_TEXTV: break;
337 : default: break;
338 : }
339 :
340 4358 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
341 2796 : nStackDepth --;
342 : }
343 :
344 : /************************************************************************/
345 : /* dataHandlerCbk() */
346 : /************************************************************************/
347 :
348 3206 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
349 : {
350 3206 : ((OGRXLSXDataSource*)pUserData)->dataHandlerCbk(data, nLen);
351 3206 : }
352 :
353 3206 : void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
354 : {
355 3206 : if (bStopParsing) return;
356 :
357 3206 : nDataHandlerCounter ++;
358 3206 : if (nDataHandlerCounter >= BUFSIZ)
359 : {
360 : CPLError(CE_Failure, CPLE_AppDefined,
361 0 : "File probably corrupted (million laugh pattern)");
362 0 : XML_StopParser(oParser, XML_FALSE);
363 0 : bStopParsing = TRUE;
364 0 : return;
365 : }
366 :
367 3206 : nWithoutEventCounter = 0;
368 :
369 3206 : switch(stateStack[nStackDepth].eVal)
370 : {
371 838 : case STATE_DEFAULT: break;
372 102 : case STATE_SHEETDATA: break;
373 384 : case STATE_ROW: break;
374 836 : case STATE_CELL: break;
375 1046 : case STATE_TEXTV: dataHandlerTextV(data, nLen);
376 : default: break;
377 : }
378 : }
379 :
380 : /************************************************************************/
381 : /* PushState() */
382 : /************************************************************************/
383 :
384 3292 : void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
385 : {
386 3292 : if (nStackDepth + 1 == STACK_SIZE)
387 : {
388 0 : bStopParsing = TRUE;
389 0 : return;
390 : }
391 3292 : nStackDepth ++;
392 3292 : stateStack[nStackDepth].eVal = eVal;
393 3292 : stateStack[nStackDepth].nBeginDepth = nDepth;
394 : }
395 :
396 : /************************************************************************/
397 : /* GetAttributeValue() */
398 : /************************************************************************/
399 :
400 4718 : static const char* GetAttributeValue(const char **ppszAttr,
401 : const char* pszKey,
402 : const char* pszDefaultVal)
403 : {
404 16602 : while(*ppszAttr)
405 : {
406 11572 : if (strcmp(ppszAttr[0], pszKey) == 0)
407 4406 : return ppszAttr[1];
408 7166 : ppszAttr += 2;
409 : }
410 312 : return pszDefaultVal;
411 : }
412 :
413 : /************************************************************************/
414 : /* GetOGRFieldType() */
415 : /************************************************************************/
416 :
417 1128 : OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char* pszValue,
418 : const char* pszValueType)
419 : {
420 1128 : if (!bAutodetectTypes || pszValueType == NULL)
421 32 : return OFTString;
422 1096 : else if (strcmp(pszValueType, "string") == 0)
423 410 : return OFTString;
424 686 : else if (strcmp(pszValueType, "float") == 0)
425 : {
426 504 : CPLValueType eValueType = CPLGetValueType(pszValue);
427 504 : if (eValueType == CPL_VALUE_STRING)
428 0 : return OFTString;
429 504 : else if (eValueType == CPL_VALUE_INTEGER)
430 334 : return OFTInteger;
431 : else
432 170 : return OFTReal;
433 : }
434 182 : else if (strcmp(pszValueType, "datetime") == 0)
435 : {
436 76 : return OFTDateTime;
437 : }
438 106 : else if (strcmp(pszValueType, "date") == 0)
439 : {
440 68 : return OFTDate;
441 : }
442 38 : else if (strcmp(pszValueType, "time") == 0)
443 : {
444 26 : return OFTTime;
445 : }
446 : else
447 12 : return OFTString;
448 : }
449 :
450 : /************************************************************************/
451 : /* SetField() */
452 : /************************************************************************/
453 :
454 1156 : static void SetField(OGRFeature* poFeature,
455 : int i,
456 : const char* pszValue,
457 : const char* pszCellType)
458 : {
459 1156 : if (pszValue[0] == '\0')
460 304 : return;
461 :
462 852 : OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();
463 :
464 958 : if (strcmp(pszCellType, "time") == 0 ||
465 : strcmp(pszCellType, "date") == 0 ||
466 : strcmp(pszCellType, "datetime") == 0)
467 : {
468 : struct tm sTm;
469 106 : double dfNumberOfDaysSince1900 = atof(pszValue);
470 : #define NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 25569
471 : #define NUMBER_OF_SECONDS_PER_DAY 86400
472 : GIntBig nUnixTime = (GIntBig)((dfNumberOfDaysSince1900 -
473 : NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 )*
474 106 : NUMBER_OF_SECONDS_PER_DAY);
475 106 : CPLUnixTimeToYMDHMS(nUnixTime, &sTm);
476 :
477 192 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
478 : {
479 : poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
480 86 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec, 0);
481 : }
482 20 : else if (strcmp(pszCellType, "time") == 0)
483 : {
484 : poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d",
485 4 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
486 : }
487 16 : else if (strcmp(pszCellType, "date") == 0)
488 : {
489 : poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d",
490 8 : sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday));
491 : }
492 : else /* if (strcmp(pszCellType, "datetime") == 0) */
493 : {
494 : poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d",
495 : sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
496 8 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
497 : }
498 : }
499 : else
500 746 : poFeature->SetField(i, pszValue);
501 : }
502 :
503 : /************************************************************************/
504 : /* DetectHeaderLine() */
505 : /************************************************************************/
506 :
507 76 : void OGRXLSXDataSource::DetectHeaderLine()
508 :
509 : {
510 76 : int bHeaderLineCandidate = TRUE;
511 : size_t i;
512 344 : for(i = 0; i < apoFirstLineTypes.size(); i++)
513 : {
514 304 : if (apoFirstLineTypes[i] != "string")
515 : {
516 : /* If the values in the first line are not text, then it is */
517 : /* not a header line */
518 36 : bHeaderLineCandidate = FALSE;
519 36 : break;
520 : }
521 : }
522 :
523 76 : size_t nCountTextOnCurLine = 0;
524 76 : size_t nCountNonEmptyOnCurLine = 0;
525 322 : for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
526 : {
527 246 : if (apoCurLineTypes[i] == "string")
528 : {
529 : /* If there are only text values on the second line, then we cannot */
530 : /* know if it is a header line or just a regular line */
531 56 : nCountTextOnCurLine ++;
532 : }
533 190 : else if (apoCurLineTypes[i] != "")
534 : {
535 176 : nCountNonEmptyOnCurLine ++;
536 : }
537 : }
538 :
539 76 : const char* pszXLSXHeaders = CPLGetConfigOption("OGR_XLSX_HEADERS", "");
540 76 : bFirstLineIsHeaders = FALSE;
541 76 : if (EQUAL(pszXLSXHeaders, "FORCE"))
542 2 : bFirstLineIsHeaders = TRUE;
543 74 : else if (EQUAL(pszXLSXHeaders, "DISABLE"))
544 4 : bFirstLineIsHeaders = FALSE;
545 70 : else if (bHeaderLineCandidate &&
546 : apoFirstLineTypes.size() != 0 &&
547 : apoFirstLineTypes.size() == apoCurLineTypes.size() &&
548 : nCountTextOnCurLine != apoFirstLineTypes.size() &&
549 : nCountNonEmptyOnCurLine != 0)
550 : {
551 14 : bFirstLineIsHeaders = TRUE;
552 : }
553 : CPLDebug("XLSX", "%s %s",
554 76 : poCurLayer->GetName(),
555 152 : bFirstLineIsHeaders ? "has header line" : "has no header line");
556 76 : }
557 :
558 : /************************************************************************/
559 : /* startElementDefault() */
560 : /************************************************************************/
561 :
562 1670 : void OGRXLSXDataSource::startElementDefault(const char *pszName,
563 : const char **ppszAttr)
564 : {
565 1670 : if (strcmp(pszName, "sheetData") == 0)
566 : {
567 98 : apoFirstLineValues.resize(0);
568 196 : apoFirstLineTypes.resize(0);
569 98 : nCurLine = 0;
570 98 : PushState(STATE_SHEETDATA);
571 : }
572 1670 : }
573 :
574 : /************************************************************************/
575 : /* startElementTable() */
576 : /************************************************************************/
577 :
578 436 : void OGRXLSXDataSource::startElementTable(const char *pszName,
579 : const char **ppszAttr)
580 : {
581 436 : if (strcmp(pszName, "row") == 0)
582 : {
583 436 : PushState(STATE_ROW);
584 :
585 : int nNewCurLine = atoi(
586 436 : GetAttributeValue(ppszAttr, "r", "0")) - 1;
587 996 : for(;nCurLine<nNewCurLine;)
588 : {
589 124 : nCurCol = 0;
590 124 : apoCurLineValues.resize(0);
591 248 : apoCurLineTypes.resize(0);
592 124 : endElementRow("row");
593 : }
594 436 : nCurCol = 0;
595 436 : apoCurLineValues.resize(0);
596 872 : apoCurLineTypes.resize(0);
597 : }
598 436 : }
599 :
600 : /************************************************************************/
601 : /* endElementTable() */
602 : /************************************************************************/
603 :
604 98 : void OGRXLSXDataSource::endElementTable(const char *pszName)
605 : {
606 98 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
607 : {
608 98 : CPLAssert(strcmp(pszName, "sheetData") == 0);
609 :
610 98 : if (nCurLine == 0 ||
611 : (nCurLine == 1 && apoFirstLineValues.size() == 0))
612 : {
613 : /* We could remove empty sheet, but too late now */
614 : }
615 80 : else if (nCurLine == 1)
616 : {
617 : /* If we have only one single line in the sheet */
618 : size_t i;
619 12 : for(i = 0; i < apoFirstLineValues.size(); i++)
620 : {
621 8 : const char* pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
622 : OGRFieldType eType = GetOGRFieldType(apoFirstLineValues[i].c_str(),
623 8 : apoFirstLineTypes[i].c_str());
624 8 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
625 8 : poCurLayer->CreateField(&oFieldDefn);
626 : }
627 :
628 4 : OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
629 12 : for(i = 0; i < apoFirstLineValues.size(); i++)
630 : {
631 : SetField(poFeature, i, apoFirstLineValues[i].c_str(),
632 8 : apoFirstLineTypes[i].c_str());
633 : }
634 4 : poCurLayer->CreateFeature(poFeature);
635 4 : delete poFeature;
636 : }
637 :
638 98 : if (poCurLayer)
639 : {
640 98 : ((OGRMemLayer*)poCurLayer)->SetUpdatable(bUpdatable);
641 98 : ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE);
642 98 : ((OGRXLSXLayer*)poCurLayer)->SetUpdated(FALSE);
643 : }
644 :
645 98 : poCurLayer = NULL;
646 : }
647 98 : }
648 :
649 : /************************************************************************/
650 : /* startElementRow() */
651 : /************************************************************************/
652 :
653 1118 : void OGRXLSXDataSource::startElementRow(const char *pszName,
654 : const char **ppszAttr)
655 : {
656 1118 : if (strcmp(pszName, "c") == 0)
657 : {
658 1118 : PushState(STATE_CELL);
659 :
660 1118 : const char* pszR = GetAttributeValue(ppszAttr, "r", NULL);
661 1118 : if (pszR)
662 : {
663 : /* Convert col number from base 26 */
664 1118 : int nNewCurCol = (pszR[0] - 'A');
665 1118 : int i = 1;
666 2236 : while(pszR[i] >= 'A' && pszR[i] <= 'Z')
667 : {
668 0 : nNewCurCol = nNewCurCol * 26 + (pszR[i] - 'A');
669 0 : i ++;
670 : }
671 2700 : for(;nCurCol<nNewCurCol;nCurCol++)
672 : {
673 232 : apoCurLineValues.push_back("");
674 464 : apoCurLineTypes.push_back("");
675 : }
676 : }
677 :
678 1118 : osValueType = "float";
679 :
680 1118 : const char* pszS = GetAttributeValue(ppszAttr, "s", "-1");
681 1118 : int nS = atoi(pszS);
682 2066 : if (nS >= 0 && nS < (int)apoStyles.size())
683 : {
684 948 : OGRFieldType eType = apoStyles[nS];
685 948 : if (eType == OFTDateTime)
686 50 : osValueType = "datetime";
687 898 : else if (eType == OFTDate)
688 40 : osValueType = "date";
689 858 : else if (eType == OFTTime)
690 16 : osValueType = "time";
691 : }
692 170 : else if (nS != -1)
693 0 : CPLDebug("XLSX", "Cannot find style %d", nS);
694 :
695 1118 : const char* pszT = GetAttributeValue(ppszAttr, "t", "");
696 1118 : if ( EQUAL(pszT,"s"))
697 492 : osValueType = "stringLookup";
698 626 : else if( EQUAL(pszT,"inlineStr") )
699 52 : osValueType = "string";
700 :
701 1118 : osValue = "";
702 : }
703 1118 : }
704 :
705 : /************************************************************************/
706 : /* endElementRow() */
707 : /************************************************************************/
708 :
709 560 : void OGRXLSXDataSource::endElementRow(const char *pszName)
710 : {
711 560 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
712 : {
713 560 : CPLAssert(strcmp(pszName, "row") == 0);
714 :
715 : OGRFeature* poFeature;
716 : size_t i;
717 :
718 : /* Backup first line values and types in special arrays */
719 560 : if (nCurLine == 0)
720 : {
721 80 : apoFirstLineTypes = apoCurLineTypes;
722 80 : apoFirstLineValues = apoCurLineValues;
723 :
724 : #if skip_leading_empty_rows
725 : if (apoFirstLineTypes.size() == 0)
726 : {
727 : /* Skip leading empty rows */
728 : apoFirstLineTypes.resize(0);
729 : apoFirstLineValues.resize(0);
730 : return;
731 : }
732 : #endif
733 : }
734 :
735 560 : if (nCurLine == 1)
736 : {
737 76 : DetectHeaderLine();
738 :
739 76 : poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);
740 :
741 76 : if (bFirstLineIsHeaders)
742 : {
743 210 : for(i = 0; i < apoFirstLineValues.size(); i++)
744 : {
745 194 : const char* pszFieldName = apoFirstLineValues[i].c_str();
746 194 : if (pszFieldName[0] == '\0')
747 0 : pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
748 194 : OGRFieldType eType = OFTString;
749 194 : if (i < apoCurLineValues.size())
750 : {
751 : eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
752 194 : apoCurLineTypes[i].c_str());
753 : }
754 194 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
755 194 : poCurLayer->CreateField(&oFieldDefn);
756 : }
757 : }
758 : else
759 : {
760 192 : for(i = 0; i < apoFirstLineValues.size(); i++)
761 : {
762 : const char* pszFieldName =
763 132 : CPLSPrintf("Field%d", (int)i + 1);
764 : OGRFieldType eType = GetOGRFieldType(
765 : apoFirstLineValues[i].c_str(),
766 132 : apoFirstLineTypes[i].c_str());
767 132 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
768 132 : poCurLayer->CreateField(&oFieldDefn);
769 : }
770 :
771 60 : poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
772 192 : for(i = 0; i < apoFirstLineValues.size(); i++)
773 : {
774 : SetField(poFeature, i, apoFirstLineValues[i].c_str(),
775 132 : apoFirstLineTypes[i].c_str());
776 : }
777 60 : poCurLayer->CreateFeature(poFeature);
778 60 : delete poFeature;
779 : }
780 : }
781 :
782 560 : if (nCurLine >= 1)
783 : {
784 : /* Add new fields found on following lines. */
785 960 : if (apoCurLineValues.size() >
786 480 : (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
787 : {
788 154 : for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
789 : i < apoCurLineValues.size();
790 : i++)
791 : {
792 : const char* pszFieldName =
793 100 : CPLSPrintf("Field%d", (int)i + 1);
794 : OGRFieldType eType = GetOGRFieldType(
795 : apoCurLineValues[i].c_str(),
796 100 : apoCurLineTypes[i].c_str());
797 100 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
798 100 : poCurLayer->CreateField(&oFieldDefn);
799 : }
800 : }
801 :
802 : /* Update field type if necessary */
803 480 : if (bAutodetectTypes)
804 : {
805 1418 : for(i = 0; i < apoCurLineValues.size(); i++)
806 : {
807 948 : if (apoCurLineValues[i].size())
808 : {
809 : OGRFieldType eValType = GetOGRFieldType(
810 : apoCurLineValues[i].c_str(),
811 694 : apoCurLineTypes[i].c_str());
812 : OGRFieldType eFieldType =
813 694 : poCurLayer->GetLayerDefn()->GetFieldDefn(i)->GetType();
814 694 : if (eFieldType == OFTDateTime &&
815 : (eValType == OFTDate || eValType == OFTTime) )
816 : {
817 : /* ok */
818 : }
819 694 : else if (eFieldType == OFTReal && eValType == OFTInteger)
820 : {
821 : /* ok */;
822 : }
823 686 : else if (eFieldType != OFTString && eValType != eFieldType)
824 : {
825 : OGRFieldDefn oNewFieldDefn(
826 44 : poCurLayer->GetLayerDefn()->GetFieldDefn(i));
827 52 : if ((eFieldType == OFTDate || eFieldType == OFTTime) &&
828 : eValType == OFTDateTime)
829 8 : oNewFieldDefn.SetType(OFTDateTime);
830 44 : else if (eFieldType == OFTInteger &&
831 : eValType == OFTReal)
832 8 : oNewFieldDefn.SetType(OFTReal);
833 : else
834 28 : oNewFieldDefn.SetType(OFTString);
835 : poCurLayer->AlterFieldDefn(i, &oNewFieldDefn,
836 44 : ALTER_TYPE_FLAG);
837 : }
838 : }
839 : }
840 : }
841 :
842 : /* Add feature for current line */
843 480 : poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
844 1496 : for(i = 0; i < apoCurLineValues.size(); i++)
845 : {
846 : SetField(poFeature, i, apoCurLineValues[i].c_str(),
847 1016 : apoCurLineTypes[i].c_str());
848 : }
849 480 : poCurLayer->CreateFeature(poFeature);
850 480 : delete poFeature;
851 : }
852 :
853 560 : nCurLine++;
854 : }
855 560 : }
856 :
857 : /************************************************************************/
858 : /* startElementCell() */
859 : /************************************************************************/
860 :
861 1134 : void OGRXLSXDataSource::startElementCell(const char *pszName,
862 : const char **ppszAttr)
863 : {
864 1134 : if (osValue.size() == 0 && strcmp(pszName, "v") == 0)
865 : {
866 994 : PushState(STATE_TEXTV);
867 : }
868 140 : else if (osValue.size() == 0 && strcmp(pszName, "t") == 0)
869 : {
870 52 : PushState(STATE_TEXTV);
871 : }
872 1134 : }
873 :
874 : /************************************************************************/
875 : /* endElementCell() */
876 : /************************************************************************/
877 :
878 1206 : void OGRXLSXDataSource::endElementCell(const char *pszName)
879 : {
880 1206 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
881 : {
882 1118 : CPLAssert(strcmp(pszName, "c") == 0);
883 :
884 1118 : if (osValueType == "stringLookup")
885 : {
886 492 : int nIndex = atoi(osValue);
887 492 : if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
888 492 : osValue = apoSharedStrings[nIndex];
889 : else
890 0 : CPLDebug("XLSX", "Cannot find string %d", nIndex);
891 492 : osValueType = "string";
892 : }
893 :
894 1118 : apoCurLineValues.push_back(osValue);
895 1118 : apoCurLineTypes.push_back(osValueType);
896 :
897 1118 : nCurCol += 1;
898 : }
899 1206 : }
900 :
901 : /************************************************************************/
902 : /* dataHandlerTextV() */
903 : /************************************************************************/
904 :
905 1046 : void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
906 : {
907 1046 : osValue.append(data, nLen);
908 1046 : }
909 :
910 : /************************************************************************/
911 : /* BuildLayer() */
912 : /************************************************************************/
913 :
914 126 : void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer* poLayer, int nSheetId)
915 : {
916 126 : poCurLayer = poLayer;
917 :
918 : CPLString osSheetFilename(
919 126 : CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, nSheetId));
920 126 : const char* pszSheetFilename = osSheetFilename.c_str();
921 126 : VSILFILE* fp = VSIFOpenL(pszSheetFilename, "rb");
922 126 : if (fp == NULL)
923 : return;
924 :
925 98 : int bUpdatedBackup = bUpdated;
926 :
927 98 : oParser = OGRCreateExpatXMLParser();
928 98 : XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
929 98 : XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
930 98 : XML_SetUserData(oParser, this);
931 :
932 98 : VSIFSeekL( fp, 0, SEEK_SET );
933 :
934 98 : bStopParsing = FALSE;
935 98 : nWithoutEventCounter = 0;
936 98 : nDataHandlerCounter = 0;
937 98 : nStackDepth = 0;
938 98 : nDepth = 0;
939 98 : stateStack[0].eVal = STATE_DEFAULT;
940 98 : stateStack[0].nBeginDepth = 0;
941 :
942 : char aBuf[BUFSIZ];
943 : int nDone;
944 98 : do
945 : {
946 98 : nDataHandlerCounter = 0;
947 : unsigned int nLen =
948 98 : (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
949 98 : nDone = VSIFEofL(fp);
950 98 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
951 : {
952 : CPLError(CE_Failure, CPLE_AppDefined,
953 : "XML parsing of %s file failed : %s at line %d, column %d",
954 : pszSheetFilename,
955 : XML_ErrorString(XML_GetErrorCode(oParser)),
956 : (int)XML_GetCurrentLineNumber(oParser),
957 0 : (int)XML_GetCurrentColumnNumber(oParser));
958 0 : bStopParsing = TRUE;
959 : }
960 98 : nWithoutEventCounter ++;
961 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
962 :
963 98 : XML_ParserFree(oParser);
964 98 : oParser = NULL;
965 :
966 98 : if (nWithoutEventCounter == 10)
967 : {
968 : CPLError(CE_Failure, CPLE_AppDefined,
969 0 : "Too much data inside one element. File probably corrupted");
970 0 : bStopParsing = TRUE;
971 : }
972 :
973 98 : VSIFCloseL(fp);
974 :
975 98 : bUpdated = bUpdatedBackup;
976 : }
977 :
978 : /************************************************************************/
979 : /* startElementSSCbk() */
980 : /************************************************************************/
981 :
982 1210 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszName,
983 : const char **ppszAttr)
984 : {
985 1210 : ((OGRXLSXDataSource*)pUserData)->startElementSSCbk(pszName, ppszAttr);
986 1210 : }
987 :
988 1210 : void OGRXLSXDataSource::startElementSSCbk(const char *pszName,
989 : const char **ppszAttr)
990 : {
991 1210 : if (bStopParsing) return;
992 :
993 1210 : nWithoutEventCounter = 0;
994 1210 : switch(stateStack[nStackDepth].eVal)
995 : {
996 : case STATE_DEFAULT:
997 : {
998 1210 : if (strcmp(pszName,"t") == 0)
999 : {
1000 594 : PushState(STATE_T);
1001 594 : osCurrentString = "";
1002 : }
1003 : break;
1004 : }
1005 : default:
1006 : break;
1007 : }
1008 1210 : nDepth++;
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* endElementSSCbk() */
1013 : /************************************************************************/
1014 :
1015 1210 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszName)
1016 : {
1017 1210 : ((OGRXLSXDataSource*)pUserData)->endElementSSCbk(pszName);
1018 1210 : }
1019 :
1020 1210 : void OGRXLSXDataSource::endElementSSCbk(const char *pszName)
1021 : {
1022 1210 : if (bStopParsing) return;
1023 :
1024 1210 : nWithoutEventCounter = 0;
1025 :
1026 1210 : nDepth--;
1027 1210 : switch(stateStack[nStackDepth].eVal)
1028 : {
1029 616 : case STATE_DEFAULT: break;
1030 : case STATE_T:
1031 : {
1032 594 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1033 : {
1034 594 : apoSharedStrings.push_back(osCurrentString);
1035 : }
1036 : break;
1037 : }
1038 : default: break;
1039 : }
1040 :
1041 1210 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1042 616 : nStackDepth --;
1043 : }
1044 :
1045 : /************************************************************************/
1046 : /* dataHandlerSSCbk() */
1047 : /************************************************************************/
1048 :
1049 982 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data, int nLen)
1050 : {
1051 982 : ((OGRXLSXDataSource*)pUserData)->dataHandlerSSCbk(data, nLen);
1052 982 : }
1053 :
1054 982 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
1055 : {
1056 982 : if (bStopParsing) return;
1057 :
1058 982 : nDataHandlerCounter ++;
1059 982 : if (nDataHandlerCounter >= BUFSIZ)
1060 : {
1061 : CPLError(CE_Failure, CPLE_AppDefined,
1062 0 : "File probably corrupted (million laugh pattern)");
1063 0 : XML_StopParser(oParser, XML_FALSE);
1064 0 : bStopParsing = TRUE;
1065 0 : return;
1066 : }
1067 :
1068 982 : nWithoutEventCounter = 0;
1069 :
1070 982 : switch(stateStack[nStackDepth].eVal)
1071 : {
1072 328 : case STATE_DEFAULT: break;
1073 654 : case STATE_T: osCurrentString.append(data, nLen); break;
1074 : default: break;
1075 : }
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* AnalyseSharedStrings() */
1080 : /************************************************************************/
1081 :
1082 24 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE* fpSharedStrings)
1083 : {
1084 24 : if (fpSharedStrings == NULL)
1085 2 : return;
1086 :
1087 22 : oParser = OGRCreateExpatXMLParser();
1088 22 : XML_SetElementHandler(oParser, ::startElementSSCbk, ::endElementSSCbk);
1089 22 : XML_SetCharacterDataHandler(oParser, ::dataHandlerSSCbk);
1090 22 : XML_SetUserData(oParser, this);
1091 :
1092 22 : VSIFSeekL( fpSharedStrings, 0, SEEK_SET );
1093 :
1094 22 : bStopParsing = FALSE;
1095 22 : nWithoutEventCounter = 0;
1096 22 : nDataHandlerCounter = 0;
1097 22 : nStackDepth = 0;
1098 22 : nDepth = 0;
1099 22 : stateStack[0].eVal = STATE_DEFAULT;
1100 22 : stateStack[0].nBeginDepth = 0;
1101 :
1102 : char aBuf[BUFSIZ];
1103 : int nDone;
1104 22 : do
1105 : {
1106 22 : nDataHandlerCounter = 0;
1107 : unsigned int nLen =
1108 22 : (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSharedStrings );
1109 22 : nDone = VSIFEofL(fpSharedStrings);
1110 22 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
1111 : {
1112 : CPLError(CE_Failure, CPLE_AppDefined,
1113 : "XML parsing of %s file failed : %s at line %d, column %d",
1114 : "sharedStrings.xml",
1115 : XML_ErrorString(XML_GetErrorCode(oParser)),
1116 : (int)XML_GetCurrentLineNumber(oParser),
1117 0 : (int)XML_GetCurrentColumnNumber(oParser));
1118 0 : bStopParsing = TRUE;
1119 : }
1120 22 : nWithoutEventCounter ++;
1121 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1122 :
1123 22 : XML_ParserFree(oParser);
1124 22 : oParser = NULL;
1125 :
1126 22 : if (nWithoutEventCounter == 10)
1127 : {
1128 : CPLError(CE_Failure, CPLE_AppDefined,
1129 0 : "Too much data inside one element. File probably corrupted");
1130 0 : bStopParsing = TRUE;
1131 : }
1132 :
1133 22 : VSIFCloseL(fpSharedStrings);
1134 22 : fpSharedStrings = NULL;
1135 : }
1136 :
1137 :
1138 : /************************************************************************/
1139 : /* startElementWBCbk() */
1140 : /************************************************************************/
1141 :
1142 392 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszName,
1143 : const char **ppszAttr)
1144 : {
1145 392 : ((OGRXLSXDataSource*)pUserData)->startElementWBCbk(pszName, ppszAttr);
1146 392 : }
1147 :
1148 392 : void OGRXLSXDataSource::startElementWBCbk(const char *pszName,
1149 : const char **ppszAttr)
1150 : {
1151 392 : if (bStopParsing) return;
1152 :
1153 392 : nWithoutEventCounter = 0;
1154 392 : if (strcmp(pszName,"sheet") == 0)
1155 : {
1156 196 : const char* pszSheetName = GetAttributeValue(ppszAttr, "name", NULL);
1157 196 : const char* pszSheetId = GetAttributeValue(ppszAttr, "sheetId", NULL);
1158 196 : if (pszSheetName && pszSheetId)
1159 : {
1160 196 : int nSheetId = atoi(pszSheetId);
1161 196 : papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
1162 196 : papoLayers[nLayers++] = new OGRXLSXLayer(this, nSheetId, pszSheetName);
1163 : }
1164 : }
1165 : }
1166 :
1167 : /************************************************************************/
1168 : /* AnalyseWorkbook() */
1169 : /************************************************************************/
1170 :
1171 24 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE* fpWorkbook)
1172 : {
1173 24 : oParser = OGRCreateExpatXMLParser();
1174 24 : XML_SetElementHandler(oParser, ::startElementWBCbk, NULL);
1175 24 : XML_SetUserData(oParser, this);
1176 :
1177 24 : VSIFSeekL( fpWorkbook, 0, SEEK_SET );
1178 :
1179 24 : bStopParsing = FALSE;
1180 24 : nWithoutEventCounter = 0;
1181 24 : nDataHandlerCounter = 0;
1182 :
1183 : char aBuf[BUFSIZ];
1184 : int nDone;
1185 24 : do
1186 : {
1187 24 : nDataHandlerCounter = 0;
1188 : unsigned int nLen =
1189 24 : (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpWorkbook );
1190 24 : nDone = VSIFEofL(fpWorkbook);
1191 24 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
1192 : {
1193 : CPLError(CE_Failure, CPLE_AppDefined,
1194 : "XML parsing of %s file failed : %s at line %d, column %d",
1195 : "workbook.xml",
1196 : XML_ErrorString(XML_GetErrorCode(oParser)),
1197 : (int)XML_GetCurrentLineNumber(oParser),
1198 0 : (int)XML_GetCurrentColumnNumber(oParser));
1199 0 : bStopParsing = TRUE;
1200 : }
1201 24 : nWithoutEventCounter ++;
1202 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1203 :
1204 24 : XML_ParserFree(oParser);
1205 24 : oParser = NULL;
1206 :
1207 24 : if (nWithoutEventCounter == 10)
1208 : {
1209 : CPLError(CE_Failure, CPLE_AppDefined,
1210 0 : "Too much data inside one element. File probably corrupted");
1211 0 : bStopParsing = TRUE;
1212 : }
1213 :
1214 24 : VSIFCloseL(fpWorkbook);
1215 24 : }
1216 :
1217 :
1218 : /************************************************************************/
1219 : /* startElementStylesCbk() */
1220 : /************************************************************************/
1221 :
1222 1652 : static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszName,
1223 : const char **ppszAttr)
1224 : {
1225 1652 : ((OGRXLSXDataSource*)pUserData)->startElementStylesCbk(pszName, ppszAttr);
1226 1652 : }
1227 :
1228 1652 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszName,
1229 : const char **ppszAttr)
1230 : {
1231 1652 : if (bStopParsing) return;
1232 :
1233 1652 : nWithoutEventCounter = 0;
1234 1652 : if (strcmp(pszName,"numFmt") == 0)
1235 : {
1236 160 : const char* pszFormatCode = GetAttributeValue(ppszAttr, "formatCode", NULL);
1237 160 : const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1238 160 : int nNumFmtId = atoi(pszNumFmtId);
1239 160 : if (pszFormatCode && nNumFmtId >= 164)
1240 : {
1241 : int bHasDate = strstr(pszFormatCode, "DD") != NULL ||
1242 160 : strstr(pszFormatCode, "YY") != NULL;
1243 160 : int bHasTime = strstr(pszFormatCode, "HH") != NULL;
1244 182 : if (bHasDate && bHasTime)
1245 22 : apoMapStyleFormats[nNumFmtId] = OFTDateTime;
1246 138 : else if (bHasDate)
1247 22 : apoMapStyleFormats[nNumFmtId] = OFTDate;
1248 116 : else if (bHasTime)
1249 22 : apoMapStyleFormats[nNumFmtId] = OFTTime;
1250 : else
1251 94 : apoMapStyleFormats[nNumFmtId] = OFTReal;
1252 : }
1253 : }
1254 1492 : else if (strcmp(pszName,"cellXfs") == 0)
1255 : {
1256 24 : bInCellXFS = TRUE;
1257 : }
1258 1468 : else if (bInCellXFS && strcmp(pszName,"xf") == 0)
1259 : {
1260 216 : const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1261 216 : int nNumFmtId = atoi(pszNumFmtId);
1262 216 : OGRFieldType eType = OFTReal;
1263 216 : if (nNumFmtId >= 0)
1264 : {
1265 216 : if (nNumFmtId < 164)
1266 : {
1267 : // From http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
1268 2 : if (nNumFmtId >= 14 && nNumFmtId <= 17)
1269 0 : eType = OFTDate;
1270 2 : else if (nNumFmtId >= 18 && nNumFmtId <= 21)
1271 0 : eType = OFTTime;
1272 2 : else if (nNumFmtId == 22)
1273 0 : eType = OFTDateTime;
1274 : }
1275 : else
1276 : {
1277 214 : std::map<int, OGRFieldType>::iterator oIter = apoMapStyleFormats.find(nNumFmtId);
1278 214 : if (oIter != apoMapStyleFormats.end())
1279 214 : eType = oIter->second;
1280 : else
1281 0 : CPLDebug("XLSX", "Cannot find entry in <numFmts> with numFmtId=%d", nNumFmtId);
1282 : }
1283 : }
1284 : //printf("style[%d] = %d\n", apoStyles.size(), eType);
1285 :
1286 216 : apoStyles.push_back(eType);
1287 : }
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* endElementStylesCbk() */
1292 : /************************************************************************/
1293 :
1294 1652 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszName)
1295 : {
1296 1652 : ((OGRXLSXDataSource*)pUserData)->endElementStylesCbk(pszName);
1297 1652 : }
1298 :
1299 1652 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszName)
1300 : {
1301 1652 : if (bStopParsing) return;
1302 :
1303 1652 : nWithoutEventCounter = 0;
1304 1652 : if (strcmp(pszName,"cellXfs") == 0)
1305 : {
1306 24 : bInCellXFS = FALSE;
1307 : }
1308 : }
1309 :
1310 : /************************************************************************/
1311 : /* AnalyseStyles() */
1312 : /************************************************************************/
1313 :
1314 24 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE* fpStyles)
1315 : {
1316 24 : if (fpStyles == NULL)
1317 0 : return;
1318 :
1319 24 : oParser = OGRCreateExpatXMLParser();
1320 24 : XML_SetElementHandler(oParser, ::startElementStylesCbk, ::endElementStylesCbk);
1321 24 : XML_SetUserData(oParser, this);
1322 :
1323 24 : VSIFSeekL( fpStyles, 0, SEEK_SET );
1324 :
1325 24 : bStopParsing = FALSE;
1326 24 : nWithoutEventCounter = 0;
1327 24 : nDataHandlerCounter = 0;
1328 24 : bInCellXFS = FALSE;
1329 :
1330 : char aBuf[BUFSIZ];
1331 : int nDone;
1332 24 : do
1333 : {
1334 24 : nDataHandlerCounter = 0;
1335 : unsigned int nLen =
1336 24 : (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpStyles );
1337 24 : nDone = VSIFEofL(fpStyles);
1338 24 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
1339 : {
1340 : CPLError(CE_Failure, CPLE_AppDefined,
1341 : "XML parsing of %s file failed : %s at line %d, column %d",
1342 : "styles.xml",
1343 : XML_ErrorString(XML_GetErrorCode(oParser)),
1344 : (int)XML_GetCurrentLineNumber(oParser),
1345 0 : (int)XML_GetCurrentColumnNumber(oParser));
1346 0 : bStopParsing = TRUE;
1347 : }
1348 24 : nWithoutEventCounter ++;
1349 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1350 :
1351 24 : XML_ParserFree(oParser);
1352 24 : oParser = NULL;
1353 :
1354 24 : if (nWithoutEventCounter == 10)
1355 : {
1356 : CPLError(CE_Failure, CPLE_AppDefined,
1357 0 : "Too much data inside one element. File probably corrupted");
1358 0 : bStopParsing = TRUE;
1359 : }
1360 :
1361 24 : VSIFCloseL(fpStyles);
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* CreateLayer() */
1366 : /************************************************************************/
1367 :
1368 : OGRLayer *
1369 16 : OGRXLSXDataSource::CreateLayer( const char * pszLayerName,
1370 : OGRSpatialReference *poSRS,
1371 : OGRwkbGeometryType eType,
1372 : char ** papszOptions )
1373 :
1374 : {
1375 : /* -------------------------------------------------------------------- */
1376 : /* Verify we are in update mode. */
1377 : /* -------------------------------------------------------------------- */
1378 16 : if( !bUpdatable )
1379 : {
1380 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1381 : "Data source %s opened read-only.\n"
1382 : "New layer %s cannot be created.\n",
1383 0 : pszName, pszLayerName );
1384 :
1385 0 : return NULL;
1386 : }
1387 :
1388 : /* -------------------------------------------------------------------- */
1389 : /* Do we already have this layer? If so, should we blow it */
1390 : /* away? */
1391 : /* -------------------------------------------------------------------- */
1392 : int iLayer;
1393 :
1394 72 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
1395 : {
1396 56 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
1397 : {
1398 0 : if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
1399 : && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
1400 : {
1401 0 : DeleteLayer( pszLayerName );
1402 : }
1403 : else
1404 : {
1405 : CPLError( CE_Failure, CPLE_AppDefined,
1406 : "Layer %s already exists, CreateLayer failed.\n"
1407 : "Use the layer creation option OVERWRITE=YES to "
1408 : "replace it.",
1409 0 : pszLayerName );
1410 0 : return NULL;
1411 : }
1412 : }
1413 : }
1414 :
1415 : /* -------------------------------------------------------------------- */
1416 : /* Create the layer object. */
1417 : /* -------------------------------------------------------------------- */
1418 16 : OGRLayer* poLayer = new OGRXLSXLayer(this, nLayers + 1, pszLayerName, TRUE);
1419 :
1420 16 : papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
1421 16 : papoLayers[nLayers] = poLayer;
1422 16 : nLayers ++;
1423 :
1424 16 : bUpdated = TRUE;
1425 :
1426 16 : return poLayer;
1427 : }
1428 :
1429 : /************************************************************************/
1430 : /* DeleteLayer() */
1431 : /************************************************************************/
1432 :
1433 0 : void OGRXLSXDataSource::DeleteLayer( const char *pszLayerName )
1434 :
1435 : {
1436 : int iLayer;
1437 :
1438 : /* -------------------------------------------------------------------- */
1439 : /* Verify we are in update mode. */
1440 : /* -------------------------------------------------------------------- */
1441 0 : if( !bUpdatable )
1442 : {
1443 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1444 : "Data source %s opened read-only.\n"
1445 : "Layer %s cannot be deleted.\n",
1446 0 : pszName, pszLayerName );
1447 :
1448 0 : return;
1449 : }
1450 :
1451 : /* -------------------------------------------------------------------- */
1452 : /* Try to find layer. */
1453 : /* -------------------------------------------------------------------- */
1454 0 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
1455 : {
1456 0 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
1457 0 : break;
1458 : }
1459 :
1460 0 : if( iLayer == nLayers )
1461 : {
1462 : CPLError( CE_Failure, CPLE_AppDefined,
1463 : "Attempt to delete layer '%s', but this layer is not known to OGR.",
1464 0 : pszLayerName );
1465 0 : return;
1466 : }
1467 :
1468 0 : DeleteLayer(iLayer);
1469 : }
1470 :
1471 : /************************************************************************/
1472 : /* DeleteLayer() */
1473 : /************************************************************************/
1474 :
1475 0 : OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
1476 : {
1477 0 : if( iLayer < 0 || iLayer >= nLayers )
1478 : {
1479 : CPLError( CE_Failure, CPLE_AppDefined,
1480 : "Layer %d not in legal range of 0 to %d.",
1481 0 : iLayer, nLayers-1 );
1482 0 : return OGRERR_FAILURE;
1483 : }
1484 :
1485 : /* -------------------------------------------------------------------- */
1486 : /* Blow away our OGR structures related to the layer. This is */
1487 : /* pretty dangerous if anything has a reference to this layer! */
1488 : /* -------------------------------------------------------------------- */
1489 :
1490 0 : delete papoLayers[iLayer];
1491 : memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
1492 0 : sizeof(void *) * (nLayers - iLayer - 1) );
1493 0 : nLayers--;
1494 :
1495 0 : bUpdated = TRUE;
1496 :
1497 0 : return OGRERR_NONE;
1498 : }
1499 :
1500 : /************************************************************************/
1501 : /* WriteOverride() */
1502 : /************************************************************************/
1503 :
1504 60 : static void WriteOverride(VSILFILE* fp, const char* pszPartName, const char* pszContentType)
1505 : {
1506 : VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
1507 60 : pszPartName, pszContentType);
1508 60 : }
1509 :
1510 : #define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1511 : #define MAIN_NS "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\""
1512 : #define SCHEMA_OD "http://schemas.openxmlformats.org/officeDocument/2006"
1513 : #define SCHEMA_OD_RS "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
1514 : #define SCHEMA_PACKAGE "http://schemas.openxmlformats.org/package/2006"
1515 : #define SCHEMA_PACKAGE_RS "http://schemas.openxmlformats.org/package/2006/relationships"
1516 :
1517 : /************************************************************************/
1518 : /* WriteContentTypes() */
1519 : /************************************************************************/
1520 :
1521 4 : static void WriteContentTypes(const char* pszName, int nLayers)
1522 : {
1523 : VSILFILE* fp;
1524 :
1525 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName), "wb");
1526 4 : VSIFPrintfL(fp, XML_HEADER);
1527 4 : VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
1528 4 : WriteOverride(fp, "/_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml");
1529 4 : WriteOverride(fp, "/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml");
1530 4 : WriteOverride(fp, "/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml");
1531 4 : WriteOverride(fp, "/xl/_rels/workbook.xml.rels", "application/vnd.openxmlformats-package.relationships+xml");
1532 36 : for(int i=0;i<nLayers;i++)
1533 : {
1534 : WriteOverride(fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i+1),
1535 32 : "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
1536 : }
1537 4 : WriteOverride(fp, "/xl/styles.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
1538 4 : WriteOverride(fp, "/xl/workbook.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
1539 4 : WriteOverride(fp, "/xl/sharedStrings.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
1540 4 : VSIFPrintfL(fp, "</Types>\n");
1541 4 : VSIFCloseL(fp);
1542 4 : }
1543 :
1544 : /************************************************************************/
1545 : /* WriteApp() */
1546 : /************************************************************************/
1547 :
1548 4 : static void WriteApp(const char* pszName)
1549 : {
1550 : VSILFILE* fp;
1551 :
1552 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName), "wb");
1553 4 : VSIFPrintfL(fp, XML_HEADER);
1554 : VSIFPrintfL(fp, "<Properties xmlns=\"%s/extended-properties\" "
1555 4 : "xmlns:vt=\"%s/docPropsVTypes\">\n", SCHEMA_OD, SCHEMA_OD);
1556 4 : VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
1557 4 : VSIFPrintfL(fp, "</Properties>\n");
1558 4 : VSIFCloseL(fp);
1559 4 : }
1560 :
1561 : /************************************************************************/
1562 : /* WriteCore() */
1563 : /************************************************************************/
1564 :
1565 4 : static void WriteCore(const char* pszName)
1566 : {
1567 : VSILFILE* fp;
1568 :
1569 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName), "wb");
1570 4 : VSIFPrintfL(fp, XML_HEADER);
1571 : VSIFPrintfL(fp, "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
1572 : "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
1573 : "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
1574 : "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
1575 4 : "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n", SCHEMA_PACKAGE);
1576 4 : VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
1577 4 : VSIFPrintfL(fp, "</cp:coreProperties>\n");
1578 4 : VSIFCloseL(fp);
1579 4 : }
1580 :
1581 : /************************************************************************/
1582 : /* WriteWorkbook() */
1583 : /************************************************************************/
1584 :
1585 4 : static void WriteWorkbook(const char* pszName, OGRDataSource* poDS)
1586 : {
1587 : VSILFILE* fp;
1588 :
1589 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName), "wb");
1590 4 : VSIFPrintfL(fp, XML_HEADER);
1591 4 : VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
1592 4 : VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
1593 : /*
1594 : VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\" date1904=\"false\"/>\n");
1595 : VSIFPrintfL(fp, "<workbookProtection/>\n");
1596 : VSIFPrintfL(fp, "<bookViews>\n");
1597 : VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\" showHorizontalScroll=\"true\" "
1598 : "showSheetTabs=\"true\" showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
1599 : "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
1600 : VSIFPrintfL(fp, "</bookViews>\n");
1601 : */
1602 4 : VSIFPrintfL(fp, "<sheets>\n");
1603 36 : for(int i=0;i<poDS->GetLayerCount();i++)
1604 : {
1605 32 : OGRXLSXLayer* poLayer = (OGRXLSXLayer*) poDS->GetLayer(i);
1606 32 : const char* pszLayerName = poLayer->GetName();
1607 32 : char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
1608 32 : VSIFPrintfL(fp, "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" r:id=\"rId%d\"/>\n", pszXML, i+1, i+2);
1609 32 : CPLFree(pszXML);
1610 : }
1611 4 : VSIFPrintfL(fp, "</sheets>\n");
1612 4 : VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" iterate=\"false\" iterateDelta=\"0.001\"/>\n");
1613 4 : VSIFPrintfL(fp, "</workbook>\n");
1614 4 : VSIFCloseL(fp);
1615 4 : }
1616 :
1617 : /************************************************************************/
1618 : /* BuildColString() */
1619 : /************************************************************************/
1620 :
1621 206 : static void BuildColString(char szCol[5], int nCol)
1622 : {
1623 206 : int k = 0;
1624 206 : szCol[k++] = (nCol % 26) + 'A';
1625 412 : while(nCol >= 26)
1626 : {
1627 0 : nCol /= 26;
1628 0 : szCol[k++] = (nCol % 26) + 'A';
1629 : }
1630 206 : szCol[k] = 0;
1631 206 : for(int l=0;l<k/2;l++)
1632 : {
1633 0 : char chTmp = szCol[k-1-l];
1634 0 : szCol[k-1-l] = szCol[l];
1635 0 : szCol[l] = chTmp;
1636 : }
1637 206 : }
1638 :
1639 : /************************************************************************/
1640 : /* WriteLayer() */
1641 : /************************************************************************/
1642 :
1643 32 : static void WriteLayer(const char* pszName, OGRLayer* poLayer, int iLayer,
1644 : std::map<std::string,int>& oStringMap,
1645 : std::vector<std::string>& oStringList)
1646 : {
1647 : VSILFILE* fp;
1648 : int j;
1649 :
1650 32 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, iLayer + 1), "wb");
1651 32 : VSIFPrintfL(fp, XML_HEADER);
1652 32 : VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
1653 : /*
1654 : VSIFPrintfL(fp, "<sheetViews>\n");
1655 : VSIFPrintfL(fp, "<sheetView colorId=\"64\" defaultGridColor=\"true\" rightToLeft=\"false\" showFormulas=\"false\" showGridLines=\"true\" showOutlineSymbols=\"true\" showRowColHeaders=\"true\" showZeros=\"true\" tabSelected=\"%s\" topLeftCell=\"A1\" view=\"normal\" windowProtection=\"false\" workbookViewId=\"0\" zoomScale=\"100\" zoomScaleNormal=\"100\" zoomScalePageLayoutView=\"60\">\n",
1656 : (i == 0) ? "true" : "false");
1657 : VSIFPrintfL(fp, "<selection activeCell=\"A1\" activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n");
1658 : VSIFPrintfL(fp, "</sheetView>\n");
1659 : VSIFPrintfL(fp, "</sheetViews>\n");*/
1660 :
1661 32 : poLayer->ResetReading();
1662 :
1663 32 : OGRFeature* poFeature = poLayer->GetNextFeature();
1664 :
1665 32 : OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
1666 32 : int bHasHeaders = FALSE;
1667 32 : int iRow = 1;
1668 :
1669 32 : VSIFPrintfL(fp, "<cols>\n");
1670 124 : for(j=0;j<poFDefn->GetFieldCount();j++)
1671 : {
1672 92 : int nWidth = 15;
1673 92 : if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
1674 10 : nWidth = 25;
1675 : VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
1676 92 : j+1, 1024, nWidth);
1677 :
1678 92 : if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
1679 : CPLSPrintf("Field%d", j+1)) != 0)
1680 48 : bHasHeaders = TRUE;
1681 : }
1682 32 : VSIFPrintfL(fp, "</cols>\n");
1683 :
1684 32 : VSIFPrintfL(fp, "<sheetData>\n");
1685 :
1686 32 : if (bHasHeaders && poFeature != NULL)
1687 : {
1688 4 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
1689 52 : for(j=0;j<poFDefn->GetFieldCount();j++)
1690 : {
1691 48 : const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
1692 48 : std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
1693 : int nStringIndex;
1694 48 : if (oIter != oStringMap.end())
1695 0 : nStringIndex = oIter->second;
1696 : else
1697 : {
1698 48 : nStringIndex = (int)oStringList.size();
1699 48 : oStringMap[pszVal] = nStringIndex;
1700 96 : oStringList.push_back(pszVal);
1701 : }
1702 :
1703 : char szCol[5];
1704 48 : BuildColString(szCol, j);
1705 :
1706 48 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
1707 48 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
1708 48 : VSIFPrintfL(fp, "</c>\n");
1709 : }
1710 4 : VSIFPrintfL(fp, "</row>\n");
1711 :
1712 4 : iRow ++;
1713 : }
1714 :
1715 162 : while(poFeature != NULL)
1716 : {
1717 98 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
1718 602 : for(j=0;j<poFeature->GetFieldCount();j++)
1719 : {
1720 504 : if (poFeature->IsFieldSet(j))
1721 : {
1722 : char szCol[5];
1723 158 : BuildColString(szCol, j);
1724 :
1725 158 : OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType();
1726 :
1727 158 : if (eType == OFTReal)
1728 : {
1729 30 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
1730 30 : VSIFPrintfL(fp, "<v>%.16f</v>\n", poFeature->GetFieldAsDouble(j));
1731 30 : VSIFPrintfL(fp, "</c>\n");
1732 : }
1733 128 : else if (eType == OFTInteger)
1734 : {
1735 18 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
1736 18 : VSIFPrintfL(fp, "<v>%d</v>\n", poFeature->GetFieldAsInteger(j));
1737 18 : VSIFPrintfL(fp, "</c>\n");
1738 : }
1739 132 : else if (eType == OFTDate || eType == OFTDateTime || eType == OFTTime)
1740 : {
1741 : VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", szCol, iRow,
1742 22 : (eType == OFTDate) ? 1 : (eType == OFTDateTime) ? 2 : 3);
1743 : int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
1744 : poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
1745 22 : &nHour, &nMinute, &nSecond, &nTZFlag);
1746 : struct tm brokendowntime;
1747 22 : memset(&brokendowntime, 0, sizeof(brokendowntime));
1748 22 : brokendowntime.tm_year = (eType == OFTTime) ? 70 : nYear - 1900;
1749 22 : brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
1750 22 : brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
1751 22 : brokendowntime.tm_hour = nHour;
1752 22 : brokendowntime.tm_min = nMinute;
1753 22 : brokendowntime.tm_sec = nSecond;
1754 22 : GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
1755 22 : double dfNumberOfDaysSince1900 = (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
1756 22 : if (eType != OFTTime)
1757 18 : dfNumberOfDaysSince1900 += NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
1758 22 : if (eType == OFTDate)
1759 4 : VSIFPrintfL(fp, "<v>%d</v>\n", (int)(dfNumberOfDaysSince1900 + 0.1));
1760 : else
1761 18 : VSIFPrintfL(fp, "<v>%.16f</v>\n", dfNumberOfDaysSince1900);
1762 22 : VSIFPrintfL(fp, "</c>\n");
1763 : }
1764 : else
1765 : {
1766 88 : const char* pszVal = poFeature->GetFieldAsString(j);
1767 88 : std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
1768 : int nStringIndex;
1769 88 : if (oIter != oStringMap.end())
1770 28 : nStringIndex = oIter->second;
1771 : else
1772 : {
1773 60 : nStringIndex = (int)oStringList.size();
1774 60 : oStringMap[pszVal] = nStringIndex;
1775 120 : oStringList.push_back(pszVal);
1776 : }
1777 88 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
1778 88 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
1779 88 : VSIFPrintfL(fp, "</c>\n");
1780 : }
1781 : }
1782 : }
1783 98 : VSIFPrintfL(fp, "</row>\n");
1784 :
1785 98 : iRow ++;
1786 98 : delete poFeature;
1787 98 : poFeature = poLayer->GetNextFeature();
1788 : }
1789 32 : VSIFPrintfL(fp, "</sheetData>\n");
1790 32 : VSIFPrintfL(fp, "</worksheet>\n");
1791 32 : VSIFCloseL(fp);
1792 32 : }
1793 :
1794 : /************************************************************************/
1795 : /* WriteSharedStrings() */
1796 : /************************************************************************/
1797 :
1798 4 : static void WriteSharedStrings(const char* pszName,
1799 : std::map<std::string,int>& oStringMap,
1800 : std::vector<std::string>& oStringList)
1801 : {
1802 : VSILFILE* fp;
1803 :
1804 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName), "wb");
1805 4 : VSIFPrintfL(fp, XML_HEADER);
1806 : VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n",
1807 : MAIN_NS,
1808 4 : (int)oStringList.size());
1809 112 : for(int i = 0; i < (int)oStringList.size(); i++)
1810 : {
1811 108 : VSIFPrintfL(fp, "<si>\n");
1812 108 : char* pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
1813 108 : VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
1814 108 : CPLFree(pszXML);
1815 108 : VSIFPrintfL(fp, "</si>\n");
1816 : }
1817 4 : VSIFPrintfL(fp, "</sst>\n");
1818 4 : VSIFCloseL(fp);
1819 4 : }
1820 :
1821 : /************************************************************************/
1822 : /* WriteStyles() */
1823 : /************************************************************************/
1824 :
1825 4 : static void WriteStyles(const char* pszName)
1826 : {
1827 : VSILFILE* fp;
1828 :
1829 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName), "wb");
1830 4 : VSIFPrintfL(fp, XML_HEADER);
1831 4 : VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
1832 4 : VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
1833 4 : VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
1834 4 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
1835 4 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
1836 4 : VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
1837 4 : VSIFPrintfL(fp, "</numFmts>\n");
1838 4 : VSIFPrintfL(fp, "<fonts count=\"1\">\n");
1839 4 : VSIFPrintfL(fp, "<font>\n");
1840 4 : VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
1841 4 : VSIFPrintfL(fp, "<family val=\"2\"/>\n");
1842 4 : VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
1843 4 : VSIFPrintfL(fp, "</font>\n");
1844 4 : VSIFPrintfL(fp, "</fonts>\n");
1845 4 : VSIFPrintfL(fp, "<fills count=\"1\">\n");
1846 4 : VSIFPrintfL(fp, "<fill>\n");
1847 4 : VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
1848 4 : VSIFPrintfL(fp, "</fill>\n");
1849 4 : VSIFPrintfL(fp, "</fills>\n");
1850 4 : VSIFPrintfL(fp, "<borders count=\"1\">\n");
1851 4 : VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
1852 4 : VSIFPrintfL(fp, "<left/>\n");
1853 4 : VSIFPrintfL(fp, "<right/>\n");
1854 4 : VSIFPrintfL(fp, "<top/>\n");
1855 4 : VSIFPrintfL(fp, "<bottom/>\n");
1856 4 : VSIFPrintfL(fp, "<diagonal/>\n");
1857 4 : VSIFPrintfL(fp, "</border>\n");
1858 4 : VSIFPrintfL(fp, "</borders>\n");
1859 4 : VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
1860 4 : VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
1861 4 : VSIFPrintfL(fp, "</xf>\n");
1862 4 : VSIFPrintfL(fp, "</cellStyleXfs>\n");
1863 4 : VSIFPrintfL(fp, "<cellXfs count=\"4\">\n");
1864 4 : VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
1865 4 : VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
1866 4 : VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
1867 4 : VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
1868 4 : VSIFPrintfL(fp, "</cellXfs>\n");
1869 4 : VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
1870 4 : VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" name=\"Normal\" xfId=\"0\"/>\n");
1871 4 : VSIFPrintfL(fp, "</cellStyles>\n");
1872 4 : VSIFPrintfL(fp, "</styleSheet>\n");
1873 4 : VSIFCloseL(fp);
1874 4 : }
1875 :
1876 : /************************************************************************/
1877 : /* WriteWorkbookRels() */
1878 : /************************************************************************/
1879 :
1880 4 : static void WriteWorkbookRels(const char* pszName, int nLayers)
1881 : {
1882 : VSILFILE* fp;
1883 :
1884 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName), "wb");
1885 4 : VSIFPrintfL(fp, XML_HEADER);
1886 4 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
1887 4 : VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/styles\" Target=\"styles.xml\"/>\n", SCHEMA_OD_RS);
1888 36 : for(int i=0;i<nLayers;i++)
1889 : {
1890 : VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" Target=\"worksheets/sheet%d.xml\"/>\n",
1891 32 : 2 + i, SCHEMA_OD_RS, 1 + i);
1892 : }
1893 : VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" Target=\"sharedStrings.xml\"/>\n",
1894 4 : 2 + nLayers, SCHEMA_OD_RS);
1895 4 : VSIFPrintfL(fp, "</Relationships>\n");
1896 4 : VSIFCloseL(fp);
1897 4 : }
1898 :
1899 : /************************************************************************/
1900 : /* WriteDotRels() */
1901 : /************************************************************************/
1902 :
1903 4 : static void WriteDotRels(const char* pszName)
1904 : {
1905 : VSILFILE* fp;
1906 :
1907 4 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName), "wb");
1908 4 : VSIFPrintfL(fp, XML_HEADER);
1909 4 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
1910 4 : VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" Target=\"xl/workbook.xml\"/>\n", SCHEMA_OD_RS);
1911 4 : VSIFPrintfL(fp, "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" Target=\"docProps/core.xml\"/>\n", SCHEMA_PACKAGE_RS);
1912 4 : VSIFPrintfL(fp, "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" Target=\"docProps/app.xml\"/>\n", SCHEMA_OD_RS);
1913 4 : VSIFPrintfL(fp, "</Relationships>\n");
1914 4 : VSIFCloseL(fp);
1915 4 : }
1916 :
1917 : /************************************************************************/
1918 : /* SyncToDisk() */
1919 : /************************************************************************/
1920 :
1921 26 : OGRErr OGRXLSXDataSource::SyncToDisk()
1922 : {
1923 : int i;
1924 :
1925 26 : if (!bUpdated)
1926 22 : return OGRERR_NONE;
1927 :
1928 : VSIStatBufL sStat;
1929 4 : if (VSIStatL(pszName, &sStat) == 0)
1930 : {
1931 2 : if (VSIUnlink( pszName ) != 0)
1932 : {
1933 : CPLError(CE_Failure, CPLE_FileIO,
1934 0 : "Cannot delete %s", pszName);
1935 0 : return OGRERR_FAILURE;
1936 : }
1937 : }
1938 :
1939 : /* Cause all layers to be initialized */
1940 36 : for(int i = 0; i<nLayers; i++)
1941 : {
1942 32 : ((OGRXLSXLayer*)papoLayers[i])->GetLayerDefn();
1943 : }
1944 :
1945 : /* Maintain new ZIP files opened */
1946 4 : VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "wb");
1947 4 : if (fpZIP == NULL)
1948 : {
1949 : CPLError(CE_Failure, CPLE_FileIO,
1950 0 : "Cannot create %s", pszName);
1951 0 : return OGRERR_FAILURE;
1952 : }
1953 :
1954 4 : WriteContentTypes(pszName, nLayers);
1955 :
1956 : //VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
1957 4 : WriteApp(pszName);
1958 4 : WriteCore(pszName);
1959 :
1960 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
1961 4 : WriteWorkbook(pszName, this);
1962 :
1963 4 : std::map<std::string,int> oStringMap;
1964 4 : std::vector<std::string> oStringList;
1965 :
1966 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
1967 36 : for(i=0;i<nLayers;i++)
1968 : {
1969 32 : WriteLayer(pszName, GetLayer(i), i, oStringMap, oStringList);
1970 : }
1971 :
1972 4 : WriteSharedStrings(pszName, oStringMap, oStringList);
1973 4 : WriteStyles(pszName);
1974 :
1975 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
1976 4 : WriteWorkbookRels(pszName, nLayers);
1977 :
1978 : //VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
1979 4 : WriteDotRels(pszName);
1980 :
1981 : /* Now close ZIP file */
1982 4 : VSIFCloseL(fpZIP);
1983 :
1984 : /* Reset updated flag at datasource and layer level */
1985 4 : bUpdated = FALSE;
1986 36 : for(int i = 0; i<nLayers; i++)
1987 : {
1988 32 : ((OGRXLSXLayer*)papoLayers[i])->SetUpdated(FALSE);
1989 : }
1990 :
1991 4 : return OGRERR_NONE;
1992 : }
|