Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxfreader.cpp
Line
Count
Source
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.4k
OGRDXFReaderBase::~OGRDXFReaderBase() = default;
26
27
/************************************************************************/
28
/*                             Initialize()                             */
29
/************************************************************************/
30
31
void OGRDXFReaderBase::Initialize(VSILFILE *fpIn)
32
33
27.3k
{
34
27.3k
    fp = fpIn;
35
27.3k
}
36
37
/************************************************************************/
38
/*                          ResetReadPointer()                          */
39
/************************************************************************/
40
41
void OGRDXFReaderASCII::ResetReadPointer(uint64_t iNewOffset,
42
                                         int nNewLineNumber /* = 0 */)
43
44
19.9k
{
45
19.9k
    nSrcBufferBytes = 0;
46
19.9k
    iSrcBufferOffset = 0;
47
19.9k
    iSrcBufferFileOffset = iNewOffset;
48
19.9k
    nLastValueSize = 0;
49
19.9k
    nLineNumber = nNewLineNumber;
50
51
19.9k
    VSIFSeekL(fp, iNewOffset, SEEK_SET);
52
19.9k
}
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.93M
{
64
1.93M
    if (nSrcBufferBytes - iSrcBufferOffset > 511)
65
696
        return;
66
67
1.93M
    if (iSrcBufferOffset > 0)
68
1.81M
    {
69
1.81M
        CPLAssert(nSrcBufferBytes <= 1024);
70
1.81M
        CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
71
72
1.81M
        memmove(achSrcBuffer.data(), achSrcBuffer.data() + iSrcBufferOffset,
73
1.81M
                nSrcBufferBytes - iSrcBufferOffset);
74
1.81M
        iSrcBufferFileOffset += iSrcBufferOffset;
75
1.81M
        nSrcBufferBytes -= iSrcBufferOffset;
76
1.81M
        iSrcBufferOffset = 0;
77
1.81M
    }
78
79
1.93M
    nSrcBufferBytes += static_cast<int>(
80
1.93M
        VSIFReadL(achSrcBuffer.data() + nSrcBufferBytes, 1, 512, fp));
81
1.93M
    achSrcBuffer[nSrcBufferBytes] = '\0';
82
83
1.93M
    CPLAssert(nSrcBufferBytes <= 1024);
84
1.93M
    CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
85
1.93M
}
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
41.0M
{
96
    /* -------------------------------------------------------------------- */
97
    /*      Make sure we have lots of data in our buffer for one value.     */
98
    /* -------------------------------------------------------------------- */
99
41.0M
    if (nSrcBufferBytes - iSrcBufferOffset < 512)
100
1.91M
        LoadDiskChunk();
101
102
    /* -------------------------------------------------------------------- */
103
    /*      Capture the value code, and skip past it.                       */
104
    /* -------------------------------------------------------------------- */
105
41.0M
    unsigned int iStartSrcBufferOffset = iSrcBufferOffset;
106
41.0M
    int nValueCode = atoi(achSrcBuffer.data() + iSrcBufferOffset);
107
108
41.0M
    nLineNumber++;
109
110
    // proceed to newline.
111
248M
    while (achSrcBuffer[iSrcBufferOffset] != '\n' &&
112
223M
           achSrcBuffer[iSrcBufferOffset] != '\r' &&
113
207M
           achSrcBuffer[iSrcBufferOffset] != '\0')
114
207M
        iSrcBufferOffset++;
115
116
41.0M
    if (achSrcBuffer[iSrcBufferOffset] == '\0')
117
23.6k
        return -1;
118
119
    // skip past newline.  CR, CRLF, or LFCR
120
40.9M
    if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
121
16.7M
         achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
122
35.9M
        (achSrcBuffer[iSrcBufferOffset] == '\n' &&
123
24.2M
         achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
124
7.93M
        iSrcBufferOffset += 2;
125
33.0M
    else
126
33.0M
        iSrcBufferOffset += 1;
127
128
40.9M
    if (achSrcBuffer[iSrcBufferOffset] == '\0')
129
2.64k
        return -1;
130
131
    /* -------------------------------------------------------------------- */
132
    /*      Capture the value string.                                       */
133
    /* -------------------------------------------------------------------- */
134
40.9M
    unsigned int iEOL = iSrcBufferOffset;
135
40.9M
    CPLString osValue;
136
137
40.9M
    nLineNumber++;
138
139
    // proceed to newline.
140
284M
    while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
141
243M
           achSrcBuffer[iEOL] != '\0')
142
243M
        iEOL++;
143
144
40.9M
    bool bLongLine = false;
145
40.9M
    while (achSrcBuffer[iEOL] == '\0' ||
146
40.9M
           (achSrcBuffer[iEOL] == '\r' && achSrcBuffer[iEOL + 1] == '\0'))
147
23.0k
    {
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
23.0k
        const auto nValueLength = osValue.length();
152
153
23.0k
        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
23.0k
        osValue.resize(nValueLength + iEOL - iSrcBufferOffset, '\0');
161
23.0k
        std::copy(achSrcBuffer.data() + iSrcBufferOffset,
162
23.0k
                  achSrcBuffer.data() + iEOL, osValue.begin() + nValueLength);
163
164
23.0k
        iSrcBufferOffset = iEOL;
165
23.0k
        LoadDiskChunk();
166
23.0k
        iEOL = iSrcBufferOffset;
167
23.0k
        bLongLine = true;
168
169
        // Have we prematurely reached the end of the file?
170
23.0k
        if (achSrcBuffer[iEOL] == '\0')
171
8.96k
            return -1;
172
173
        // Proceed to newline again
174
4.34M
        while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
175
4.33M
               achSrcBuffer[iEOL] != '\0')
176
4.32M
            iEOL++;
177
14.0k
    }
