Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SVG Translator
4 : * Purpose: Implements OGRSVGDataSource class
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_svg.h"
14 : #include "cpl_conv.h"
15 :
16 : /************************************************************************/
17 : /* OGRSVGDataSource() */
18 : /************************************************************************/
19 :
20 1 : OGRSVGDataSource::OGRSVGDataSource()
21 : : papoLayers(nullptr), nLayers(0)
22 : #ifdef HAVE_EXPAT
23 : ,
24 : eValidity(SVG_VALIDITY_UNKNOWN), bIsCloudmade(false),
25 1 : oCurrentParser(nullptr), nDataHandlerCounter(0)
26 : #endif
27 : {
28 1 : }
29 :
30 : /************************************************************************/
31 : /* ~OGRSVGDataSource() */
32 : /************************************************************************/
33 :
34 2 : OGRSVGDataSource::~OGRSVGDataSource()
35 :
36 : {
37 4 : for (int i = 0; i < nLayers; i++)
38 3 : delete papoLayers[i];
39 1 : CPLFree(papoLayers);
40 2 : }
41 :
42 : /************************************************************************/
43 : /* GetLayer() */
44 : /************************************************************************/
45 :
46 12 : OGRLayer *OGRSVGDataSource::GetLayer(int iLayer)
47 :
48 : {
49 12 : if (iLayer < 0 || iLayer >= nLayers)
50 0 : return nullptr;
51 : else
52 12 : return papoLayers[iLayer];
53 : }
54 :
55 : #ifdef HAVE_EXPAT
56 :
57 : /************************************************************************/
58 : /* startElementValidateCbk() */
59 : /************************************************************************/
60 :
61 4 : void OGRSVGDataSource::startElementValidateCbk(const char *pszNameIn,
62 : const char **ppszAttr)
63 : {
64 4 : if (eValidity == SVG_VALIDITY_UNKNOWN)
65 : {
66 1 : if (strcmp(pszNameIn, "svg") == 0)
67 : {
68 1 : eValidity = SVG_VALIDITY_VALID;
69 2 : for (int i = 0; ppszAttr[i] != nullptr; i += 2)
70 : {
71 2 : if (strcmp(ppszAttr[i], "xmlns:cm") == 0 &&
72 1 : strcmp(ppszAttr[i + 1], "http://cloudmade.com/") == 0)
73 : {
74 1 : bIsCloudmade = true;
75 1 : break;
76 : }
77 : }
78 : }
79 : else
80 : {
81 0 : eValidity = SVG_VALIDITY_INVALID;
82 : }
83 : }
84 4 : }
85 :
86 : /************************************************************************/
87 : /* dataHandlerValidateCbk() */
88 : /************************************************************************/
89 :
90 8 : void OGRSVGDataSource::dataHandlerValidateCbk(CPL_UNUSED const char *data,
91 : CPL_UNUSED int nLen)
92 : {
93 8 : nDataHandlerCounter++;
94 8 : if (nDataHandlerCounter >= PARSER_BUF_SIZE)
95 : {
96 0 : CPLError(CE_Failure, CPLE_AppDefined,
97 : "File probably corrupted (million laugh pattern)");
98 0 : XML_StopParser(oCurrentParser, XML_FALSE);
99 : }
100 8 : }
101 :
102 4 : static void XMLCALL startElementValidateCbk(void *pUserData,
103 : const char *pszName,
104 : const char **ppszAttr)
105 : {
106 4 : OGRSVGDataSource *poDS = (OGRSVGDataSource *)pUserData;
107 4 : poDS->startElementValidateCbk(pszName, ppszAttr);
108 4 : }
109 :
110 8 : static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data,
111 : int nLen)
112 : {
113 8 : OGRSVGDataSource *poDS = (OGRSVGDataSource *)pUserData;
114 8 : poDS->dataHandlerValidateCbk(data, nLen);
115 8 : }
116 : #endif
117 :
118 : /************************************************************************/
119 : /* Open() */
120 : /************************************************************************/
121 :
122 1 : int OGRSVGDataSource::Open(const char *pszFilename)
123 :
124 : {
125 : #ifdef HAVE_EXPAT
126 : /* -------------------------------------------------------------------- */
127 : /* Try to open the file. */
128 : /* -------------------------------------------------------------------- */
129 2 : CPLString osFilename; // keep in that scope
130 1 : if (EQUAL(CPLGetExtension(pszFilename), "svgz") &&
131 0 : strstr(pszFilename, "/vsigzip/") == nullptr)
132 : {
133 0 : osFilename = CPLString("/vsigzip/") + pszFilename;
134 0 : pszFilename = osFilename.c_str();
135 : }
136 :
137 1 : VSILFILE *fp = VSIFOpenL(pszFilename, "r");
138 1 : if (fp == nullptr)
139 0 : return FALSE;
140 :
141 1 : eValidity = SVG_VALIDITY_UNKNOWN;
142 :
143 1 : XML_Parser oParser = OGRCreateExpatXMLParser();
144 1 : oCurrentParser = oParser;
145 1 : XML_SetUserData(oParser, this);
146 1 : XML_SetElementHandler(oParser, ::startElementValidateCbk, nullptr);
147 1 : XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk);
148 :
149 1 : std::vector<char> aBuf(PARSER_BUF_SIZE);
150 1 : int nDone = 0;
151 1 : unsigned int nLen = 0;
152 1 : int nCount = 0;
153 :
154 : /* Begin to parse the file and look for the <svg> element */
155 : /* It *MUST* be the first element of an XML file */
156 : /* So once we have read the first element, we know if we can */
157 : /* handle the file or not with that driver */
158 0 : do
159 : {
160 1 : nDataHandlerCounter = 0;
161 1 : nLen = (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp);
162 1 : nDone = nLen < aBuf.size();
163 1 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
164 : {
165 0 : if (nLen <= PARSER_BUF_SIZE - 1)
166 0 : aBuf[nLen] = 0;
167 : else
168 0 : aBuf[PARSER_BUF_SIZE - 1] = 0;
169 0 : if (strstr(aBuf.data(), "<?xml") && strstr(aBuf.data(), "<svg"))
170 : {
171 0 : CPLError(
172 : CE_Failure, CPLE_AppDefined,
173 : "XML parsing of SVG file failed : %s at line %d, column %d",
174 : XML_ErrorString(XML_GetErrorCode(oParser)),
175 0 : (int)XML_GetCurrentLineNumber(oParser),
176 0 : (int)XML_GetCurrentColumnNumber(oParser));
177 : }
178 0 : eValidity = SVG_VALIDITY_INVALID;
179 0 : break;
180 : }
181 1 : if (eValidity == SVG_VALIDITY_INVALID)
182 : {
183 0 : break;
184 : }
185 1 : else if (eValidity == SVG_VALIDITY_VALID)
186 : {
187 1 : break;
188 : }
189 : else
190 : {
191 : /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the
192 : * file */
193 : /* is SVG or not, we give up and fail silently */
194 0 : nCount++;
195 0 : if (nCount == 50)
196 0 : break;
197 : }
198 0 : } while (!nDone && nLen > 0);
199 :
200 1 : XML_ParserFree(oParser);
201 :
202 1 : VSIFCloseL(fp);
203 :
204 1 : if (eValidity == SVG_VALIDITY_VALID)
205 : {
206 1 : if (bIsCloudmade)
207 : {
208 1 : nLayers = 3;
209 2 : papoLayers = (OGRSVGLayer **)CPLRealloc(
210 1 : papoLayers, nLayers * sizeof(OGRSVGLayer *));
211 1 : papoLayers[0] =
212 1 : new OGRSVGLayer(pszFilename, "points", SVG_POINTS, this);
213 1 : papoLayers[1] =
214 1 : new OGRSVGLayer(pszFilename, "lines", SVG_LINES, this);
215 1 : papoLayers[2] =
216 1 : new OGRSVGLayer(pszFilename, "polygons", SVG_POLYGONS, this);
217 : }
218 : else
219 : {
220 0 : CPLDebug(
221 : "SVG",
222 : "%s seems to be a SVG file, but not a Cloudmade vector one.",
223 : pszFilename);
224 : }
225 : }
226 :
227 1 : return nLayers > 0;
228 : #else
229 : char aBuf[256];
230 : VSILFILE *fp = VSIFOpenL(pszFilename, "r");
231 : if (fp)
232 : {
233 : unsigned int nLen = (unsigned int)VSIFReadL(aBuf, 1, 255, fp);
234 : aBuf[nLen] = 0;
235 : if (strstr(aBuf, "<?xml") && strstr(aBuf, "<svg") &&
236 : strstr(aBuf, "http://cloudmade.com/"))
237 : {
238 : CPLError(CE_Failure, CPLE_NotSupported,
239 : "OGR/SVG driver has not been built with read support. "
240 : "Expat library required");
241 : }
242 : VSIFCloseL(fp);
243 : }
244 : return FALSE;
245 : #endif
246 : }
|