/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxfreader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: DXF Translator |
4 | | * Purpose: Implements low level DXF reading with caching and parsing of |
5 | | * of the code/value pairs. |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "ogr_dxf.h" |
15 | | #include "cpl_conv.h" |
16 | | #include "cpl_string.h" |
17 | | #include "cpl_csv.h" |
18 | | |
19 | | #include <cinttypes> |
20 | | |
21 | | /************************************************************************/ |
22 | | /* ~OGRDXFReaderBase() */ |
23 | | /************************************************************************/ |
24 | | |
25 | 27.7k | OGRDXFReaderBase::~OGRDXFReaderBase() = default; |
26 | | |
27 | | /************************************************************************/ |
28 | | /* Initialize() */ |
29 | | /************************************************************************/ |
30 | | |
31 | | void OGRDXFReaderBase::Initialize(VSILFILE *fpIn) |
32 | | |
33 | 27.6k | { |
34 | 27.6k | fp = fpIn; |
35 | 27.6k | } |
36 | | |
37 | | /************************************************************************/ |
38 | | /* ResetReadPointer() */ |
39 | | /************************************************************************/ |
40 | | |
41 | | void OGRDXFReaderASCII::ResetReadPointer(uint64_t iNewOffset, |
42 | | int nNewLineNumber /* = 0 */) |
43 | | |
44 | 20.0k | { |
45 | 20.0k | nSrcBufferBytes = 0; |
46 | 20.0k | iSrcBufferOffset = 0; |
47 | 20.0k | iSrcBufferFileOffset = iNewOffset; |
48 | 20.0k | nLastValueSize = 0; |
49 | 20.0k | nLineNumber = nNewLineNumber; |
50 | | |
51 | 20.0k | VSIFSeekL(fp, iNewOffset, SEEK_SET); |
52 | 20.0k | } |
53 | | |
54 | | /************************************************************************/ |
55 | | /* LoadDiskChunk() */ |
56 | | /* */ |
57 | | /* Load another block (512 bytes) of input from the source */ |
58 | | /* file. */ |
59 | | /************************************************************************/ |
60 | | |
61 | | void OGRDXFReaderASCII::LoadDiskChunk() |
62 | | |
63 | 1.99M | { |
64 | 1.99M | if (nSrcBufferBytes - iSrcBufferOffset > 511) |
65 | 800 | return; |
66 | | |
67 | 1.99M | if (iSrcBufferOffset > 0) |
68 | 1.87M | { |
69 | 1.87M | CPLAssert(nSrcBufferBytes <= 1024); |
70 | 1.87M | CPLAssert(iSrcBufferOffset <= nSrcBufferBytes); |
71 | | |
72 | 1.87M | memmove(achSrcBuffer.data(), achSrcBuffer.data() + iSrcBufferOffset, |
73 | 1.87M | nSrcBufferBytes - iSrcBufferOffset); |
74 | 1.87M | iSrcBufferFileOffset += iSrcBufferOffset; |
75 | 1.87M | nSrcBufferBytes -= iSrcBufferOffset; |
76 | 1.87M | iSrcBufferOffset = 0; |
77 | 1.87M | } |
78 | | |
79 | 1.99M | nSrcBufferBytes += static_cast<int>( |
80 | 1.99M | VSIFReadL(achSrcBuffer.data() + nSrcBufferBytes, 1, 512, fp)); |
81 | 1.99M | achSrcBuffer[nSrcBufferBytes] = '\0'; |
82 | | |
83 | 1.99M | CPLAssert(nSrcBufferBytes <= 1024); |
84 | 1.99M | CPLAssert(iSrcBufferOffset <= nSrcBufferBytes); |
85 | 1.99M | } |
86 | | |
87 | | /************************************************************************/ |
88 | | /* ReadValue() */ |
89 | | /* */ |
90 | | /* Read one type code and value line pair from the DXF file. */ |
91 | | /************************************************************************/ |
92 | | |
93 | | int OGRDXFReaderASCII::ReadValueRaw(char *pszValueBuf, int nValueBufSize) |
94 | | |
95 | 42.4M | { |
96 | | /* -------------------------------------------------------------------- */ |
97 | | /* Make sure we have lots of data in our buffer for one value. */ |
98 | | /* -------------------------------------------------------------------- */ |
99 | 42.4M | if (nSrcBufferBytes - iSrcBufferOffset < 512) |
100 | 1.96M | LoadDiskChunk(); |
101 | | |
102 | | /* -------------------------------------------------------------------- */ |
103 | | /* Capture the value code, and skip past it. */ |
104 | | /* -------------------------------------------------------------------- */ |
105 | 42.4M | unsigned int iStartSrcBufferOffset = iSrcBufferOffset; |
106 | 42.4M | int nValueCode = atoi(achSrcBuffer.data() + iSrcBufferOffset); |
107 | | |
108 | 42.4M | nLineNumber++; |
109 | | |
110 | | // proceed to newline. |
111 | 256M | while (achSrcBuffer[iSrcBufferOffset] != '\n' && |
112 | 256M | achSrcBuffer[iSrcBufferOffset] != '\r' && |
113 | 256M | achSrcBuffer[iSrcBufferOffset] != '\0') |
114 | 213M | iSrcBufferOffset++; |
115 | | |
116 | 42.4M | if (achSrcBuffer[iSrcBufferOffset] == '\0') |
117 | 23.7k | return -1; |
118 | | |
119 | | // skip past newline. CR, CRLF, or LFCR |
120 | 42.4M | if ((achSrcBuffer[iSrcBufferOffset] == '\r' && |
121 | 42.4M | achSrcBuffer[iSrcBufferOffset + 1] == '\n') || |
122 | 42.4M | (achSrcBuffer[iSrcBufferOffset] == '\n' && |
123 | 37.4M | achSrcBuffer[iSrcBufferOffset + 1] == '\r')) |
124 | 7.96M | iSrcBufferOffset += 2; |
125 | 34.4M | else |
126 | 34.4M | iSrcBufferOffset += 1; |
127 | | |
128 | 42.4M | if (achSrcBuffer[iSrcBufferOffset] == '\0') |
129 | 2.58k | return -1; |
130 | | |
131 | | /* -------------------------------------------------------------------- */ |
132 | | /* Capture the value string. */ |
133 | | /* -------------------------------------------------------------------- */ |
134 | 42.4M | unsigned int iEOL = iSrcBufferOffset; |
135 | 42.4M | CPLString osValue; |
136 | | |
137 | 42.4M | nLineNumber++; |
138 | | |
139 | | // proceed to newline. |
140 | 297M | while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' && |
141 | 297M | achSrcBuffer[iEOL] != '\0') |
142 | 254M | iEOL++; |
143 | | |
144 | 42.4M | bool bLongLine = false; |
145 | 42.4M | while (achSrcBuffer[iEOL] == '\0' || |
146 | 42.4M | (achSrcBuffer[iEOL] == '\r' && achSrcBuffer[iEOL + 1] == '\0')) |
147 | 26.1k | { |
148 | | // The line is longer than the buffer (or the line ending is split at |
149 | | // end of buffer). Let's copy what we have so far into our string, and |
150 | | // read more |
151 | 26.1k | const auto nValueLength = osValue.length(); |
152 | | |
153 | 26.1k | if (nValueLength + iEOL - iSrcBufferOffset > 1048576) |
154 | 0 | { |
155 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Line %d is too long", |
156 | 0 | nLineNumber); |
157 | 0 | return -1; |
158 | 0 | } |
159 | | |
160 | 26.1k | osValue.resize(nValueLength + iEOL - iSrcBufferOffset, '\0'); |
161 | 26.1k | std::copy(achSrcBuffer.data() + iSrcBufferOffset, |
162 | 26.1k | achSrcBuffer.data() + iEOL, osValue.begin() + nValueLength); |
163 | | |
164 | 26.1k | iSrcBufferOffset = iEOL; |
165 | 26.1k | LoadDiskChunk(); |
166 | 26.1k | iEOL = iSrcBufferOffset; |
167 | 26.1k | bLongLine = true; |
168 | | |
169 | | // Have we prematurely reached the end of the file? |
170 | 26.1k | if (achSrcBuffer[iEOL] == '\0') |
171 | 9.36k | return -1; |
172 | | |
173 | | // Proceed to newline again |
174 | 5.13M | while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' && |
175 | 5.13M | achSrcBuffer[iEOL] != '\0') |
176 | 5.11M | iEOL++; |
177 | 16.7k | } |
178 | | |
179 | 42.4M | size_t nValueBufLen = 0; |
180 | | |
181 | | // If this was an extremely long line, copy from osValue into the buffer |
182 | 42.4M | if (!osValue.empty()) |
183 | 10.6k | { |
184 | 10.6k | strncpy(pszValueBuf, osValue.c_str(), nValueBufSize - 1); |
185 | 10.6k | pszValueBuf[nValueBufSize - 1] = '\0'; |
186 | | |
187 | 10.6k | nValueBufLen = strlen(pszValueBuf); |
188 | | |
189 | 10.6k | if (static_cast<int>(osValue.length()) > nValueBufSize - 1) |
190 | 5.05k | { |
191 | 5.05k | CPLDebug("DXF", "Long line truncated to %d characters.\n%s...", |
192 | 5.05k | nValueBufSize - 1, pszValueBuf); |
193 | 5.05k | } |
194 | 10.6k | } |
195 | | |
196 | | // Copy the last (normally, the only) section of this line into the buffer |
197 | 42.4M | if (static_cast<int>(iEOL - iSrcBufferOffset) > |
198 | 42.4M | nValueBufSize - static_cast<int>(nValueBufLen) - 1) |
199 | 24.0k | { |
200 | 24.0k | strncpy(pszValueBuf + nValueBufLen, |
201 | 24.0k | achSrcBuffer.data() + iSrcBufferOffset, |
202 | 24.0k | nValueBufSize - static_cast<int>(nValueBufLen) - 1); |
203 | 24.0k | pszValueBuf[nValueBufSize - 1] = '\0'; |
204 | | |
205 | 24.0k | CPLDebug("DXF", "Long line truncated to %d characters.\n%s...", |
206 | 24.0k | nValueBufSize - 1, pszValueBuf); |
207 | 24.0k | } |
208 | 42.3M | else |
209 | 42.3M | { |
210 | 42.3M | strncpy(pszValueBuf + nValueBufLen, |
211 | 42.3M | achSrcBuffer.data() + iSrcBufferOffset, |
212 | 42.3M | iEOL - iSrcBufferOffset); |
213 | 42.3M | pszValueBuf[nValueBufLen + iEOL - iSrcBufferOffset] = '\0'; |
214 | 42.3M | } |
215 | | |
216 | 42.4M | iSrcBufferOffset = iEOL; |
217 | | |
218 | | // skip past newline. CR, CRLF, or LFCR |
219 | 42.4M | if ((achSrcBuffer[iSrcBufferOffset] == '\r' && |
220 | 42.4M | achSrcBuffer[iSrcBufferOffset + 1] == '\n') || |
221 | 42.4M | (achSrcBuffer[iSrcBufferOffset] == '\n' && |
222 | 37.4M | achSrcBuffer[iSrcBufferOffset + 1] == '\r')) |
223 | 8.00M | iSrcBufferOffset += 2; |
224 | 34.4M | else |
225 | 34.4M | iSrcBufferOffset += 1; |
226 | | |
227 | | /* -------------------------------------------------------------------- */ |
228 | | /* Record how big this value was, so it can be unread safely. */ |
229 | | /* -------------------------------------------------------------------- */ |
230 | 42.4M | if (bLongLine) |
231 | 10.6k | nLastValueSize = 0; |
232 | 42.4M | else |
233 | 42.4M | { |
234 | 42.4M | nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset; |
235 | 42.4M | CPLAssert(nLastValueSize > 0); |
236 | 42.4M | } |
237 | | |
238 | 42.4M | return nValueCode; |
239 | 42.4M | } |
240 | | |
241 | | int OGRDXFReaderASCII::ReadValue(char *pszValueBuf, int nValueBufSize) |
242 | 42.4M | { |
243 | 42.4M | int nValueCode; |
244 | 42.4M | while (true) |
245 | 42.4M | { |
246 | 42.4M | nValueCode = ReadValueRaw(pszValueBuf, nValueBufSize); |
247 | 42.4M | if (nValueCode == 999) |
248 | 18.1k | { |
249 | | // Skip comments |
250 | 18.1k | continue; |
251 | 18.1k | } |
252 | 42.4M | break; |
253 | 42.4M | } |
254 | 42.4M | return nValueCode; |
255 | 42.4M | } |
256 | | |
257 | | /************************************************************************/ |
258 | | /* UnreadValue() */ |
259 | | /* */ |
260 | | /* Unread the last value read, accomplished by resetting the */ |
261 | | /* read pointer. */ |
262 | | /************************************************************************/ |
263 | | |
264 | | void OGRDXFReaderASCII::UnreadValue() |
265 | | |
266 | 1.22M | { |
267 | 1.22M | if (nLastValueSize == 0) |
268 | 495 | { |
269 | 495 | CPLError(CE_Failure, CPLE_AppDefined, |
270 | 495 | "Cannot UnreadValue(), likely due to a previous long line"); |
271 | 495 | return; |
272 | 495 | } |
273 | 1.22M | CPLAssert(iSrcBufferOffset >= nLastValueSize); |
274 | 1.22M | CPLAssert(nLastValueSize > 0); |
275 | | |
276 | 1.22M | iSrcBufferOffset -= nLastValueSize; |
277 | 1.22M | nLineNumber -= 2; |
278 | 1.22M | nLastValueSize = 0; |
279 | 1.22M | } |
280 | | |
281 | | int OGRDXFReaderBinary::ReadValue(char *pszValueBuffer, int nValueBufferSize) |
282 | 68 | { |
283 | 68 | if (VSIFTellL(fp) == 0) |
284 | 0 | { |
285 | 0 | VSIFSeekL(fp, AUTOCAD_BINARY_DXF_SIGNATURE.size(), SEEK_SET); |
286 | 0 | } |
287 | 68 | if (VSIFTellL(fp) == AUTOCAD_BINARY_DXF_SIGNATURE.size()) |
288 | 68 | { |
289 | | // Detect if the file is AutoCAD Binary r12 |
290 | 68 | GByte abyZeroSection[8] = {0}; |
291 | 68 | if (VSIFReadL(abyZeroSection, 1, sizeof(abyZeroSection), fp) != |
292 | 68 | sizeof(abyZeroSection)) |
293 | 1 | { |
294 | 1 | CPLError(CE_Failure, CPLE_FileIO, "File too short"); |
295 | 1 | return -1; |
296 | 1 | } |
297 | 67 | m_bIsR12 = memcmp(abyZeroSection, "\x00SECTION", 8) == 0; |
298 | 67 | VSIFSeekL(fp, AUTOCAD_BINARY_DXF_SIGNATURE.size(), SEEK_SET); |
299 | 67 | } |
300 | | |
301 | 67 | m_nPrevPos = VSIFTellL(fp); |
302 | | |
303 | 67 | uint16_t nCode = 0; |
304 | 67 | bool bReadCodeUINT16 = true; |
305 | 67 | if (m_bIsR12) |
306 | 0 | { |
307 | 0 | GByte nCodeByte = 0; |
308 | 0 | if (VSIFReadL(&nCodeByte, 1, 1, fp) != 1) |
309 | 0 | { |
310 | 0 | CPLError(CE_Failure, CPLE_FileIO, "File too short"); |
311 | 0 | return -1; |
312 | 0 | } |
313 | 0 | bReadCodeUINT16 = (nCodeByte == 255); |
314 | 0 | if (!bReadCodeUINT16) |
315 | 0 | nCode = nCodeByte; |
316 | 0 | } |
317 | 67 | if (bReadCodeUINT16) |
318 | 67 | { |
319 | 67 | if (VSIFReadL(&nCode, 1, sizeof(uint16_t), fp) != sizeof(uint16_t)) |
320 | 0 | { |
321 | 0 | CPLError(CE_Failure, CPLE_FileIO, "File too short"); |
322 | 0 | return -1; |
323 | 0 | } |
324 | 67 | CPL_LSBPTR16(&nCode); |
325 | 67 | } |
326 | | |
327 | | // Credits to ezdxf for the ranges |
328 | 67 | bool bRet = true; |
329 | 67 | if (nCode >= 290 && nCode < 300) |
330 | 1 | { |
331 | 1 | GByte nVal = 0; |
332 | 1 | bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == 1; |
333 | 1 | CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal); |
334 | | // CPLDebug("DXF", "Read %d: %d", nCode, nVal); |
335 | 1 | } |
336 | 66 | else if ((nCode >= 60 && nCode < 80) || (nCode >= 170 && nCode < 180) || |
337 | 66 | (nCode >= 270 && nCode < 290) || (nCode >= 370 && nCode < 390) || |
338 | 66 | (nCode >= 400 && nCode < 410) || (nCode >= 1060 && nCode < 1071)) |
339 | 1 | { |
340 | 1 | int16_t nVal = 0; |
341 | 1 | bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal); |
342 | 1 | CPL_LSBPTR16(&nVal); |
343 | 1 | CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal); |
344 | | // CPLDebug("DXF", "Read %d: %d", nCode, nVal); |
345 | 1 | } |
346 | 65 | else if ((nCode >= 90 && nCode < 100) || (nCode >= 420 && nCode < 430) || |
347 | 65 | (nCode >= 440 && nCode < 460) || (nCode == 1071)) |
348 | 2 | { |
349 | 2 | int32_t nVal = 0; |
350 | 2 | bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal); |
351 | 2 | CPL_LSBPTR32(&nVal); |
352 | 2 | CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal); |
353 | | // CPLDebug("DXF", "Read %d: %d", nCode, nVal); |
354 | 2 | } |
355 | 63 | else if (nCode >= 160 && nCode < 170) |
356 | 0 | { |
357 | 0 | int64_t nVal = 0; |
358 | 0 | bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal); |
359 | 0 | CPL_LSBPTR64(&nVal); |
360 | 0 | CPLsnprintf(pszValueBuffer, nValueBufferSize, "%" PRId64, nVal); |
361 | | // CPLDebug("DXF", "Read %d: %" PRId64, nCode, nVal); |
362 | 0 | } |
363 | 63 | else if ((nCode >= 10 && nCode < 60) || (nCode >= 110 && nCode < 150) || |
364 | 63 | (nCode >= 210 && nCode < 240) || (nCode >= 460 && nCode < 470) || |
365 | 63 | (nCode >= 1010 && nCode < 1060)) |
366 | 4 | { |
367 | 4 | double dfVal = 0; |
368 | 4 | bRet = VSIFReadL(&dfVal, 1, sizeof(dfVal), fp) == sizeof(dfVal); |
369 | 4 | CPL_LSBPTR64(&dfVal); |
370 | 4 | CPLsnprintf(pszValueBuffer, nValueBufferSize, "%.17g", dfVal); |
371 | | // CPLDebug("DXF", "Read %d: %g", nCode, dfVal); |
372 | 4 | } |
373 | 59 | else if ((nCode >= 310 && nCode < 320) || nCode == 1004) |
374 | 7 | { |
375 | | // Binary |
376 | 7 | GByte nChunkLength = 0; |
377 | 7 | bRet = VSIFReadL(&nChunkLength, 1, sizeof(nChunkLength), fp) == |
378 | 7 | sizeof(nChunkLength); |
379 | 7 | std::vector<GByte> abyData(nChunkLength); |
380 | 7 | bRet &= VSIFReadL(abyData.data(), 1, nChunkLength, fp) == nChunkLength; |
381 | 7 | if (2 * nChunkLength + 1 > nValueBufferSize) |
382 | 1 | { |
383 | 1 | CPLError(CE_Failure, CPLE_AppDefined, |
384 | 1 | "Provided buffer too small to store string"); |
385 | 1 | return -1; |
386 | 1 | } |
387 | 87 | for (int i = 0; i < nChunkLength; ++i) |
388 | 81 | { |
389 | 81 | snprintf(pszValueBuffer + 2 * i, nValueBufferSize - 2 * i, "%02X", |
390 | 81 | abyData[i]); |
391 | 81 | } |
392 | 6 | pszValueBuffer[2 * nChunkLength] = 0; |
393 | | // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer); |
394 | 6 | } |
395 | 52 | else |
396 | 52 | { |
397 | | // Zero terminated string |
398 | 52 | bool bEOS = false; |
399 | 2.77k | for (int i = 0; bRet && i < nValueBufferSize; ++i) |
400 | 2.75k | { |
401 | 2.75k | char ch = 0; |
402 | 2.75k | bRet = VSIFReadL(&ch, 1, 1, fp) == 1; |
403 | 2.75k | pszValueBuffer[i] = ch; |
404 | 2.75k | if (ch == 0) |
405 | 27 | { |
406 | | // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer); |
407 | 27 | bEOS = true; |
408 | 27 | break; |
409 | 27 | } |
410 | 2.75k | } |
411 | 52 | if (!bEOS) |
412 | 25 | { |
413 | 25 | CPLError(CE_Failure, CPLE_AppDefined, |
414 | 25 | "Provided buffer too small to store string"); |
415 | 4.22k | while (bRet) |
416 | 4.22k | { |
417 | 4.22k | char ch = 0; |
418 | 4.22k | bRet = VSIFReadL(&ch, 1, 1, fp) == 1; |
419 | 4.22k | if (ch == 0) |
420 | 25 | { |
421 | 25 | break; |
422 | 25 | } |
423 | 4.22k | } |
424 | 25 | return -1; |
425 | 25 | } |
426 | 52 | } |
427 | | |
428 | 41 | if (!bRet) |
429 | 11 | { |
430 | 11 | CPLError(CE_Failure, CPLE_FileIO, "File too short"); |
431 | 11 | return -1; |
432 | 11 | } |
433 | 30 | return nCode; |
434 | 41 | } |
435 | | |
436 | | void OGRDXFReaderBinary::UnreadValue() |
437 | 0 | { |
438 | 0 | if (m_nPrevPos == static_cast<uint64_t>(-1)) |
439 | 0 | { |
440 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
441 | 0 | "UnreadValue() can be called just once after ReadValue()"); |
442 | 0 | } |
443 | 0 | else |
444 | 0 | { |
445 | 0 | VSIFSeekL(fp, m_nPrevPos, SEEK_SET); |
446 | 0 | m_nPrevPos = static_cast<uint64_t>(-1); |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | | void OGRDXFReaderBinary::ResetReadPointer(uint64_t nPos, int nNewLineNumber) |
451 | 0 | { |
452 | 0 | VSIFSeekL(fp, nPos, SEEK_SET); |
453 | 0 | nLineNumber = nNewLineNumber; |
454 | 0 | } |