LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 193 215 89.8 %
Date: 2025-07-13 09:04:35 Functions: 9 9 100.0 %

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

Generated by: LCOV version 1.14