1 /******************************************************************************
2 *
3 * Project: XLSX Translator
4 * Purpose: Implements OGRXLSXDriver.
5 * Author: Even Rouault, even dot rouault at spatialys.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "ogr_xlsx.h"
30 #include "cpl_conv.h"
31
32 CPL_CVSID("$Id: ogrxlsxdriver.cpp 1761acd90777d5bcc49eddbc13c193098f0ed40b 2020-10-01 12:12:00 +0200 Even Rouault $")
33
34 extern "C" void RegisterOGRXLSX();
35
36 using namespace OGRXLSX;
37
38 // g++ -DHAVE_EXPAT -g -Wall -fPIC ogr/ogrsf_frmts/xlsx/*.cpp -shared -o ogr_XLSX.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/xlsx -L. -lgdal
39
40 static const char XLSX_MIMETYPE[] =
41 "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
42
43 /************************************************************************/
44 /* Identify() */
45 /************************************************************************/
46
OGRXLSXDriverIdentify(GDALOpenInfo * poOpenInfo)47 static int OGRXLSXDriverIdentify( GDALOpenInfo* poOpenInfo )
48 {
49 const char* pszExt = CPLGetExtension(poOpenInfo->pszFilename);
50 if (!EQUAL(pszExt, "XLSX") && !EQUAL(pszExt, "XLSM") &&
51 !EQUAL(pszExt, "XLSX}") && !EQUAL(pszExt, "XLSM}"))
52 return FALSE;
53
54 if( STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") ||
55 STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/") )
56 return poOpenInfo->eAccess == GA_ReadOnly;
57
58 return poOpenInfo->nHeaderBytes > 2 &&
59 memcmp(poOpenInfo->pabyHeader, "PK", 2) == 0;
60 }
61
62 /************************************************************************/
63 /* Open() */
64 /************************************************************************/
65
OGRXLSXDriverOpen(GDALOpenInfo * poOpenInfo)66 static GDALDataset* OGRXLSXDriverOpen( GDALOpenInfo* poOpenInfo )
67
68 {
69 if (!OGRXLSXDriverIdentify(poOpenInfo) )
70 return nullptr;
71
72 CPLString osPrefixedFilename("/vsizip/");
73 osPrefixedFilename += poOpenInfo->pszFilename;
74 if( STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") ||
75 STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/") )
76 {
77 osPrefixedFilename = poOpenInfo->pszFilename;
78 }
79
80 CPLString osTmpFilename;
81 osTmpFilename = CPLSPrintf("%s/[Content_Types].xml", osPrefixedFilename.c_str());
82 VSILFILE* fpContent = VSIFOpenL(osTmpFilename, "rb");
83 if (fpContent == nullptr)
84 return nullptr;
85
86 char szBuffer[2048];
87 int nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fpContent);
88 szBuffer[nRead] = 0;
89
90 VSIFCloseL(fpContent);
91
92 if (strstr(szBuffer, XLSX_MIMETYPE) == nullptr)
93 return nullptr;
94
95 osTmpFilename = CPLSPrintf("%s/xl/workbook.xml", osPrefixedFilename.c_str());
96 VSILFILE* fpWorkbook = VSIFOpenL(osTmpFilename, "rb");
97 if (fpWorkbook == nullptr)
98 return nullptr;
99
100 osTmpFilename = CPLSPrintf("%s/xl/_rels/workbook.xml.rels", osPrefixedFilename.c_str());
101 VSILFILE* fpWorkbookRels = VSIFOpenL(osTmpFilename, "rb");
102 if (fpWorkbookRels == nullptr)
103 {
104 VSIFCloseL(fpWorkbook);
105 return nullptr;
106 }
107
108 osTmpFilename = CPLSPrintf("%s/xl/sharedStrings.xml", osPrefixedFilename.c_str());
109 VSILFILE* fpSharedStrings = VSIFOpenL(osTmpFilename, "rb");
110 osTmpFilename = CPLSPrintf("%s/xl/styles.xml", osPrefixedFilename.c_str());
111 VSILFILE* fpStyles = VSIFOpenL(osTmpFilename, "rb");
112
113 OGRXLSXDataSource *poDS = new OGRXLSXDataSource();
114
115 if( !poDS->Open( poOpenInfo->pszFilename, osPrefixedFilename,
116 fpWorkbook, fpWorkbookRels, fpSharedStrings, fpStyles,
117 poOpenInfo->eAccess == GA_Update ) )
118 {
119 delete poDS;
120 poDS = nullptr;
121 }
122
123 return poDS;
124 }
125
126 /************************************************************************/
127 /* OGRXLSXDriverCreate() */
128 /************************************************************************/
129
130 static
OGRXLSXDriverCreate(const char * pszName,int,int,int,GDALDataType,char ** papszOptions)131 GDALDataset *OGRXLSXDriverCreate( const char *pszName,
132 int /* nXSize */,
133 int /* nYSize */,
134 int /* nBands */,
135 GDALDataType /* eDT */,
136 char **papszOptions )
137
138 {
139 if (!EQUAL(CPLGetExtension(pszName), "XLSX"))
140 {
141 CPLError( CE_Failure, CPLE_AppDefined, "File extension should be XLSX" );
142 return nullptr;
143 }
144
145 /* -------------------------------------------------------------------- */
146 /* First, ensure there isn't any such file yet. */
147 /* -------------------------------------------------------------------- */
148 VSIStatBufL sStatBuf;
149
150 if( VSIStatL( pszName, &sStatBuf ) == 0 )
151 {
152 CPLError( CE_Failure, CPLE_AppDefined,
153 "It seems a file system object called '%s' already exists.",
154 pszName );
155
156 return nullptr;
157 }
158
159 /* -------------------------------------------------------------------- */
160 /* Try to create datasource. */
161 /* -------------------------------------------------------------------- */
162 OGRXLSXDataSource *poDS = new OGRXLSXDataSource();
163
164 if( !poDS->Create( pszName, papszOptions ) )
165 {
166 delete poDS;
167 return nullptr;
168 }
169 else
170 return poDS;
171 }
172
173 /************************************************************************/
174 /* RegisterOGRXLSX() */
175 /************************************************************************/
176
RegisterOGRXLSX()177 void RegisterOGRXLSX()
178
179 {
180 if( GDALGetDriverByName( "XLSX" ) != nullptr )
181 return;
182
183 GDALDriver *poDriver = new GDALDriver();
184
185 poDriver->SetDescription( "XLSX" );
186 poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
187 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
188 "MS Office Open XML spreadsheet" );
189 poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "xlsx xlsm" );
190 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/vector/xlsx.html" );
191 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
192 poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES,
193 "Integer Integer64 Real String Date DateTime "
194 "Time" );
195 poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean" );
196 poDriver->SetMetadataItem( GDAL_DCAP_NONSPATIAL, "YES" );
197
198 poDriver->pfnIdentify = OGRXLSXDriverIdentify;
199 poDriver->pfnOpen = OGRXLSXDriverOpen;
200 poDriver->pfnCreate = OGRXLSXDriverCreate;
201
202 GetGDALDriverManager()->RegisterDriver( poDriver );
203 }
204