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