Coverage Report

Created: 2025-08-11 09:23

/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
}