178
179
40.9M
    size_t nValueBufLen = 0;
180
181
    // If this was an extremely long line, copy from osValue into the buffer
182
40.9M
    if (!osValue.empty())
183
8.90k
    {
184
8.90k
        strncpy(pszValueBuf, osValue.c_str(), nValueBufSize - 1);
185
8.90k
        pszValueBuf[nValueBufSize - 1] = '\0';
186
187
8.90k
        nValueBufLen = strlen(pszValueBuf);
188
189
8.90k
        if (static_cast<int>(osValue.length()) > nValueBufSize - 1)
190
4.06k
        {
191
4.06k
            CPLDebug("DXF", "Long line truncated to %d characters.\n%s...",
192
4.06k
                     nValueBufSize - 1, pszValueBuf);
193
4.06k
        }
194
8.90k
    }
195
196
    // Copy the last (normally, the only) section of this line into the buffer
197
40.9M
    if (static_cast<int>(iEOL - iSrcBufferOffset) >
198
40.9M
        nValueBufSize - static_cast<int>(nValueBufLen) - 1)
199
21.5k
    {
200
21.5k
        strncpy(pszValueBuf + nValueBufLen,
201
21.5k
                achSrcBuffer.data() + iSrcBufferOffset,
202
21.5k
                nValueBufSize - static_cast<int>(nValueBufLen) - 1);
203
21.5k
        pszValueBuf[nValueBufSize - 1] = '\0';
204
205
21.5k
        CPLDebug("DXF", "Long line truncated to %d characters.\n%s...",
206
21.5k
                 nValueBufSize - 1, pszValueBuf);
207
21.5k
    }
208
40.9M
    else
209
40.9M
    {
210
40.9M
        strncpy(pszValueBuf + nValueBufLen,
211
40.9M
                achSrcBuffer.data() + iSrcBufferOffset,
212
40.9M
                iEOL - iSrcBufferOffset);
213
40.9M
        pszValueBuf[nValueBufLen + iEOL - iSrcBufferOffset] = '\0';
214
40.9M
    }
215
216
40.9M
    iSrcBufferOffset = iEOL;
217
218
    // skip past newline.  CR, CRLF, or LFCR
219
40.9M
    if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
220
16.6M
         achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
