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 421 : void OGRDXFReaderBase::Initialize(VSILFILE *fpIn)
32 :
33 : {
34 421 : fp = fpIn;
35 421 : }
36 :
37 : /************************************************************************/
38 : /* ResetReadPointer() */
39 : /************************************************************************/
40 :
41 276 : void OGRDXFReaderASCII::ResetReadPointer(uint64_t iNewOffset,
42 : int nNewLineNumber /* = 0 */)
43 :
44 : {
45 276 : nSrcBufferBytes = 0;
46 276 : iSrcBufferOffset = 0;
47 276 : iSrcBufferFileOffset = iNewOffset;
48 276 : nLastValueSize = 0;
49 276 : nLineNumber = nNewLineNumber;
50 :
51 276 : VSIFSeekL(fp, iNewOffset, SEEK_SET);
52 276 : }
53 :
54 : /************************************************************************/
55 : /* LoadDiskChunk() */
56 : /* */
57 : /* Load another block (512 bytes) of input from the source */
58 : /* file. */
59 : /************************************************************************/
60 :
61 24346 : void OGRDXFReaderASCII::LoadDiskChunk()
62 :
63 : {
64 24346 : if (nSrcBufferBytes - iSrcBufferOffset > 511)
65 0 : return;
66 :
67 24346 : if (iSrcBufferOffset > 0)
68 : {
69 23609 : CPLAssert(nSrcBufferBytes <= 1024);
70 23609 : CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
71 :
72 23609 : memmove(achSrcBuffer.data(), achSrcBuffer.data() + iSrcBufferOffset,
73 23609 : nSrcBufferBytes - iSrcBufferOffset);
74 23609 : iSrcBufferFileOffset += iSrcBufferOffset;
75 23609 : nSrcBufferBytes -= iSrcBufferOffset;
76 23609 : iSrcBufferOffset = 0;
77 : }
78 :
79 24346 : nSrcBufferBytes += static_cast<int>(
80 24346 : VSIFReadL(achSrcBuffer.data() + nSrcBufferBytes, 1, 512, fp));
81 24346 : achSrcBuffer[nSrcBufferBytes] = '\0';
82 :
83 24346 : CPLAssert(nSrcBufferBytes <= 1024);
84 24346 : 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 230630 : 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 230630 : if (nSrcBufferBytes - iSrcBufferOffset < 512)
100 24343 : LoadDiskChunk();
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Capture the value code, and skip past it. */
104 : /* -------------------------------------------------------------------- */
105 230630 : unsigned int iStartSrcBufferOffset = iSrcBufferOffset;
106 230630 : int nValueCode = atoi(achSrcBuffer.data() + iSrcBufferOffset);
107 :
108 230630 : nLineNumber++;
109 :
110 : // proceed to newline.
111 918329 : while (achSrcBuffer[iSrcBufferOffset] != '\n' &&
112 1606300 : achSrcBuffer[iSrcBufferOffset] != '\r' &&
113 687969 : achSrcBuffer[iSrcBufferOffset] != '\0')
114 687699 : iSrcBufferOffset++;
115 :
116 230630 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
117 270 : return -1;
118 :
119 : // skip past newline. CR, CRLF, or LFCR
120 230360 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
121 452267 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
122 221907 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
123 221891 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
124 8454 : iSrcBufferOffset += 2;
125 : else
126 221906 : iSrcBufferOffset += 1;
127 :
128 230360 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
129 0 : return -1;
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* Capture the value string. */
133 : /* -------------------------------------------------------------------- */
134 230360 : unsigned int iEOL = iSrcBufferOffset;
135 460720 : CPLString osValue;
136 :
137 230360 : nLineNumber++;
138 :
139 : // proceed to newline.
140 3300120 : while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
141 1534880 : achSrcBuffer[iEOL] != '\0')
142 1534880 : iEOL++;
143 :
144 230360 : bool bLongLine = false;
145 460721 : while (achSrcBuffer[iEOL] == '\0' ||
146 230359 : (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 230359 : size_t nValueBufLen = 0;
180 :
181 : // If this was an extremely long line, copy from osValue into the buffer
182 230359 : 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 230359 : if (static_cast<int>(iEOL - iSrcBufferOffset) >
198 230359 : 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 460714 : strncpy(pszValueBuf + nValueBufLen,
211 460714 : achSrcBuffer.data() + iSrcBufferOffset,
212 230357 : iEOL - iSrcBufferOffset);
213 230357 : pszValueBuf[nValueBufLen + iEOL - iSrcBufferOffset] = '\0';
214 : }
215 :
216 230359 : iSrcBufferOffset = iEOL;
217 :
218 : // skip past newline. CR, CRLF, or LFCR
219 230359 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
220 452265 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
221 221906 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
222 221894 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
223 8453 : iSrcBufferOffset += 2;
224 : else
225 221906 : iSrcBufferOffset += 1;
226 :
227 : /* -------------------------------------------------------------------- */
228 : /* Record how big this value was, so it can be unread safely. */
229 : /* -------------------------------------------------------------------- */
230 230359 : if (bLongLine)
231 2 : nLastValueSize = 0;
232 : else
233 : {
234 230357 : nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset;
235 230357 : CPLAssert(nLastValueSize > 0);
236 : }
237 :
238 230359 : return nValueCode;
239 : }
240 :
241 230630 : int OGRDXFReaderASCII::ReadValue(char *pszValueBuf, int nValueBufSize)
242 : {
243 : int nValueCode;
244 : while (true)
245 : {
246 230630 : nValueCode = ReadValueRaw(pszValueBuf, nValueBufSize);
247 230630 : if (nValueCode == 999)
248 : {
249 : // Skip comments
250 13 : continue;
251 : }
252 230617 : break;
253 : }
254 230617 : return nValueCode;
255 : }
256 :
257 : /************************************************************************/
258 : /* UnreadValue() */
259 : /* */
260 : /* Unread the last value read, accomplished by resetting the */
261 : /* read pointer. */
262 : /************************************************************************/
263 :
264 2597 : void OGRDXFReaderASCII::UnreadValue()
265 :
266 : {
267 2597 : 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 2596 : CPLAssert(iSrcBufferOffset >= nLastValueSize);
274 2596 : CPLAssert(nLastValueSize > 0);
275 :
276 2596 : iSrcBufferOffset -= nLastValueSize;
277 2596 : nLineNumber -= 2;
278 2596 : nLastValueSize = 0;
279 : }
280 :
281 5935 : int OGRDXFReaderBinary::ReadValue(char *pszValueBuffer, int nValueBufferSize)
282 : {
283 5935 : if (VSIFTellL(fp) == 0)
284 : {
285 2 : VSIFSeekL(
286 2 : fp, static_cast<vsi_l_offset>(AUTOCAD_BINARY_DXF_SIGNATURE.size()),
287 : SEEK_SET);
288 : }
289 5935 : if (VSIFTellL(fp) == AUTOCAD_BINARY_DXF_SIGNATURE.size())
290 : {
291 : // Detect if the file is AutoCAD Binary r12
292 7 : GByte abyZeroSection[8] = {0};
293 7 : if (VSIFReadL(abyZeroSection, 1, sizeof(abyZeroSection), fp) !=
294 : sizeof(abyZeroSection))
295 : {
296 0 : CPLError(CE_Failure, CPLE_FileIO, "File too short");
297 0 : return -1;
298 : }
299 7 : m_bIsR12 = memcmp(abyZeroSection, "\x00SECTION", 8) == 0;
300 7 : VSIFSeekL(
301 7 : fp, static_cast<vsi_l_offset>(AUTOCAD_BINARY_DXF_SIGNATURE.size()),
302 : SEEK_SET);
303 : }
304 :
305 5935 : m_nPrevPos = VSIFTellL(fp);
306 :
307 5935 : uint16_t nCode = 0;
308 5935 : bool bReadCodeUINT16 = true;
309 5935 : if (m_bIsR12)
310 : {
311 1480 : GByte nCodeByte = 0;
312 1480 : if (VSIFReadL(&nCodeByte, 1, 1, fp) != 1)
313 : {
314 0 : CPLError(CE_Failure, CPLE_FileIO, "File too short");
315 0 : return -1;
316 : }
317 1480 : bReadCodeUINT16 = (nCodeByte == 255);
318 1480 : if (!bReadCodeUINT16)
319 1480 : nCode = nCodeByte;
320 : }
321 5935 : if (bReadCodeUINT16)
322 : {
323 4455 : if (VSIFReadL(&nCode, 1, sizeof(uint16_t), fp) != sizeof(uint16_t))
324 : {
325 0 : CPLError(CE_Failure, CPLE_FileIO, "File too short");
326 0 : return -1;
327 : }
328 4455 : CPL_LSBPTR16(&nCode);
329 : }
330 :
331 : // Credits to ezdxf for the ranges
332 5935 : bool bRet = true;
333 5935 : if (nCode >= 290 && nCode < 300)
334 : {
335 28 : GByte nVal = 0;
336 28 : bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == 1;
337 28 : CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal);
338 : // CPLDebug("DXF", "Read %d: %d", nCode, nVal);
339 : }
340 5907 : else if ((nCode >= 60 && nCode < 80) || (nCode >= 170 && nCode < 180) ||
341 4852 : (nCode >= 270 && nCode < 290) || (nCode >= 370 && nCode < 390) ||
342 4576 : (nCode >= 400 && nCode < 410) || (nCode >= 1060 && nCode < 1071))
343 : {
344 1334 : int16_t nVal = 0;
345 1334 : bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
346 1334 : CPL_LSBPTR16(&nVal);
347 1334 : CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal);
348 : // CPLDebug("DXF", "Read %d: %d", nCode, nVal);
349 : }
350 4573 : else if ((nCode >= 90 && nCode < 100) || (nCode >= 420 && nCode < 430) ||
351 4492 : (nCode >= 440 && nCode < 460) || (nCode == 1071))
352 : {
353 84 : int32_t nVal = 0;
354 84 : bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
355 84 : CPL_LSBPTR32(&nVal);
356 84 : CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal);
357 : // CPLDebug("DXF", "Read %d: %d", nCode, nVal);
358 : }
359 4489 : else if (nCode >= 160 && nCode < 170)
360 : {
361 3 : int64_t nVal = 0;
362 3 : bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
363 3 : CPL_LSBPTR64(&nVal);
364 3 : CPLsnprintf(pszValueBuffer, nValueBufferSize, "%" PRId64, nVal);
365 : // CPLDebug("DXF", "Read %d: %" PRId64, nCode, nVal);
366 : }
367 4486 : else if ((nCode >= 10 && nCode < 60) || (nCode >= 110 && nCode < 150) ||
368 3084 : (nCode >= 210 && nCode < 240) || (nCode >= 460 && nCode < 470) ||
369 3084 : (nCode >= 1010 && nCode < 1060))
370 : {
371 1402 : double dfVal = 0;
372 1402 : bRet = VSIFReadL(&dfVal, 1, sizeof(dfVal), fp) == sizeof(dfVal);
373 1402 : CPL_LSBPTR64(&dfVal);
374 1402 : CPLsnprintf(pszValueBuffer, nValueBufferSize, "%.17g", dfVal);
375 : // CPLDebug("DXF", "Read %d: %g", nCode, dfVal);
376 : }
377 3084 : else if ((nCode >= 310 && nCode < 320) || nCode == 1004)
378 : {
379 : // Binary
380 11 : GByte nChunkLength = 0;
381 11 : bRet = VSIFReadL(&nChunkLength, 1, sizeof(nChunkLength), fp) ==
382 : sizeof(nChunkLength);
383 11 : std::vector<GByte> abyData(nChunkLength);
384 11 : bRet &= VSIFReadL(abyData.data(), 1, nChunkLength, fp) == nChunkLength;
385 11 : if (2 * nChunkLength + 1 > nValueBufferSize)
386 : {
387 0 : CPLError(CE_Failure, CPLE_AppDefined,
388 : "Provided buffer too small to store string");
389 0 : return -1;
390 : }
391 1205 : for (int i = 0; i < nChunkLength; ++i)
392 : {
393 1194 : snprintf(pszValueBuffer + 2 * i, nValueBufferSize - 2 * i, "%02X",
394 1194 : abyData[i]);
395 : }
396 22 : pszValueBuffer[2 * nChunkLength] = 0;
397 : // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer);
398 : }
399 : else
400 : {
401 : // Zero terminated string
402 3073 : bool bEOS = false;
403 26992 : for (int i = 0; bRet && i < nValueBufferSize; ++i)
404 : {
405 26992 : char ch = 0;
406 26992 : bRet = VSIFReadL(&ch, 1, 1, fp) == 1;
407 26992 : pszValueBuffer[i] = ch;
408 26992 : if (ch == 0)
409 : {
410 : // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer);
411 3073 : bEOS = true;
412 3073 : break;
413 : }
414 : }
415 3073 : if (!bEOS)
416 : {
417 0 : CPLError(CE_Failure, CPLE_AppDefined,
418 : "Provided buffer too small to store string");
419 0 : while (bRet)
420 : {
421 0 : char ch = 0;
422 0 : bRet = VSIFReadL(&ch, 1, 1, fp) == 1;
423 0 : if (ch == 0)
424 : {
425 0 : break;
426 : }
427 : }
428 0 : return -1;
429 : }
430 : }
431 :
432 5935 : if (!bRet)
433 : {
434 0 : CPLError(CE_Failure, CPLE_FileIO, "File too short");
435 0 : return -1;
436 : }
437 5935 : return nCode;
438 : }
439 :
440 54 : void OGRDXFReaderBinary::UnreadValue()
441 : {
442 54 : if (m_nPrevPos == static_cast<uint64_t>(-1))
443 : {
444 0 : CPLError(CE_Failure, CPLE_AppDefined,
445 : "UnreadValue() can be called just once after ReadValue()");
446 : }
447 : else
448 : {
449 54 : VSIFSeekL(fp, m_nPrevPos, SEEK_SET);
450 54 : m_nPrevPos = static_cast<uint64_t>(-1);
451 : }
452 54 : }
453 :
454 9 : void OGRDXFReaderBinary::ResetReadPointer(uint64_t nPos, int nNewLineNumber)
455 : {
456 9 : VSIFSeekL(fp, nPos, SEEK_SET);
457 9 : nLineNumber = nNewLineNumber;
458 9 : }
|