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 106 : OGRXLSXLayer::OGRXLSXLayer( OGRXLSXDataSource* poDSIn,
42 : int nSheetIdIn,
43 : const char * pszName,
44 : int bUpdatedIn) :
45 106 : OGRMemLayer(pszName, NULL, wkbNone)
46 : {
47 106 : bInit = FALSE;
48 106 : nSheetId = nSheetIdIn;
49 106 : poDS = poDSIn;
50 106 : bUpdated = bUpdatedIn;
51 106 : bHasHeaderLine = FALSE;
52 106 : }
53 :
54 : /************************************************************************/
55 : /* Init() */
56 : /************************************************************************/
57 :
58 3040 : void OGRXLSXLayer::Init()
59 : {
60 3040 : if (!bInit)
61 : {
62 63 : bInit = TRUE;
63 63 : CPLDebug("XLSX", "Init(%s)", GetName());
64 63 : poDS->BuildLayer(this, nSheetId);
65 : }
66 3040 : }
67 :
68 : /************************************************************************/
69 : /* Updated() */
70 : /************************************************************************/
71 :
72 957 : void OGRXLSXLayer::SetUpdated(int bUpdatedIn)
73 : {
74 957 : if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
75 : {
76 3 : bUpdated = TRUE;
77 3 : poDS->SetUpdated();
78 : }
79 954 : else if (bUpdated && !bUpdatedIn)
80 : {
81 11 : bUpdated = FALSE;
82 : }
83 957 : }
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 339 : OGRFeature* OGRXLSXLayer::GetNextFeature()
99 : {
100 339 : Init();
101 339 : OGRFeature* poFeature = OGRMemLayer::GetNextFeature();
102 339 : if (poFeature)
103 273 : poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine);
104 339 : return poFeature;
105 : }
106 :
107 : /************************************************************************/
108 : /* GetFeature() */
109 : /************************************************************************/
110 :
111 18 : OGRFeature* OGRXLSXLayer::GetFeature( long nFeatureId )
112 : {
113 18 : Init();
114 18 : OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine));
115 18 : if (poFeature)
116 2 : poFeature->SetFID(nFeatureId);
117 18 : return poFeature;
118 : }
119 :
120 : /************************************************************************/
121 : /* SetFeature() */
122 : /************************************************************************/
123 :
124 316 : OGRErr OGRXLSXLayer::SetFeature( OGRFeature *poFeature )
125 : {
126 316 : Init();
127 316 : if (poFeature == NULL)
128 0 : return OGRMemLayer::SetFeature(poFeature);
129 :
130 316 : long nFID = poFeature->GetFID();
131 316 : if (nFID != OGRNullFID)
132 1 : poFeature->SetFID(nFID - (1 + bHasHeaderLine));
133 316 : SetUpdated();
134 316 : OGRErr eErr = OGRMemLayer::SetFeature(poFeature);
135 316 : poFeature->SetFID(nFID);
136 316 : 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 13 : OGRXLSXDataSource::OGRXLSXDataSource()
155 :
156 : {
157 13 : pszName = NULL;
158 13 : bUpdatable = FALSE;
159 13 : bUpdated = FALSE;
160 :
161 13 : nLayers = 0;
162 13 : papoLayers = NULL;
163 :
164 13 : bFirstLineIsHeaders = FALSE;
165 :
166 13 : oParser = NULL;
167 13 : bStopParsing = FALSE;
168 13 : nWithoutEventCounter = 0;
169 13 : nDataHandlerCounter = 0;
170 13 : nStackDepth = 0;
171 13 : nDepth = 0;
172 13 : nCurLine = 0;
173 13 : nCurCol = 0;
174 13 : stateStack[0].eVal = STATE_DEFAULT;
175 13 : stateStack[0].nBeginDepth = 0;
176 13 : bInCellXFS = FALSE;
177 :
178 13 : poCurLayer = NULL;
179 :
180 : const char* pszXLSXFieldTypes =
181 13 : CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "");
182 13 : bAutodetectTypes = !EQUAL(pszXLSXFieldTypes, "STRING");
183 13 : }
184 :
185 : /************************************************************************/
186 : /* ~OGRXLSXDataSource() */
187 : /************************************************************************/
188 :
189 13 : OGRXLSXDataSource::~OGRXLSXDataSource()
190 :
191 : {
192 13 : SyncToDisk();
193 :
194 13 : CPLFree( pszName );
195 :
196 110 : for(int i=0;i<nLayers;i++)
197 97 : delete papoLayers[i];
198 13 : CPLFree( papoLayers );
199 13 : }
200 :
201 : /************************************************************************/
202 : /* TestCapability() */
203 : /************************************************************************/
204 :
205 11 : int OGRXLSXDataSource::TestCapability( const char * pszCap )
206 :
207 : {
208 11 : if( EQUAL(pszCap,ODsCCreateLayer) )
209 8 : return bUpdatable;
210 3 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
211 0 : return bUpdatable;
212 : else
213 3 : return FALSE;
214 : }
215 :
216 : /************************************************************************/
217 : /* GetLayer() */
218 : /************************************************************************/
219 :
220 377 : OGRLayer *OGRXLSXDataSource::GetLayer( int iLayer )
221 :
222 : {
223 377 : if (iLayer < 0 || iLayer >= nLayers)
224 2 : return NULL;
225 :
226 375 : return papoLayers[iLayer];
227 : }
228 :
229 : /************************************************************************/
230 : /* GetLayerCount() */
231 : /************************************************************************/
232 :
233 367 : int OGRXLSXDataSource::GetLayerCount()
234 : {
235 367 : return nLayers;
236 : }
237 :
238 : /************************************************************************/
239 : /* Open() */
240 : /************************************************************************/
241 :
242 12 : int OGRXLSXDataSource::Open( const char * pszFilename,
243 : VSILFILE* fpWorkbook,
244 : VSILFILE* fpSharedStrings,
245 : VSILFILE* fpStyles,
246 : int bUpdateIn )
247 :
248 : {
249 12 : bUpdatable = bUpdateIn;
250 :
251 12 : pszName = CPLStrdup( pszFilename );
252 :
253 12 : AnalyseWorkbook(fpWorkbook);
254 12 : AnalyseSharedStrings(fpSharedStrings);
255 12 : AnalyseStyles(fpStyles);
256 :
257 : /* Remove empty layers at the end, which tend to be there */
258 12 : while(nLayers > 1)
259 : {
260 20 : if (papoLayers[nLayers-1]->GetFeatureCount() == 0)
261 : {
262 9 : delete papoLayers[nLayers-1];
263 9 : nLayers --;
264 : }
265 : else
266 11 : break;
267 : }
268 :
269 12 : return TRUE;
270 : }
271 :
272 : /************************************************************************/
273 : /* Create() */
274 : /************************************************************************/
275 :
276 1 : int OGRXLSXDataSource::Create( const char * pszFilename, char **papszOptions )
277 : {
278 1 : bUpdated = TRUE;
279 1 : bUpdatable = TRUE;
280 :
281 1 : pszName = CPLStrdup( pszFilename );
282 :
283 1 : return TRUE;
284 : }
285 :
286 : /************************************************************************/
287 : /* startElementCbk() */
288 : /************************************************************************/
289 :
290 2179 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
291 : const char **ppszAttr)
292 : {
293 2179 : ((OGRXLSXDataSource*)pUserData)->startElementCbk(pszName, ppszAttr);
294 2179 : }
295 :
296 2179 : void OGRXLSXDataSource::startElementCbk(const char *pszName,
297 : const char **ppszAttr)
298 : {
299 2179 : if (bStopParsing) return;
300 :
301 2179 : nWithoutEventCounter = 0;
302 2179 : switch(stateStack[nStackDepth].eVal)
303 : {
304 835 : case STATE_DEFAULT: startElementDefault(pszName, ppszAttr); break;
305 218 : case STATE_SHEETDATA: startElementTable(pszName, ppszAttr); break;
306 559 : case STATE_ROW: startElementRow(pszName, ppszAttr); break;
307 567 : case STATE_CELL: startElementCell(pszName, ppszAttr); break;
308 : case STATE_TEXTV: break;
309 : default: break;
310 : }
311 2179 : nDepth++;
312 : }
313 :
314 : /************************************************************************/
315 : /* endElementCbk() */
316 : /************************************************************************/
317 :
318 2179 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
319 : {
320 2179 : ((OGRXLSXDataSource*)pUserData)->endElementCbk(pszName);
321 2179 : }
322 :
323 2179 : void OGRXLSXDataSource::endElementCbk(const char *pszName)
324 : {
325 2179 : if (bStopParsing) return;
326 :
327 2179 : nWithoutEventCounter = 0;
328 :
329 2179 : nDepth--;
330 2179 : switch(stateStack[nStackDepth].eVal)
331 : {
332 786 : case STATE_DEFAULT: break;
333 49 : case STATE_SHEETDATA: endElementTable(pszName); break;
334 218 : case STATE_ROW: endElementRow(pszName); break;
335 603 : case STATE_CELL: endElementCell(pszName); break;
336 : case STATE_TEXTV: break;
337 : default: break;
338 : }
339 :
340 2179 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
341 1398 : nStackDepth --;
342 : }
343 :
344 : /************************************************************************/
345 : /* dataHandlerCbk() */
346 : /************************************************************************/
347 :
348 1603 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
349 : {
350 1603 : ((OGRXLSXDataSource*)pUserData)->dataHandlerCbk(data, nLen);
351 1603 : }
352 :
353 1603 : void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
354 : {
355 1603 : if (bStopParsing) return;
356 :
357 1603 : nDataHandlerCounter ++;
358 1603 : 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 1603 : nWithoutEventCounter = 0;
368 :
369 1603 : switch(stateStack[nStackDepth].eVal)
370 : {
371 419 : case STATE_DEFAULT: break;
372 51 : case STATE_SHEETDATA: break;
373 192 : case STATE_ROW: break;
374 418 : case STATE_CELL: break;
375 523 : case STATE_TEXTV: dataHandlerTextV(data, nLen);
376 : default: break;
377 : }
378 : }
379 :
380 : /************************************************************************/
381 : /* PushState() */
382 : /************************************************************************/
383 :
384 1646 : void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
385 : {
386 1646 : if (nStackDepth + 1 == STACK_SIZE)
387 : {
388 0 : bStopParsing = TRUE;
389 0 : return;
390 : }
391 1646 : nStackDepth ++;
392 1646 : stateStack[nStackDepth].eVal = eVal;
393 1646 : stateStack[nStackDepth].nBeginDepth = nDepth;
394 : }
395 :
396 : /************************************************************************/
397 : /* GetAttributeValue() */
398 : /************************************************************************/
399 :
400 2359 : static const char* GetAttributeValue(const char **ppszAttr,
401 : const char* pszKey,
402 : const char* pszDefaultVal)
403 : {
404 8301 : while(*ppszAttr)
405 : {
406 5786 : if (strcmp(ppszAttr[0], pszKey) == 0)
407 2203 : return ppszAttr[1];
408 3583 : ppszAttr += 2;
409 : }
410 156 : return pszDefaultVal;
411 : }
412 :
413 : /************************************************************************/
414 : /* GetOGRFieldType() */
415 : /************************************************************************/
416 :
417 564 : OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char* pszValue,
418 : const char* pszValueType)
419 : {
420 564 : if (!bAutodetectTypes || pszValueType == NULL)
421 16 : return OFTString;
422 548 : else if (strcmp(pszValueType, "string") == 0)
423 205 : return OFTString;
424 343 : else if (strcmp(pszValueType, "float") == 0)
425 : {
426 252 : CPLValueType eValueType = CPLGetValueType(pszValue);
427 252 : if (eValueType == CPL_VALUE_STRING)
428 8 : return OFTString;
429 244 : else if (eValueType == CPL_VALUE_INTEGER)
430 159 : return OFTInteger;
431 : else
432 85 : return OFTReal;
433 : }
434 91 : else if (strcmp(pszValueType, "datetime") == 0)
435 : {
436 38 : return OFTDateTime;
437 : }
438 53 : else if (strcmp(pszValueType, "date") == 0)
439 : {
440 34 : return OFTDate;
441 : }
442 19 : else if (strcmp(pszValueType, "time") == 0)
443 : {
444 13 : return OFTTime;
445 : }
446 : else
447 6 : return OFTString;
448 : }
449 :
450 : /************************************************************************/
451 : /* SetField() */
452 : /************************************************************************/
453 :
454 578 : static void SetField(OGRFeature* poFeature,
455 : int i,
456 : const char* pszValue,
457 : const char* pszCellType)
458 : {
459 578 : if (pszValue[0] == '\0')
460 152 : return;
461 :
462 426 : OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();
463 :
464 479 : if (strcmp(pszCellType, "time") == 0 ||
465 : strcmp(pszCellType, "date") == 0 ||
466 : strcmp(pszCellType, "datetime") == 0)
467 : {
468 : struct tm sTm;
469 53 : 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 53 : NUMBER_OF_SECONDS_PER_DAY);
475 53 : CPLUnixTimeToYMDHMS(nUnixTime, &sTm);
476 :
477 96 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
478 : {
479 : poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
480 43 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec, 0);
481 : }
482 10 : else if (strcmp(pszCellType, "time") == 0)
483 : {
484 : poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d",
485 2 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
486 : }
487 8 : else if (strcmp(pszCellType, "date") == 0)
488 : {
489 : poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d",
490 4 : 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 4 : sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
497 : }
498 : }
499 : else
500 373 : poFeature->SetField(i, pszValue);
501 : }
502 :
503 : /************************************************************************/
504 : /* DetectHeaderLine() */
505 : /************************************************************************/
506 :
507 38 : void OGRXLSXDataSource::DetectHeaderLine()
508 :
509 : {
510 38 : int bHeaderLineCandidate = TRUE;
511 : size_t i;
512 172 : for(i = 0; i < apoFirstLineTypes.size(); i++)
513 : {
514 152 : 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 18 : bHeaderLineCandidate = FALSE;
519 18 : break;
520 : }
521 : }
522 :
523 38 : size_t nCountTextOnCurLine = 0;
524 38 : size_t nCountNonEmptyOnCurLine = 0;
525 161 : for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
526 : {
527 123 : 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 28 : nCountTextOnCurLine ++;
532 : }
533 95 : else if (apoCurLineTypes[i] != "")
534 : {
535 88 : nCountNonEmptyOnCurLine ++;
536 : }
537 : }
538 :
539 38 : const char* pszXLSXHeaders = CPLGetConfigOption("OGR_XLSX_HEADERS", "");
540 38 : bFirstLineIsHeaders = FALSE;
541 38 : if (EQUAL(pszXLSXHeaders, "FORCE"))
542 1 : bFirstLineIsHeaders = TRUE;
543 37 : else if (EQUAL(pszXLSXHeaders, "DISABLE"))
544 2 : bFirstLineIsHeaders = FALSE;
545 35 : else if (bHeaderLineCandidate &&
546 : apoFirstLineTypes.size() != 0 &&
547 : apoFirstLineTypes.size() == apoCurLineTypes.size() &&
548 : nCountTextOnCurLine != apoFirstLineTypes.size() &&
549 : nCountNonEmptyOnCurLine != 0)
550 : {
551 7 : bFirstLineIsHeaders = TRUE;
552 : }
553 : CPLDebug("XLSX", "%s %s",
554 38 : poCurLayer->GetName(),
555 76 : bFirstLineIsHeaders ? "has header line" : "has no header line");
556 38 : }
557 :
558 : /************************************************************************/
559 : /* startElementDefault() */
560 : /************************************************************************/
561 :
562 835 : void OGRXLSXDataSource::startElementDefault(const char *pszName,
563 : const char **ppszAttr)
564 : {
565 835 : if (strcmp(pszName, "sheetData") == 0)
566 : {
567 49 : apoFirstLineValues.resize(0);
568 98 : apoFirstLineTypes.resize(0);
569 49 : nCurLine = 0;
570 49 : PushState(STATE_SHEETDATA);
571 : }
572 835 : }
573 :
574 : /************************************************************************/
575 : /* startElementTable() */
576 : /************************************************************************/
577 :
578 218 : void OGRXLSXDataSource::startElementTable(const char *pszName,
579 : const char **ppszAttr)
580 : {
581 218 : if (strcmp(pszName, "row") == 0)
582 : {
583 218 : PushState(STATE_ROW);
584 :
585 : int nNewCurLine = atoi(
586 218 : GetAttributeValue(ppszAttr, "r", "0")) - 1;
587 498 : for(;nCurLine<nNewCurLine;)
588 : {
589 62 : nCurCol = 0;
590 62 : apoCurLineValues.resize(0);
591 124 : apoCurLineTypes.resize(0);
592 62 : endElementRow("row");
593 : }
594 218 : nCurCol = 0;
595 218 : apoCurLineValues.resize(0);
596 436 : apoCurLineTypes.resize(0);
597 : }
598 218 : }
599 :
600 : /************************************************************************/
601 : /* endElementTable() */
602 : /************************************************************************/
603 :
604 49 : void OGRXLSXDataSource::endElementTable(const char *pszName)
605 : {
606 49 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
607 : {
608 49 : CPLAssert(strcmp(pszName, "sheetData") == 0);
609 :
610 49 : if (nCurLine == 0 ||
611 : (nCurLine == 1 && apoFirstLineValues.size() == 0))
612 : {
613 : /* We could remove empty sheet, but too late now */
614 : }
615 40 : else if (nCurLine == 1)
616 : {
617 : /* If we have only one single line in the sheet */
618 : size_t i;
619 6 : for(i = 0; i < apoFirstLineValues.size(); i++)
620 : {
621 4 : const char* pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
622 : OGRFieldType eType = GetOGRFieldType(apoFirstLineValues[i].c_str(),
623 4 : apoFirstLineTypes[i].c_str());
624 4 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
625 4 : poCurLayer->CreateField(&oFieldDefn);
626 : }
627 :
628 2 : OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
629 6 : for(i = 0; i < apoFirstLineValues.size(); i++)
630 : {
631 : SetField(poFeature, i, apoFirstLineValues[i].c_str(),
632 4 : apoFirstLineTypes[i].c_str());
633 : }
634 2 : poCurLayer->CreateFeature(poFeature);
635 2 : delete poFeature;
636 : }
637 :
638 49 : if (poCurLayer)
639 : {
640 49 : ((OGRMemLayer*)poCurLayer)->SetUpdatable(bUpdatable);
641 49 : ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE);
642 49 : ((OGRXLSXLayer*)poCurLayer)->SetUpdated(FALSE);
643 : }
644 :
645 49 : poCurLayer = NULL;
646 : }
647 49 : }
648 :
649 : /************************************************************************/
650 : /* startElementRow() */
651 : /************************************************************************/
652 :
653 559 : void OGRXLSXDataSource::startElementRow(const char *pszName,
654 : const char **ppszAttr)
655 : {
656 559 : if (strcmp(pszName, "c") == 0)
657 : {
658 559 : PushState(STATE_CELL);
659 :
660 559 : const char* pszR = GetAttributeValue(ppszAttr, "r", NULL);
661 559 : if (pszR)
662 : {
663 : /* Convert col number from base 26 */
664 559 : int nNewCurCol = (pszR[0] - 'A');
665 559 : int i = 1;
666 1118 : while(pszR[i] >= 'A' && pszR[i] <= 'Z')
667 : {
668 0 : nNewCurCol = nNewCurCol * 26 + (pszR[i] - 'A');
669 0 : i ++;
670 : }
671 1350 : for(;nCurCol<nNewCurCol;nCurCol++)
672 : {
673 116 : apoCurLineValues.push_back("");
674 232 : apoCurLineTypes.push_back("");
675 : }
676 : }
677 :
678 559 : osValueType = "float";
679 :
680 559 : const char* pszS = GetAttributeValue(ppszAttr, "s", "-1");
681 559 : int nS = atoi(pszS);
682 1033 : if (nS >= 0 && nS < (int)apoStyles.size())
683 : {
684 474 : OGRFieldType eType = apoStyles[nS];
685 474 : if (eType == OFTDateTime)
686 25 : osValueType = "datetime";
687 449 : else if (eType == OFTDate)
688 20 : osValueType = "date";
689 429 : else if (eType == OFTTime)
690 8 : osValueType = "time";
691 : }
692 85 : else if (nS != -1)
693 0 : CPLDebug("XLSX", "Cannot find style %d", nS);
694 :
695 559 : const char* pszT = GetAttributeValue(ppszAttr, "t", "");
696 559 : if ( EQUAL(pszT,"s"))
697 246 : osValueType = "stringLookup";
698 313 : else if( EQUAL(pszT,"inlineStr") )
699 26 : osValueType = "string";
700 :
701 559 : osValue = "";
702 : }
703 559 : }
704 :
705 : /************************************************************************/
706 : /* endElementRow() */
707 : /************************************************************************/
708 :
709 280 : void OGRXLSXDataSource::endElementRow(const char *pszName)
710 : {
711 280 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
712 : {
713 280 : 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 280 : if (nCurLine == 0)
720 : {
721 40 : apoFirstLineTypes = apoCurLineTypes;
722 40 : 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 280 : if (nCurLine == 1)
736 : {
737 38 : DetectHeaderLine();
738 :
739 38 : poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);
740 :
741 38 : if (bFirstLineIsHeaders)
742 : {
743 105 : for(i = 0; i < apoFirstLineValues.size(); i++)
744 : {
745 97 : const char* pszFieldName = apoFirstLineValues[i].c_str();
746 97 : if (pszFieldName[0] == '\0')
747 0 : pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
748 97 : OGRFieldType eType = OFTString;
749 97 : if (i < apoCurLineValues.size())
750 : {
751 : eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
752 97 : apoCurLineTypes[i].c_str());
753 : }
754 97 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
755 97 : poCurLayer->CreateField(&oFieldDefn);
756 : }
757 : }
758 : else
759 : {
760 96 : for(i = 0; i < apoFirstLineValues.size(); i++)
761 : {
762 : const char* pszFieldName =
763 66 : CPLSPrintf("Field%d", (int)i + 1);
764 : OGRFieldType eType = GetOGRFieldType(
765 : apoFirstLineValues[i].c_str(),
766 66 : apoFirstLineTypes[i].c_str());
767 66 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
768 66 : poCurLayer->CreateField(&oFieldDefn);
769 : }
770 :
771 30 : poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
772 96 : for(i = 0; i < apoFirstLineValues.size(); i++)
773 : {
774 : SetField(poFeature, i, apoFirstLineValues[i].c_str(),
775 66 : apoFirstLineTypes[i].c_str());
776 : }
777 30 : poCurLayer->CreateFeature(poFeature);
778 30 : delete poFeature;
779 : }
780 : }
781 :
782 280 : if (nCurLine >= 1)
783 : {
784 : /* Add new fields found on following lines. */
785 480 : if (apoCurLineValues.size() >
786 240 : (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
787 : {
788 77 : for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
789 : i < apoCurLineValues.size();
790 : i++)
791 : {
792 : const char* pszFieldName =
793 50 : CPLSPrintf("Field%d", (int)i + 1);
794 : OGRFieldType eType = GetOGRFieldType(
795 : apoCurLineValues[i].c_str(),
796 50 : apoCurLineTypes[i].c_str());
797 50 : OGRFieldDefn oFieldDefn(pszFieldName, eType);
798 50 : poCurLayer->CreateField(&oFieldDefn);
799 : }
800 : }
801 :
802 : /* Update field type if necessary */
803 240 : if (bAutodetectTypes)
804 : {
805 709 : for(i = 0; i < apoCurLineValues.size(); i++)
806 : {
807 474 : if (apoCurLineValues[i].size())
808 : {
809 : OGRFieldType eValType = GetOGRFieldType(
810 : apoCurLineValues[i].c_str(),
811 347 : apoCurLineTypes[i].c_str());
812 : OGRFieldType eFieldType =
813 347 : poCurLayer->GetLayerDefn()->GetFieldDefn(i)->GetType();
814 347 : if (eFieldType == OFTDateTime &&
815 : (eValType == OFTDate || eValType == OFTTime) )
816 : {
817 : /* ok */
818 : }
819 347 : else if (eFieldType == OFTReal && eValType == OFTInteger)
820 : {
821 : /* ok */;
822 : }
823 343 : else if (eFieldType != OFTString && eValType != eFieldType)
824 : {
825 : OGRFieldDefn oNewFieldDefn(
826 14 : poCurLayer->GetLayerDefn()->GetFieldDefn(i));
827 18 : if ((eFieldType == OFTDate || eFieldType == OFTTime) &&
828 : eValType == OFTDateTime)
829 4 : oNewFieldDefn.SetType(OFTDateTime);
830 14 : else if (eFieldType == OFTInteger &&
831 : eValType == OFTReal)
832 4 : oNewFieldDefn.SetType(OFTReal);
833 : else
834 6 : oNewFieldDefn.SetType(OFTString);
835 : poCurLayer->AlterFieldDefn(i, &oNewFieldDefn,
836 14 : ALTER_TYPE_FLAG);
837 : }
838 : }
839 : }
840 : }
841 :
842 : /* Add feature for current line */
843 240 : poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
844 748 : for(i = 0; i < apoCurLineValues.size(); i++)
845 : {
846 : SetField(poFeature, i, apoCurLineValues[i].c_str(),
847 508 : apoCurLineTypes[i].c_str());
848 : }
849 240 : poCurLayer->CreateFeature(poFeature);
850 240 : delete poFeature;
851 : }
852 :
853 280 : nCurLine++;
854 : }
855 280 : }
856 :
857 : /************************************************************************/
858 : /* startElementCell() */
859 : /************************************************************************/
860 :
861 567 : void OGRXLSXDataSource::startElementCell(const char *pszName,
862 : const char **ppszAttr)
863 : {
864 567 : if (osValue.size() == 0 && strcmp(pszName, "v") == 0)
865 : {
866 497 : PushState(STATE_TEXTV);
867 : }
868 70 : else if (osValue.size() == 0 && strcmp(pszName, "t") == 0)
869 : {
870 26 : PushState(STATE_TEXTV);
871 : }
872 567 : }
873 :
874 : /************************************************************************/
875 : /* endElementCell() */
876 : /************************************************************************/
877 :
878 603 : void OGRXLSXDataSource::endElementCell(const char *pszName)
879 : {
880 603 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
881 : {
882 559 : CPLAssert(strcmp(pszName, "c") == 0);
883 :
884 559 : if (osValueType == "stringLookup")
885 : {
886 246 : int nIndex = atoi(osValue);
887 246 : if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
888 246 : osValue = apoSharedStrings[nIndex];
889 : else
890 0 : CPLDebug("XLSX", "Cannot find string %d", nIndex);
891 246 : osValueType = "string";
892 : }
893 :
894 559 : apoCurLineValues.push_back(osValue);
895 559 : apoCurLineTypes.push_back(osValueType);
896 :
897 559 : nCurCol += 1;
898 : }
899 603 : }
900 :
901 : /************************************************************************/
902 : /* dataHandlerTextV() */
903 : /************************************************************************/
904 :
905 523 : void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
906 : {
907 523 : osValue.append(data, nLen);
908 523 : }
909 :
910 : /************************************************************************/
911 : /* BuildLayer() */
912 : /************************************************************************/
913 :
914 63 : void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer* poLayer, int nSheetId)
915 : {
916 63 : poCurLayer = poLayer;
917 :
918 : CPLString osSheetFilename(
919 63 : CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, nSheetId));
920 63 : const char* pszSheetFilename = osSheetFilename.c_str();
921 63 : VSILFILE* fp = VSIFOpenL(pszSheetFilename, "rb");
922 63 : if (fp == NULL)
923 : return;
924 :
925 49 : int bUpdatedBackup = bUpdated;
926 :
927 49 : oParser = OGRCreateExpatXMLParser();
928 49 : XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
929 49 : XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
930 49 : XML_SetUserData(oParser, this);
931 :
932 49 : VSIFSeekL( fp, 0, SEEK_SET );
933 :
934 49 : bStopParsing = FALSE;
935 49 : nWithoutEventCounter = 0;
936 49 : nDataHandlerCounter = 0;
937 49 : nStackDepth = 0;
938 49 : nDepth = 0;
939 49 : stateStack[0].eVal = STATE_DEFAULT;
940 49 : 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 49 : XML_ParserFree(oParser);
964 49 : oParser = NULL;
965 :
966 49 : 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 49 : VSIFCloseL(fp);
974 :
975 49 : bUpdated = bUpdatedBackup;
976 : }
977 :
978 : /************************************************************************/
979 : /* startElementSSCbk() */
980 : /************************************************************************/
981 :
982 605 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszName,
983 : const char **ppszAttr)
984 : {
985 605 : ((OGRXLSXDataSource*)pUserData)->startElementSSCbk(pszName, ppszAttr);
986 605 : }
987 :
988 605 : void OGRXLSXDataSource::startElementSSCbk(const char *pszName,
989 : const char **ppszAttr)
990 : {
991 605 : if (bStopParsing) return;
992 :
993 605 : nWithoutEventCounter = 0;
994 605 : switch(stateStack[nStackDepth].eVal)
995 : {
996 : case STATE_DEFAULT:
997 : {
998 605 : if (strcmp(pszName,"t") == 0)
999 : {
1000 297 : PushState(STATE_T);
1001 297 : osCurrentString = "";
1002 : }
1003 : break;
1004 : }
1005 : default:
1006 : break;
1007 : }
1008 605 : nDepth++;
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* endElementSSCbk() */
1013 : /************************************************************************/
1014 :
1015 605 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszName)
1016 : {
1017 605 : ((OGRXLSXDataSource*)pUserData)->endElementSSCbk(pszName);
1018 605 : }
1019 :
1020 605 : void OGRXLSXDataSource::endElementSSCbk(const char *pszName)
1021 : {
1022 605 : if (bStopParsing) return;
1023 :
1024 605 : nWithoutEventCounter = 0;
1025 :
1026 605 : nDepth--;
1027 605 : switch(stateStack[nStackDepth].eVal)
1028 : {
1029 308 : case STATE_DEFAULT: break;
1030 : case STATE_T:
1031 : {
1032 297 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1033 : {
1034 297 : apoSharedStrings.push_back(osCurrentString);
1035 : }
1036 : break;
1037 : }
1038 : default: break;
1039 : }
1040 :
1041 605 : if (stateStack[nStackDepth].nBeginDepth == nDepth)
1042 308 : nStackDepth --;
1043 : }
1044 :
1045 : /************************************************************************/
1046 : /* dataHandlerSSCbk() */
1047 : /************************************************************************/
1048 :
1049 491 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data, int nLen)
1050 : {
1051 491 : ((OGRXLSXDataSource*)pUserData)->dataHandlerSSCbk(data, nLen);
1052 491 : }
1053 :
1054 491 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
1055 : {
1056 491 : if (bStopParsing) return;
1057 :
1058 491 : nDataHandlerCounter ++;
1059 491 : 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 491 : nWithoutEventCounter = 0;
1069 :
1070 491 : switch(stateStack[nStackDepth].eVal)
1071 : {
1072 164 : case STATE_DEFAULT: break;
1073 327 : case STATE_T: osCurrentString.append(data, nLen); break;
1074 : default: break;
1075 : }
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* AnalyseSharedStrings() */
1080 : /************************************************************************/
1081 :
1082 12 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE* fpSharedStrings)
1083 : {
1084 12 : if (fpSharedStrings == NULL)
1085 1 : return;
1086 :
1087 11 : oParser = OGRCreateExpatXMLParser();
1088 11 : XML_SetElementHandler(oParser, ::startElementSSCbk, ::endElementSSCbk);
1089 11 : XML_SetCharacterDataHandler(oParser, ::dataHandlerSSCbk);
1090 11 : XML_SetUserData(oParser, this);
1091 :
1092 11 : VSIFSeekL( fpSharedStrings, 0, SEEK_SET );
1093 :
1094 11 : bStopParsing = FALSE;
1095 11 : nWithoutEventCounter = 0;
1096 11 : nDataHandlerCounter = 0;
1097 11 : nStackDepth = 0;
1098 11 : nDepth = 0;
1099 11 : stateStack[0].eVal = STATE_DEFAULT;
1100 11 : 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 11 : XML_ParserFree(oParser);
1124 11 : oParser = NULL;
1125 :
1126 11 : 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 11 : VSIFCloseL(fpSharedStrings);
1134 11 : fpSharedStrings = NULL;
1135 : }
1136 :
1137 :
1138 : /************************************************************************/
1139 : /* startElementWBCbk() */
1140 : /************************************************************************/
1141 :
1142 196 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszName,
1143 : const char **ppszAttr)
1144 : {
1145 196 : ((OGRXLSXDataSource*)pUserData)->startElementWBCbk(pszName, ppszAttr);
1146 196 : }
1147 :
1148 196 : void OGRXLSXDataSource::startElementWBCbk(const char *pszName,
1149 : const char **ppszAttr)
1150 : {
1151 196 : if (bStopParsing) return;
1152 :
1153 196 : nWithoutEventCounter = 0;
1154 196 : if (strcmp(pszName,"sheet") == 0)
1155 : {
1156 98 : const char* pszSheetName = GetAttributeValue(ppszAttr, "name", NULL);
1157 98 : const char* pszSheetId = GetAttributeValue(ppszAttr, "sheetId", NULL);
1158 98 : if (pszSheetName && pszSheetId)
1159 : {
1160 98 : int nSheetId = atoi(pszSheetId);
1161 98 : papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
1162 98 : papoLayers[nLayers++] = new OGRXLSXLayer(this, nSheetId, pszSheetName);
1163 : }
1164 : }
1165 : }
1166 :
1167 : /************************************************************************/
1168 : /* AnalyseWorkbook() */
1169 : /************************************************************************/
1170 :
1171 12 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE* fpWorkbook)
1172 : {
1173 12 : oParser = OGRCreateExpatXMLParser();
1174 12 : XML_SetElementHandler(oParser, ::startElementWBCbk, NULL);
1175 12 : XML_SetUserData(oParser, this);
1176 :
1177 12 : VSIFSeekL( fpWorkbook, 0, SEEK_SET );
1178 :
1179 12 : bStopParsing = FALSE;
1180 12 : nWithoutEventCounter = 0;
1181 12 : 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 12 : XML_ParserFree(oParser);
1205 12 : oParser = NULL;
1206 :
1207 12 : 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 12 : VSIFCloseL(fpWorkbook);
1215 12 : }
1216 :
1217 :
1218 : /************************************************************************/
1219 : /* startElementStylesCbk() */
1220 : /************************************************************************/
1221 :
1222 826 : static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszName,
1223 : const char **ppszAttr)
1224 : {
1225 826 : ((OGRXLSXDataSource*)pUserData)->startElementStylesCbk(pszName, ppszAttr);
1226 826 : }
1227 :
1228 826 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszName,
1229 : const char **ppszAttr)
1230 : {
1231 826 : if (bStopParsing) return;
1232 :
1233 826 : nWithoutEventCounter = 0;
1234 826 : if (strcmp(pszName,"numFmt") == 0)
1235 : {
1236 80 : const char* pszFormatCode = GetAttributeValue(ppszAttr, "formatCode", NULL);
1237 80 : const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1238 80 : int nNumFmtId = atoi(pszNumFmtId);
1239 80 : if (pszFormatCode && nNumFmtId >= 164)
1240 : {
1241 : int bHasDate = strstr(pszFormatCode, "DD") != NULL ||
1242 80 : strstr(pszFormatCode, "YY") != NULL;
1243 80 : int bHasTime = strstr(pszFormatCode, "HH") != NULL;
1244 91 : if (bHasDate && bHasTime)
1245 11 : apoMapStyleFormats[nNumFmtId] = OFTDateTime;
1246 69 : else if (bHasDate)
1247 11 : apoMapStyleFormats[nNumFmtId] = OFTDate;
1248 58 : else if (bHasTime)
1249 11 : apoMapStyleFormats[nNumFmtId] = OFTTime;
1250 : else
1251 47 : apoMapStyleFormats[nNumFmtId] = OFTReal;
1252 : }
1253 : }
1254 746 : else if (strcmp(pszName,"cellXfs") == 0)
1255 : {
1256 12 : bInCellXFS = TRUE;
1257 : }
1258 734 : else if (bInCellXFS && strcmp(pszName,"xf") == 0)
1259 : {
1260 108 : const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
1261 108 : int nNumFmtId = atoi(pszNumFmtId);
1262 108 : OGRFieldType eType = OFTReal;
1263 108 : if (nNumFmtId >= 0)
1264 : {
1265 108 : if (nNumFmtId < 164)
1266 : {
1267 : // From http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
1268 1 : if (nNumFmtId >= 14 && nNumFmtId <= 17)
1269 0 : eType = OFTDate;
1270 1 : else if (nNumFmtId >= 18 && nNumFmtId <= 21)
1271 0 : eType = OFTTime;
1272 1 : else if (nNumFmtId == 22)
1273 0 : eType = OFTDateTime;
1274 : }
1275 : else
1276 : {
1277 107 : std::map<int, OGRFieldType>::iterator oIter = apoMapStyleFormats.find(nNumFmtId);
1278 107 : if (oIter != apoMapStyleFormats.end())
1279 107 : 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 108 : apoStyles.push_back(eType);
1287 : }
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* endElementStylesCbk() */
1292 : /************************************************************************/
1293 :
1294 826 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszName)
1295 : {
1296 826 : ((OGRXLSXDataSource*)pUserData)->endElementStylesCbk(pszName);
1297 826 : }
1298 :
1299 826 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszName)
1300 : {
1301 826 : if (bStopParsing) return;
1302 :
1303 826 : nWithoutEventCounter = 0;
1304 826 : if (strcmp(pszName,"cellXfs") == 0)
1305 : {
1306 12 : bInCellXFS = FALSE;
1307 : }
1308 : }
1309 :
1310 : /************************************************************************/
1311 : /* AnalyseStyles() */
1312 : /************************************************************************/
1313 :
1314 12 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE* fpStyles)
1315 : {
1316 12 : if (fpStyles == NULL)
1317 0 : return;
1318 :
1319 12 : oParser = OGRCreateExpatXMLParser();
1320 12 : XML_SetElementHandler(oParser, ::startElementStylesCbk, ::endElementStylesCbk);
1321 12 : XML_SetUserData(oParser, this);
1322 :
1323 12 : VSIFSeekL( fpStyles, 0, SEEK_SET );
1324 :
1325 12 : bStopParsing = FALSE;
1326 12 : nWithoutEventCounter = 0;
1327 12 : nDataHandlerCounter = 0;
1328 12 : 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 12 : XML_ParserFree(oParser);
1352 12 : oParser = NULL;
1353 :
1354 12 : 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 12 : VSIFCloseL(fpStyles);
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* CreateLayer() */
1366 : /************************************************************************/
1367 :
1368 : OGRLayer *
1369 8 : 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 8 : 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 36 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
1395 : {
1396 28 : 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 8 : OGRLayer* poLayer = new OGRXLSXLayer(this, nLayers + 1, pszLayerName, TRUE);
1419 :
1420 8 : papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
1421 8 : papoLayers[nLayers] = poLayer;
1422 8 : nLayers ++;
1423 :
1424 8 : bUpdated = TRUE;
1425 :
1426 8 : 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 30 : static void WriteOverride(VSILFILE* fp, const char* pszPartName, const char* pszContentType)
1505 : {
1506 : VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
1507 30 : pszPartName, pszContentType);
1508 30 : }
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 2 : static void WriteContentTypes(const char* pszName, int nLayers)
1522 : {
1523 : VSILFILE* fp;
1524 :
1525 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName), "wb");
1526 2 : VSIFPrintfL(fp, XML_HEADER);
1527 2 : VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
1528 2 : WriteOverride(fp, "/_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml");
1529 2 : WriteOverride(fp, "/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml");
1530 2 : WriteOverride(fp, "/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml");
1531 2 : WriteOverride(fp, "/xl/_rels/workbook.xml.rels", "application/vnd.openxmlformats-package.relationships+xml");
1532 18 : for(int i=0;i<nLayers;i++)
1533 : {
1534 : WriteOverride(fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i+1),
1535 16 : "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
1536 : }
1537 2 : WriteOverride(fp, "/xl/styles.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
1538 2 : WriteOverride(fp, "/xl/workbook.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
1539 2 : WriteOverride(fp, "/xl/sharedStrings.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
1540 2 : VSIFPrintfL(fp, "</Types>\n");
1541 2 : VSIFCloseL(fp);
1542 2 : }
1543 :
1544 : /************************************************************************/
1545 : /* WriteApp() */
1546 : /************************************************************************/
1547 :
1548 2 : static void WriteApp(const char* pszName)
1549 : {
1550 : VSILFILE* fp;
1551 :
1552 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName), "wb");
1553 2 : VSIFPrintfL(fp, XML_HEADER);
1554 : VSIFPrintfL(fp, "<Properties xmlns=\"%s/extended-properties\" "
1555 2 : "xmlns:vt=\"%s/docPropsVTypes\">\n", SCHEMA_OD, SCHEMA_OD);
1556 2 : VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
1557 2 : VSIFPrintfL(fp, "</Properties>\n");
1558 2 : VSIFCloseL(fp);
1559 2 : }
1560 :
1561 : /************************************************************************/
1562 : /* WriteCore() */
1563 : /************************************************************************/
1564 :
1565 2 : static void WriteCore(const char* pszName)
1566 : {
1567 : VSILFILE* fp;
1568 :
1569 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName), "wb");
1570 2 : 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 2 : "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n", SCHEMA_PACKAGE);
1576 2 : VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
1577 2 : VSIFPrintfL(fp, "</cp:coreProperties>\n");
1578 2 : VSIFCloseL(fp);
1579 2 : }
1580 :
1581 : /************************************************************************/
1582 : /* WriteWorkbook() */
1583 : /************************************************************************/
1584 :
1585 2 : static void WriteWorkbook(const char* pszName, OGRDataSource* poDS)
1586 : {
1587 : VSILFILE* fp;
1588 :
1589 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName), "wb");
1590 2 : VSIFPrintfL(fp, XML_HEADER);
1591 2 : VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
1592 2 : 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 2 : VSIFPrintfL(fp, "<sheets>\n");
1603 18 : for(int i=0;i<poDS->GetLayerCount();i++)
1604 : {
1605 16 : OGRXLSXLayer* poLayer = (OGRXLSXLayer*) poDS->GetLayer(i);
1606 16 : const char* pszLayerName = poLayer->GetName();
1607 16 : char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
1608 16 : VSIFPrintfL(fp, "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" r:id=\"rId%d\"/>\n", pszXML, i+1, i+2);
1609 16 : CPLFree(pszXML);
1610 : }
1611 2 : VSIFPrintfL(fp, "</sheets>\n");
1612 2 : VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" iterate=\"false\" iterateDelta=\"0.001\"/>\n");
1613 2 : VSIFPrintfL(fp, "</workbook>\n");
1614 2 : VSIFCloseL(fp);
1615 2 : }
1616 :
1617 : /************************************************************************/
1618 : /* BuildColString() */
1619 : /************************************************************************/
1620 :
1621 103 : static void BuildColString(char szCol[5], int nCol)
1622 : {
1623 103 : int k = 0;
1624 103 : szCol[k++] = (nCol % 26) + 'A';
1625 206 : while(nCol >= 26)
1626 : {
1627 0 : nCol /= 26;
1628 0 : szCol[k++] = (nCol % 26) + 'A';
1629 : }
1630 103 : szCol[k] = 0;
1631 103 : 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 103 : }
1638 :
1639 : /************************************************************************/
1640 : /* WriteLayer() */
1641 : /************************************************************************/
1642 :
1643 16 : 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 16 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, iLayer + 1), "wb");
1651 16 : VSIFPrintfL(fp, XML_HEADER);
1652 16 : 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 16 : poLayer->ResetReading();
1662 :
1663 16 : OGRFeature* poFeature = poLayer->GetNextFeature();
1664 :
1665 16 : OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
1666 16 : int bHasHeaders = FALSE;
1667 16 : int iRow = 1;
1668 :
1669 16 : VSIFPrintfL(fp, "<cols>\n");
1670 62 : for(j=0;j<poFDefn->GetFieldCount();j++)
1671 : {
1672 46 : int nWidth = 15;
1673 46 : if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
1674 5 : nWidth = 25;
1675 : VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
1676 46 : j+1, 1024, nWidth);
1677 :
1678 46 : if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
1679 : CPLSPrintf("Field%d", j+1)) != 0)
1680 24 : bHasHeaders = TRUE;
1681 : }
1682 16 : VSIFPrintfL(fp, "</cols>\n");
1683 :
1684 16 : VSIFPrintfL(fp, "<sheetData>\n");
1685 :
1686 16 : if (bHasHeaders && poFeature != NULL)
1687 : {
1688 2 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
1689 26 : for(j=0;j<poFDefn->GetFieldCount();j++)
1690 : {
1691 24 : const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
1692 24 : std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
1693 : int nStringIndex;
1694 24 : if (oIter != oStringMap.end())
1695 0 : nStringIndex = oIter->second;
1696 : else
1697 : {
1698 24 : nStringIndex = (int)oStringList.size();
1699 24 : oStringMap[pszVal] = nStringIndex;
1700 48 : oStringList.push_back(pszVal);
1701 : }
1702 :
1703 : char szCol[5];
1704 24 : BuildColString(szCol, j);
1705 :
1706 24 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
1707 24 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
1708 24 : VSIFPrintfL(fp, "</c>\n");
1709 : }
1710 2 : VSIFPrintfL(fp, "</row>\n");
1711 :
1712 2 : iRow ++;
1713 : }
1714 :
1715 81 : while(poFeature != NULL)
1716 : {
1717 49 : VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
1718 301 : for(j=0;j<poFeature->GetFieldCount();j++)
1719 : {
1720 252 : if (poFeature->IsFieldSet(j))
1721 : {
1722 : char szCol[5];
1723 79 : BuildColString(szCol, j);
1724 :
1725 79 : OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType();
1726 :
1727 79 : if (eType == OFTReal)
1728 : {
1729 15 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
1730 15 : VSIFPrintfL(fp, "<v>%.16f</v>\n", poFeature->GetFieldAsDouble(j));
1731 15 : VSIFPrintfL(fp, "</c>\n");
1732 : }
1733 64 : else if (eType == OFTInteger)
1734 : {
1735 9 : VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
1736 9 : VSIFPrintfL(fp, "<v>%d</v>\n", poFeature->GetFieldAsInteger(j));
1737 9 : VSIFPrintfL(fp, "</c>\n");
1738 : }
1739 66 : else if (eType == OFTDate || eType == OFTDateTime || eType == OFTTime)
1740 : {
1741 : VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", szCol, iRow,
1742 11 : (eType == OFTDate) ? 1 : (eType == OFTDateTime) ? 2 : 3);
1743 : int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
1744 : poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
1745 11 : &nHour, &nMinute, &nSecond, &nTZFlag);
1746 : struct tm brokendowntime;
1747 11 : memset(&brokendowntime, 0, sizeof(brokendowntime));
1748 11 : brokendowntime.tm_year = (eType == OFTTime) ? 70 : nYear - 1900;
1749 11 : brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
1750 11 : brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
1751 11 : brokendowntime.tm_hour = nHour;
1752 11 : brokendowntime.tm_min = nMinute;
1753 11 : brokendowntime.tm_sec = nSecond;
1754 11 : GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
1755 11 : double dfNumberOfDaysSince1900 = (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
1756 11 : if (eType != OFTTime)
1757 9 : dfNumberOfDaysSince1900 += NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
1758 11 : if (eType == OFTDate)
1759 2 : VSIFPrintfL(fp, "<v>%d</v>\n", (int)(dfNumberOfDaysSince1900 + 0.1));
1760 : else
1761 9 : VSIFPrintfL(fp, "<v>%.16f</v>\n", dfNumberOfDaysSince1900);
1762 11 : VSIFPrintfL(fp, "</c>\n");
1763 : }
1764 : else
1765 : {
1766 44 : const char* pszVal = poFeature->GetFieldAsString(j);
1767 44 : std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
1768 : int nStringIndex;
1769 44 : if (oIter != oStringMap.end())
1770 14 : nStringIndex = oIter->second;
1771 : else
1772 : {
1773 30 : nStringIndex = (int)oStringList.size();
1774 30 : oStringMap[pszVal] = nStringIndex;
1775 60 : oStringList.push_back(pszVal);
1776 : }
1777 44 : VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
1778 44 : VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
1779 44 : VSIFPrintfL(fp, "</c>\n");
1780 : }
1781 : }
1782 : }
1783 49 : VSIFPrintfL(fp, "</row>\n");
1784 :
1785 49 : iRow ++;
1786 49 : delete poFeature;
1787 49 : poFeature = poLayer->GetNextFeature();
1788 : }
1789 16 : VSIFPrintfL(fp, "</sheetData>\n");
1790 16 : VSIFPrintfL(fp, "</worksheet>\n");
1791 16 : VSIFCloseL(fp);
1792 16 : }
1793 :
1794 : /************************************************************************/
1795 : /* WriteSharedStrings() */
1796 : /************************************************************************/
1797 :
1798 2 : 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 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName), "wb");
1805 2 : VSIFPrintfL(fp, XML_HEADER);
1806 : VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n",
1807 : MAIN_NS,
1808 2 : (int)oStringList.size());
1809 56 : for(int i = 0; i < (int)oStringList.size(); i++)
1810 : {
1811 54 : VSIFPrintfL(fp, "<si>\n");
1812 54 : char* pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
1813 54 : VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
1814 54 : CPLFree(pszXML);
1815 54 : VSIFPrintfL(fp, "</si>\n");
1816 : }
1817 2 : VSIFPrintfL(fp, "</sst>\n");
1818 2 : VSIFCloseL(fp);
1819 2 : }
1820 :
1821 : /************************************************************************/
1822 : /* WriteStyles() */
1823 : /************************************************************************/
1824 :
1825 2 : static void WriteStyles(const char* pszName)
1826 : {
1827 : VSILFILE* fp;
1828 :
1829 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName), "wb");
1830 2 : VSIFPrintfL(fp, XML_HEADER);
1831 2 : VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
1832 2 : VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
1833 2 : VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
1834 2 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
1835 2 : VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
1836 2 : VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
1837 2 : VSIFPrintfL(fp, "</numFmts>\n");
1838 2 : VSIFPrintfL(fp, "<fonts count=\"1\">\n");
1839 2 : VSIFPrintfL(fp, "<font>\n");
1840 2 : VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
1841 2 : VSIFPrintfL(fp, "<family val=\"2\"/>\n");
1842 2 : VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
1843 2 : VSIFPrintfL(fp, "</font>\n");
1844 2 : VSIFPrintfL(fp, "</fonts>\n");
1845 2 : VSIFPrintfL(fp, "<fills count=\"1\">\n");
1846 2 : VSIFPrintfL(fp, "<fill>\n");
1847 2 : VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
1848 2 : VSIFPrintfL(fp, "</fill>\n");
1849 2 : VSIFPrintfL(fp, "</fills>\n");
1850 2 : VSIFPrintfL(fp, "<borders count=\"1\">\n");
1851 2 : VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
1852 2 : VSIFPrintfL(fp, "<left/>\n");
1853 2 : VSIFPrintfL(fp, "<right/>\n");
1854 2 : VSIFPrintfL(fp, "<top/>\n");
1855 2 : VSIFPrintfL(fp, "<bottom/>\n");
1856 2 : VSIFPrintfL(fp, "<diagonal/>\n");
1857 2 : VSIFPrintfL(fp, "</border>\n");
1858 2 : VSIFPrintfL(fp, "</borders>\n");
1859 2 : VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
1860 2 : VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
1861 2 : VSIFPrintfL(fp, "</xf>\n");
1862 2 : VSIFPrintfL(fp, "</cellStyleXfs>\n");
1863 2 : VSIFPrintfL(fp, "<cellXfs count=\"4\">\n");
1864 2 : VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
1865 2 : VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
1866 2 : VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
1867 2 : VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
1868 2 : VSIFPrintfL(fp, "</cellXfs>\n");
1869 2 : VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
1870 2 : VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" name=\"Normal\" xfId=\"0\"/>\n");
1871 2 : VSIFPrintfL(fp, "</cellStyles>\n");
1872 2 : VSIFPrintfL(fp, "</styleSheet>\n");
1873 2 : VSIFCloseL(fp);
1874 2 : }
1875 :
1876 : /************************************************************************/
1877 : /* WriteWorkbookRels() */
1878 : /************************************************************************/
1879 :
1880 2 : static void WriteWorkbookRels(const char* pszName, int nLayers)
1881 : {
1882 : VSILFILE* fp;
1883 :
1884 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName), "wb");
1885 2 : VSIFPrintfL(fp, XML_HEADER);
1886 2 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
1887 2 : VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/styles\" Target=\"styles.xml\"/>\n", SCHEMA_OD_RS);
1888 18 : 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 16 : 2 + i, SCHEMA_OD_RS, 1 + i);
1892 : }
1893 : VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" Target=\"sharedStrings.xml\"/>\n",
1894 2 : 2 + nLayers, SCHEMA_OD_RS);
1895 2 : VSIFPrintfL(fp, "</Relationships>\n");
1896 2 : VSIFCloseL(fp);
1897 2 : }
1898 :
1899 : /************************************************************************/
1900 : /* WriteDotRels() */
1901 : /************************************************************************/
1902 :
1903 2 : static void WriteDotRels(const char* pszName)
1904 : {
1905 : VSILFILE* fp;
1906 :
1907 2 : fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName), "wb");
1908 2 : VSIFPrintfL(fp, XML_HEADER);
1909 2 : VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
1910 2 : VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" Target=\"xl/workbook.xml\"/>\n", SCHEMA_OD_RS);
1911 2 : VSIFPrintfL(fp, "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" Target=\"docProps/core.xml\"/>\n", SCHEMA_PACKAGE_RS);
1912 2 : VSIFPrintfL(fp, "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" Target=\"docProps/app.xml\"/>\n", SCHEMA_OD_RS);
1913 2 : VSIFPrintfL(fp, "</Relationships>\n");
1914 2 : VSIFCloseL(fp);
1915 2 : }
1916 :
1917 : /************************************************************************/
1918 : /* SyncToDisk() */
1919 : /************************************************************************/
1920 :
1921 13 : OGRErr OGRXLSXDataSource::SyncToDisk()
1922 : {
1923 : int i;
1924 :
1925 13 : if (!bUpdated)
1926 11 : return OGRERR_NONE;
1927 :
1928 : VSIStatBufL sStat;
1929 2 : if (VSIStatL(pszName, &sStat) == 0)
1930 : {
1931 1 : 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 18 : for(int i = 0; i<nLayers; i++)
1941 : {
1942 16 : ((OGRXLSXLayer*)papoLayers[i])->GetLayerDefn();
1943 : }
1944 :
1945 : /* Maintain new ZIP files opened */
1946 2 : VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "wb");
1947 2 : if (fpZIP == NULL)
1948 : {
1949 : CPLError(CE_Failure, CPLE_FileIO,
1950 0 : "Cannot create %s", pszName);
1951 0 : return OGRERR_FAILURE;
1952 : }
1953 :
1954 2 : WriteContentTypes(pszName, nLayers);
1955 :
1956 : //VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
1957 2 : WriteApp(pszName);
1958 2 : WriteCore(pszName);
1959 :
1960 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
1961 2 : WriteWorkbook(pszName, this);
1962 :
1963 2 : std::map<std::string,int> oStringMap;
1964 2 : std::vector<std::string> oStringList;
1965 :
1966 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
1967 18 : for(i=0;i<nLayers;i++)
1968 : {
1969 16 : WriteLayer(pszName, GetLayer(i), i, oStringMap, oStringList);
1970 : }
1971 :
1972 2 : WriteSharedStrings(pszName, oStringMap, oStringList);
1973 2 : WriteStyles(pszName);
1974 :
1975 : //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
1976 2 : WriteWorkbookRels(pszName, nLayers);
1977 :
1978 : //VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
1979 2 : WriteDotRels(pszName);
1980 :
1981 : /* Now close ZIP file */
1982 2 : VSIFCloseL(fpZIP);
1983 :
1984 : /* Reset updated flag at datasource and layer level */
1985 2 : bUpdated = FALSE;
1986 18 : for(int i = 0; i<nLayers; i++)
1987 : {
1988 16 : ((OGRXLSXLayer*)papoLayers[i])->SetUpdated(FALSE);
1989 : }
1990 :
1991 2 : return OGRERR_NONE;
1992 : }
|