221
35.9M
        (achSrcBuffer[iSrcBufferOffset] == '\n' &&
222
24.2M
         achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
223
7.96M
        iSrcBufferOffset += 2;
224
33.0M
    else
225
33.0M
        iSrcBufferOffset += 1;
226
227
    /* -------------------------------------------------------------------- */
228
    /*      Record how big this value was, so it can be unread safely.      */
229
    /* -------------------------------------------------------------------- */
230
40.9M
    if (bLongLine)
231
8.90k
        nLastValueSize = 0;
232
40.9M
    else
233
40.9M
    {
234
40.9M
        nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset;
235
40.9M
        CPLAssert(nLastValueSize > 0);
236
40.9M
    }
237
238
40.9M
    return nValueCode;
239
40.9M
}
240
241
int OGRDXFReaderASCII::ReadValue(char *pszValueBuf, int nValueBufSize)
242
40.9M
{
243
40.9M
    int nValueCode;
244
41.0M
    while (true)
245
41.0M
    {
246
41.0M
        nValueCode = ReadValueRaw(pszValueBuf, nValueBufSize);
247
41.0M
        if (nValueCode == 999)
248
17.0k
        {
249
            // Skip comments
250
17.0k
            continue;
251
17.0k
        }
252
40.9M
        break;
253
41.0M
    }
254
40.9M
    return nValueCode;
255
40.9M
}
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.21M
{
267
1.21M
    if (nLastValueSize == 0)
268
454
    {
269
454
        CPLError(CE_Failure, CPLE_AppDefined,
270
454
                 "Cannot UnreadValue(), likely due to a previous long line");
271
454
        return;
272
454
    }
273
1.21M
    CPLAssert(iSrcBufferOffset >= nLastValueSize);
274
1.21M
    CPLAssert(nLastValueSize > 0);
275
276
1.21M
    iSrcBufferOffset -= nLastValueSize;
277
1.21M
    nLineNumber -= 2;
278
1.21M
    nLastValueSize = 0;
279
1.21M
}
280
281
int OGRDXFReaderBinary::ReadValue(char *pszValueBuffer, int nValueBufferSize)
282
112
{
283
112
    if (VSIFTellL(fp) == 0)
284
0
    {
285
0
        VSIFSeekL(fp, AUTOCAD_BINARY_DXF_SIGNATURE.size(), SEEK_SET);
286
0
    }
287
112
    if (VSIFTellL(fp) == AUTOCAD_BINARY_DXF_SIGNATURE.size())
288
108
    {
289
        // Detect if the file is AutoCAD Binary r12
290
108
        GByte abyZeroSection[8] = {0};
291
108
        if (VSIFReadL(abyZeroSection, 1, sizeof(abyZeroSection), fp) !=
292
108
            sizeof(abyZeroSection))
293
2
        {
294
2
            CPLError(CE_Failure, CPLE_FileIO, "File too short");
295
2
            return -1;
296
2
        }
297
106
        m_bIsR12 = memcmp(abyZeroSection, "\x00SECTION", 8) == 0;
298
106
        VSIFSeekL(fp, AUTOCAD_BINARY_DXF_SIGNATURE.size(), SEEK_SET);
299
106
    }
300
301
110
    m_nPrevPos = VSIFTellL(fp);
302
303
110
    uint16_t nCode = 0;
304
110
    bool bReadCodeUINT16 = true;
305
110
    if (m_bIsR12)
306
13
    {
307
13
        GByte nCodeByte = 0;
308
13
        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
13
        bReadCodeUINT16 = (nCodeByte == 255);
314
13
        if (!bReadCodeUINT16)
315
11
            nCode = nCodeByte;
316
13
    }
317
110
    if (bReadCodeUINT16)
318
99
    {
319
99
        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
99
        CPL_LSBPTR16(&nCode);
325
99
    }
326
327
    // Credits to ezdxf for the ranges
328
110
    bool bRet = true;
329
110
    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
109
    else if ((nCode >= 60 && nCode < 80) || (nCode >= 170 && nCode < 180) ||
337
106
             (nCode >= 270 && nCode < 290) || (nCode >= 370 && nCode < 390) ||
338
105
             (nCode >= 400 && nCode < 410) || (nCode >= 1060 && nCode < 1071))
339
6
    {
340
6
        int16_t nVal = 0;
341
6
        bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
342
6
        CPL_LSBPTR16(&nVal);
343
6
        CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal);
344
        // CPLDebug("DXF", "Read %d: %d", nCode, nVal);
345
6
    }
346
103
    else if ((nCode >= 90 && nCode < 100) || (nCode >= 420 && nCode < 430) ||
347
101
             (nCode >= 440 && nCode < 460) || (nCode == 1071))
348
4
    {
349
4
        int32_t nVal = 0;
350
4
        bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
351
4
        CPL_LSBPTR32(&nVal);
352
4
        CPLsnprintf(pszValueBuffer, nValueBufferSize, "%d", nVal);
353
        // CPLDebug("DXF", "Read %d: %d", nCode, nVal);
354
4
    }
355
99
    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
99
    else if ((nCode >= 10 && nCode < 60) || (nCode >= 110 && nCode < 150) ||
364
96
             (nCode >= 210 && nCode < 240) || (nCode >= 460 && nCode < 470) ||
365
94
             (nCode >= 1010 && nCode < 1060))
366
6
    {
367
6
        double dfVal = 0;
368
6
        bRet = VSIFReadL(&dfVal, 1, sizeof(dfVal), fp) == sizeof(dfVal);
369
6
        CPL_LSBPTR64(&dfVal);
370
6
        CPLsnprintf(pszValueBuffer, nValueBufferSize, "%.17g", dfVal);
371
        // CPLDebug("DXF", "Read %d: %g", nCode, dfVal);
372
6
    }
373
93
    else if ((nCode >= 310 && nCode < 320) || nCode == 1004)
374
11
    {
375
        // Binary
376
11
        GByte nChunkLength = 0;
377
11
        bRet = VSIFReadL(&nChunkLength, 1, sizeof(nChunkLength), fp) ==
378
11
               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
5
        {
383
5
            CPLError(CE_Failure, CPLE_AppDefined,
384
5
                     "Provided buffer too small to store string");
385
5
            return -1;
386
5
        }
387
48
        for (int i = 0; i < nChunkLength; ++i)
388
42
        {
389
42
            snprintf(pszValueBuffer + 2 * i, nValueBufferSize - 2 * i, "%02X",
390
42
                     abyData[i]);
391
42
        }
392
6
        pszValueBuffer[2 * nChunkLength] = 0;
393
        // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer);
394
6
    }
395
82
    else
396
82
    {
397
        // Zero terminated string
398
82
        bool bEOS = false;
399
3.44k
        for (int i = 0; bRet && i < nValueBufferSize; ++i)
400
3.41k
        {
401
3.41k
            char ch = 0;
402
3.41k
            bRet = VSIFReadL(&ch, 1, 1, fp) == 1;
403
3.41k
            pszValueBuffer[i] = ch;
404
3.41k
            if (ch == 0)
405
51
            {
406
                // CPLDebug("DXF", "Read %d: '%s'", nCode, pszValueBuffer);
407
51
                bEOS = true;
408
51
                break;
409
51
            }
410
3.41k
        }
411
82
        if (!bEOS)
412
31
        {
413
31
            CPLError(CE_Failure, CPLE_AppDefined,
414
31
                     "Provided buffer too small to store string");
415
25.6k
            while (bRet)
416
25.6k
            {
417
25.6k
                char ch = 0;
418
25.6k
                bRet = VSIFReadL(&ch, 1, 1, fp) == 1;
419
25.6k
                if (ch == 0)
420
31
                {
421
31
                    break;
422
31
                }
423
25.6k
            }
424
31
            return -1;
425
31
        }
426
82
    }
427
428
74
    if (!bRet)
429
26
    {
430
26
        CPLError(CE_Failure, CPLE_FileIO, "File too short");
431
26
        return -1;
432
26
    }
433
48
    return nCode;
434
74
}
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
    VSIFSeekL(fp, nPos, SEEK_SET);
453
0
    nLineNumber = nNewLineNumber;
454
0
}