LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 405 495 81.8 %
Date: 2025-08-01 10:10:57 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements OGRDXFDataSource class
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.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             : 
      18             : #include <algorithm>
      19             : 
      20             : /************************************************************************/
      21             : /*                          OGRDXFDataSource()                          */
      22             : /************************************************************************/
      23             : 
      24         217 : OGRDXFDataSource::OGRDXFDataSource()
      25             :     : fp(nullptr), iEntitiesOffset(0), iEntitiesLineNumber(0),
      26             :       bInlineBlocks(false), bMergeBlockGeometries(false),
      27             :       bTranslateEscapeSequences(false), bIncludeRawCodeValues(false),
      28             :       b3DExtensibleMode(false), bHaveReadSolidData(false),
      29         217 :       poReader(std::make_unique<OGRDXFReaderASCII>())
      30             : {
      31         217 : }
      32             : 
      33             : /************************************************************************/
      34             : /*                         ~OGRDXFDataSource()                          */
      35             : /************************************************************************/
      36             : 
      37         584 : OGRDXFDataSource::~OGRDXFDataSource()
      38             : 
      39             : {
      40             :     /* -------------------------------------------------------------------- */
      41             :     /*      Destroy layers.                                                 */
      42             :     /* -------------------------------------------------------------------- */
      43         439 :     while (!apoLayers.empty())
      44             :     {
      45         222 :         delete apoLayers.back();
      46         222 :         apoLayers.pop_back();
      47             :     }
      48             : 
      49             :     /* -------------------------------------------------------------------- */
      50             :     /*      Close file.                                                     */
      51             :     /* -------------------------------------------------------------------- */
      52         217 :     if (fp != nullptr)
      53             :     {
      54         217 :         VSIFCloseL(fp);
      55         217 :         fp = nullptr;
      56             :     }
      57         367 : }
      58             : 
      59             : /************************************************************************/
      60             : /*                           TestCapability()                           */
      61             : /************************************************************************/
      62             : 
      63           1 : int OGRDXFDataSource::TestCapability(const char *pszCap)
      64             : {
      65           1 :     if (EQUAL(pszCap, ODsCZGeometries))
      66           0 :         return true;
      67             : 
      68           1 :     return false;
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                              GetLayer()                              */
      73             : /************************************************************************/
      74             : 
      75         495 : OGRLayer *OGRDXFDataSource::GetLayer(int iLayer)
      76             : 
      77             : {
      78         495 :     if (iLayer < 0 || iLayer >= (int)apoLayers.size())
      79           0 :         return nullptr;
      80             :     else
      81         495 :         return apoLayers[iLayer];
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                                Open()                                */
      86             : /************************************************************************/
      87             : 
      88         217 : bool OGRDXFDataSource::Open(const char *pszFilename, VSILFILE *fpIn,
      89             :                             bool bHeaderOnly, CSLConstList papszOptionsIn)
      90             : 
      91             : {
      92         217 :     SetDescription(pszFilename);
      93             : 
      94         217 :     osEncoding = CPL_ENC_ISO8859_1;
      95             : 
      96         217 :     bInlineBlocks = CPLTestBool(
      97             :         CSLFetchNameValueDef(papszOptionsIn, "INLINE_BLOCKS",
      98             :                              CPLGetConfigOption("DXF_INLINE_BLOCKS", "TRUE")));
      99         217 :     bMergeBlockGeometries = CPLTestBool(CSLFetchNameValueDef(
     100             :         papszOptionsIn, "MERGE_BLOCK_GEOMETRIES",
     101             :         CPLGetConfigOption("DXF_MERGE_BLOCK_GEOMETRIES", "TRUE")));
     102             : 
     103         217 :     bTranslateEscapeSequences = CPLTestBool(CSLFetchNameValueDef(
     104             :         papszOptionsIn, "TRANSLATE_ESCAPE_SEQUENCES",
     105             :         CPLGetConfigOption("DXF_TRANSLATE_ESCAPE_SEQUENCES", "TRUE")));
     106             : 
     107         217 :     bIncludeRawCodeValues = CPLTestBool(CSLFetchNameValueDef(
     108             :         papszOptionsIn, "INCLUDE_RAW_CODE_VALUES",
     109             :         CPLGetConfigOption("DXF_INCLUDE_RAW_CODE_VALUES", "FALSE")));
     110             : 
     111         217 :     b3DExtensibleMode = CPLTestBool(CSLFetchNameValueDef(
     112             :         papszOptionsIn, "3D_EXTENSIBLE_MODE",
     113             :         CPLGetConfigOption("DXF_3D_EXTENSIBLE_MODE", "FALSE")));
     114             : 
     115         217 :     m_bClosedLineAsPolygon = CPLTestBool(CSLFetchNameValueDef(
     116             :         papszOptionsIn, "CLOSED_LINE_AS_POLYGON",
     117             :         CPLGetConfigOption("DXF_CLOSED_LINE_AS_POLYGON", "FALSE")));
     118             : 
     119         217 :     m_dfHatchTolerance = CPLAtof(
     120             :         CSLFetchNameValueDef(papszOptionsIn, "HATCH_TOLERANCE",
     121             :                              CPLGetConfigOption("DXF_HATCH_TOLERANCE", "-1")));
     122             : 
     123             :     // Only for debugging
     124         217 :     if (CPLTestBool(CPLGetConfigOption("DXF_HEADER_ONLY", "FALSE")))
     125           0 :         bHeaderOnly = true;
     126             : 
     127             :     /* -------------------------------------------------------------------- */
     128             :     /*      Open the file.                                                  */
     129             :     /* -------------------------------------------------------------------- */
     130         217 :     fp = fpIn;
     131         217 :     if (fp == nullptr)
     132          67 :         fp = VSIFOpenL(pszFilename, "rb");
     133         217 :     if (fp == nullptr)
     134           0 :         return false;
     135             : 
     136         217 :     VSIFSeekL(fp, 0, SEEK_SET);
     137         434 :     std::string osBuffer;
     138         217 :     constexpr size_t nBinarySignatureLen = AUTOCAD_BINARY_DXF_SIGNATURE.size();
     139         217 :     osBuffer.resize(nBinarySignatureLen);
     140         217 :     VSIFReadL(osBuffer.data(), 1, osBuffer.size(), fp);
     141         217 :     if (memcmp(osBuffer.data(), AUTOCAD_BINARY_DXF_SIGNATURE.data(),
     142         217 :                nBinarySignatureLen) == 0)
     143             :     {
     144           5 :         poReader = std::make_unique<OGRDXFReaderBinary>();
     145             :     }
     146             :     else
     147             :     {
     148         212 :         VSIFSeekL(fp, 0, SEEK_SET);
     149             :     }
     150             : 
     151         217 :     poReader->Initialize(fp);
     152             : 
     153             :     /* -------------------------------------------------------------------- */
     154             :     /*      Confirm we have a header section.                               */
     155             :     /* -------------------------------------------------------------------- */
     156             :     char szLineBuf[257];
     157         217 :     bool bEntitiesOnly = false;
     158             : 
     159         217 :     if (ReadValue(szLineBuf) != 0 || !EQUAL(szLineBuf, "SECTION"))
     160           0 :         return false;
     161             : 
     162         434 :     if (ReadValue(szLineBuf) != 2 ||
     163         217 :         (!EQUAL(szLineBuf, "HEADER") && !EQUAL(szLineBuf, "ENTITIES") &&
     164           9 :          !EQUAL(szLineBuf, "TABLES")))
     165           0 :         return false;
     166             : 
     167         217 :     if (EQUAL(szLineBuf, "ENTITIES"))
     168          19 :         bEntitiesOnly = true;
     169             : 
     170             :     /* Some files might have no header but begin directly with a TABLES section
     171             :      */
     172         198 :     else if (EQUAL(szLineBuf, "TABLES"))
     173             :     {
     174             :         osEncoding = CSLFetchNameValueDef(
     175             :             papszOptionsIn, "ENCODING",
     176           9 :             CPLGetConfigOption("DXF_ENCODING", osEncoding));
     177             : 
     178           9 :         if (!ReadTablesSection())
     179           0 :             return false;
     180           9 :         if (ReadValue(szLineBuf) < 0)
     181             :         {
     182           0 :             DXF_READER_ERROR();
     183           0 :             return false;
     184             :         }
     185             :     }
     186             : 
     187             :     /* -------------------------------------------------------------------- */
     188             :     /*      Process the header, picking up a few useful pieces of           */
     189             :     /*      information.                                                    */
     190             :     /* -------------------------------------------------------------------- */
     191             :     else /* if( EQUAL(szLineBuf,"HEADER") ) */
     192             :     {
     193         189 :         if (!ReadHeaderSection())
     194           0 :             return false;
     195         189 :         if (ReadValue(szLineBuf) < 0)
     196             :         {
     197           0 :             DXF_READER_ERROR();
     198           0 :             return false;
     199             :         }
     200             : 
     201             :         /* --------------------------------------------------------------------
     202             :          */
     203             :         /*      Process the CLASSES section, if present. */
     204             :         /* --------------------------------------------------------------------
     205             :          */
     206         189 :         if (EQUAL(szLineBuf, "ENDSEC"))
     207             :         {
     208           0 :             if (ReadValue(szLineBuf) < 0)
     209             :             {
     210           0 :                 DXF_READER_ERROR();
     211           0 :                 return false;
     212             :             }
     213             :         }
     214             : 
     215         189 :         if (EQUAL(szLineBuf, "SECTION"))
     216             :         {
     217         188 :             if (ReadValue(szLineBuf) < 0)
     218             :             {
     219           0 :                 DXF_READER_ERROR();
     220           0 :                 return false;
     221             :             }
     222             :         }
     223             : 
     224         189 :         if (EQUAL(szLineBuf, "CLASSES"))
     225             :         {
     226             :             // int nCode = 0;
     227        1929 :             while ((/* nCode = */ ReadValue(szLineBuf, sizeof(szLineBuf))) >
     228        3858 :                        -1 &&
     229        1929 :                    !EQUAL(szLineBuf, "ENDSEC"))
     230             :             {
     231             :                 // printf("C:%d/%s\n", nCode, szLineBuf );
     232             :             }
     233             :         }
     234             : 
     235             :         /* --------------------------------------------------------------------
     236             :          */
     237             :         /*      Process the TABLES section, if present. */
     238             :         /* --------------------------------------------------------------------
     239             :          */
     240         189 :         if (EQUAL(szLineBuf, "ENDSEC"))
     241             :         {
     242         149 :             if (ReadValue(szLineBuf) < 0)
     243             :             {
     244           0 :                 DXF_READER_ERROR();
     245           0 :                 return false;
     246             :             }
     247             :         }
     248             : 
     249         189 :         if (EQUAL(szLineBuf, "SECTION"))
     250             :         {
     251         149 :             if (ReadValue(szLineBuf) < 0)
     252             :             {
     253           0 :                 DXF_READER_ERROR();
     254           0 :                 return false;
     255             :             }
     256             :         }
     257             : 
     258         189 :         if (EQUAL(szLineBuf, "TABLES"))
     259             :         {
     260         188 :             if (!ReadTablesSection())
     261           0 :                 return false;
     262         188 :             if (ReadValue(szLineBuf) < 0)
     263             :             {
     264           0 :                 DXF_READER_ERROR();
     265           0 :                 return false;
     266             :             }
     267             :         }
     268             :     }
     269             : 
     270             :     /* -------------------------------------------------------------------- */
     271             :     /*      Create a blocks layer if we are not in inlining mode.           */
     272             :     /* -------------------------------------------------------------------- */
     273         217 :     if (!bInlineBlocks)
     274           5 :         apoLayers.push_back(new OGRDXFBlocksLayer(this));
     275             : 
     276             :     /* -------------------------------------------------------------------- */
     277             :     /*      Create out layer object - we will need it when interpreting     */
     278             :     /*      blocks.                                                         */
     279             :     /* -------------------------------------------------------------------- */
     280         217 :     apoLayers.push_back(new OGRDXFLayer(this));
     281             : 
     282             :     /* -------------------------------------------------------------------- */
     283             :     /*      Process the BLOCKS section if present.                          */
     284             :     /* -------------------------------------------------------------------- */
     285         217 :     if (!bEntitiesOnly)
     286             :     {
     287         198 :         if (EQUAL(szLineBuf, "ENDSEC"))
     288             :         {
     289           0 :             if (ReadValue(szLineBuf) < 0)
     290             :             {
     291           0 :                 DXF_READER_ERROR();
     292           0 :                 return false;
     293             :             }
     294             :         }
     295             : 
     296         198 :         if (EQUAL(szLineBuf, "SECTION"))
     297             :         {
     298         195 :             if (ReadValue(szLineBuf) < 0)
     299             :             {
     300           0 :                 DXF_READER_ERROR();
     301           0 :                 return false;
     302             :             }
     303             :         }
     304             : 
     305         198 :         if (EQUAL(szLineBuf, "BLOCKS"))
     306             :         {
     307         195 :             if (!ReadBlocksSection())
     308           1 :                 return false;
     309         194 :             if (ReadValue(szLineBuf) < 0)
     310             :             {
     311           0 :                 DXF_READER_ERROR();
     312           0 :                 return false;
     313             :             }
     314             :         }
     315             :     }
     316             : 
     317         216 :     if (bHeaderOnly)
     318          67 :         return true;
     319             : 
     320             :     /* -------------------------------------------------------------------- */
     321             :     /*      Now we are at the entities section, hopefully.  Confirm.        */
     322             :     /* -------------------------------------------------------------------- */
     323         149 :     if (EQUAL(szLineBuf, "SECTION"))
     324             :     {
     325         127 :         if (ReadValue(szLineBuf) < 0)
     326             :         {
     327           0 :             DXF_READER_ERROR();
     328           0 :             return false;
     329             :         }
     330             :     }
     331             : 
     332         149 :     if (!EQUAL(szLineBuf, "ENTITIES"))
     333             :     {
     334           0 :         DXF_READER_ERROR();
     335           0 :         return false;
     336             :     }
     337             : 
     338         149 :     iEntitiesOffset = poReader->GetCurrentFilePos();
     339         149 :     iEntitiesLineNumber = poReader->nLineNumber;
     340         149 :     apoLayers[0]->ResetReading();
     341             : 
     342         149 :     return true;
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                         ReadTablesSection()                          */
     347             : /************************************************************************/
     348             : 
     349         197 : bool OGRDXFDataSource::ReadTablesSection()
     350             : 
     351             : {
     352             :     char szLineBuf[257];
     353         197 :     int nCode = 0;
     354             : 
     355        3728 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
     356        1864 :            !EQUAL(szLineBuf, "ENDSEC"))
     357             :     {
     358             :         // We are only interested in extracting tables.
     359        1667 :         if (nCode != 0 || !EQUAL(szLineBuf, "TABLE"))
     360           0 :             continue;
     361             : 
     362        1667 :         nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
     363        1667 :         if (nCode < 0)
     364             :         {
     365           0 :             DXF_READER_ERROR();
     366           0 :             return false;
     367             :         }
     368             : 
     369        1667 :         if (nCode != 2)
     370           0 :             continue;
     371             : 
     372             :         // CPLDebug( "DXF", "Found table %s.", szLineBuf );
     373             : 
     374       48036 :         while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
     375       24018 :                !EQUAL(szLineBuf, "ENDTAB"))
     376             :         {
     377       22351 :             if (nCode == 0 && EQUAL(szLineBuf, "LAYER"))
     378             :             {
     379         213 :                 if (!ReadLayerDefinition())
     380           0 :                     return false;
     381             :             }
     382       22351 :             if (nCode == 0 && EQUAL(szLineBuf, "LTYPE"))
     383             :             {
     384         743 :                 if (!ReadLineTypeDefinition())
     385           0 :                     return false;
     386             :             }
     387       22351 :             if (nCode == 0 && EQUAL(szLineBuf, "STYLE"))
     388             :             {
     389         206 :                 if (!ReadTextStyleDefinition())
     390           0 :                     return false;
     391             :             }
     392       22351 :             if (nCode == 0 && EQUAL(szLineBuf, "DIMSTYLE"))
     393             :             {
     394         190 :                 if (!ReadDimStyleDefinition())
     395           0 :                     return false;
     396             :             }
     397             :         }
     398             :     }
     399         197 :     if (nCode < 0)
     400             :     {
     401           0 :         DXF_READER_ERROR();
     402           0 :         return false;
     403             :     }
     404             : 
     405         197 :     CPLDebug("DXF", "Read %d layer definitions.", (int)oLayerTable.size());
     406         197 :     return true;
     407             : }
     408             : 
     409             : /************************************************************************/
     410             : /*                        ReadLayerDefinition()                         */
     411             : /************************************************************************/
     412             : 
     413         213 : bool OGRDXFDataSource::ReadLayerDefinition()
     414             : 
     415             : {
     416             :     char szLineBuf[257];
     417         213 :     int nCode = 0;
     418         426 :     std::map<CPLString, CPLString> oLayerProperties;
     419         426 :     CPLString osLayerName = "";
     420             : 
     421         213 :     oLayerProperties["Hidden"] = "0";
     422             : 
     423        2246 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     424             :     {
     425        2033 :         switch (nCode)
     426             :         {
     427         213 :             case 2:
     428             :                 osLayerName =
     429         213 :                     CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
     430         213 :                 oLayerProperties["Exists"] = "1";
     431         213 :                 break;
     432             : 
     433         212 :             case 6:
     434         424 :                 oLayerProperties["Linetype"] =
     435         636 :                     CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
     436         212 :                 break;
     437             : 
     438         213 :             case 62:
     439         213 :                 oLayerProperties["Color"] = szLineBuf;
     440             : 
     441             :                 // Is layer off?
     442         213 :                 if (atoi(szLineBuf) < 0 && oLayerProperties["Hidden"] != "2")
     443           2 :                     oLayerProperties["Hidden"] = "1";
     444         213 :                 break;
     445             : 
     446           1 :             case 420:
     447           1 :                 oLayerProperties["TrueColor"] = szLineBuf;
     448           1 :                 break;
     449             : 
     450           0 :             case 440:
     451           0 :                 oLayerProperties["Transparency"] = szLineBuf;
     452           0 :                 break;
     453             : 
     454         213 :             case 70:
     455         213 :                 oLayerProperties["Flags"] = szLineBuf;
     456             : 
     457             :                 // Is layer frozen?
     458         213 :                 if (atoi(szLineBuf) & 0x01)
     459           5 :                     oLayerProperties["Hidden"] = "2";
     460         213 :                 break;
     461             : 
     462         188 :             case 370:
     463             :             case 39:
     464         188 :                 oLayerProperties["LineWeight"] = szLineBuf;
     465         188 :                 break;
     466             : 
     467         993 :             default:
     468         993 :                 break;
     469             :         }
     470             :     }
     471         213 :     if (nCode < 0)
     472             :     {
     473           0 :         DXF_READER_ERROR();
     474           0 :         return false;
     475             :     }
     476             : 
     477         213 :     if (!oLayerProperties.empty())
     478         213 :         oLayerTable[osLayerName] = std::move(oLayerProperties);
     479             : 
     480         213 :     if (nCode == 0)
     481         213 :         UnreadValue();
     482         213 :     return true;
     483             : }
     484             : 
     485             : /************************************************************************/
     486             : /*                        LookupLayerProperty()                         */
     487             : /************************************************************************/
     488             : 
     489             : std::optional<CPLString>
     490       13460 : OGRDXFDataSource::LookupLayerProperty(const char *pszLayer,
     491             :                                       const char *pszProperty) const
     492             : 
     493             : {
     494       13460 :     if (pszLayer)
     495             :     {
     496       13460 :         const auto oIterLayer = oLayerTable.find(pszLayer);
     497       13460 :         if (oIterLayer != oLayerTable.end())
     498             :         {
     499       12618 :             const auto &oTable = oIterLayer->second;
     500       12618 :             const auto oIterProperty = oTable.find(pszProperty);
     501       12618 :             if (oIterProperty != oTable.end())
     502        7086 :                 return oIterProperty->second;
     503             :         }
     504             :     }
     505        6374 :     return std::nullopt;
     506             : }
     507             : 
     508             : /************************************************************************/
     509             : /*                       ReadLineTypeDefinition()                       */
     510             : /************************************************************************/
     511             : 
     512         743 : bool OGRDXFDataSource::ReadLineTypeDefinition()
     513             : 
     514             : {
     515             :     char szLineBuf[257];
     516         743 :     int nCode = 0;
     517        1486 :     CPLString osLineTypeName;
     518        1486 :     std::vector<double> oLineTypeDef;
     519             :     double dfThisValue;
     520             : 
     521        9547 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     522             :     {
     523        8804 :         switch (nCode)
     524             :         {
     525         743 :             case 2:
     526             :                 osLineTypeName =
     527         743 :                     CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
     528         743 :                 break;
     529             : 
     530         840 :             case 49:
     531         840 :                 dfThisValue = CPLAtof(szLineBuf);
     532             : 
     533             :                 // Same sign as the previous entry? Continue the previous dash
     534             :                 // or gap by appending this length
     535        1463 :                 if (oLineTypeDef.size() > 0 &&
     536         623 :                     (dfThisValue < 0) == (oLineTypeDef.back() < 0))
     537             :                 {
     538           1 :                     oLineTypeDef.back() += dfThisValue;
     539             :                 }
     540             :                 // Otherwise, add a new entry
     541             :                 else
     542             :                 {
     543         839 :                     oLineTypeDef.push_back(dfThisValue);
     544             :                 }
     545             : 
     546         840 :                 break;
     547             : 
     548        7221 :             default:
     549        7221 :                 break;
     550             :         }
     551             :     }
     552         743 :     if (nCode < 0)
     553             :     {
     554           0 :         DXF_READER_ERROR();
     555           0 :         return false;
     556             :     }
     557             : 
     558             :     // Deal with an odd number of elements by adding the last element
     559             :     // onto the first
     560         743 :     if (oLineTypeDef.size() % 2 == 1)
     561             :     {
     562           1 :         oLineTypeDef.front() += oLineTypeDef.back();
     563           1 :         oLineTypeDef.pop_back();
     564             :     }
     565             : 
     566         743 :     if (oLineTypeDef.size())
     567             :     {
     568             :         // If the first element is a gap, rotate the elements so the first
     569             :         // element is a dash
     570         217 :         if (oLineTypeDef.front() < 0)
     571             :         {
     572           2 :             std::rotate(oLineTypeDef.begin(), oLineTypeDef.begin() + 1,
     573           2 :                         oLineTypeDef.end());
     574             :         }
     575             : 
     576         217 :         oLineTypeTable[osLineTypeName] = std::move(oLineTypeDef);
     577             :     }
     578             : 
     579         743 :     if (nCode == 0)
     580         743 :         UnreadValue();
     581         743 :     return true;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                           LookupLineType()                           */
     586             : /************************************************************************/
     587             : 
     588        2733 : std::vector<double> OGRDXFDataSource::LookupLineType(const char *pszName)
     589             : 
     590             : {
     591        2733 :     if (pszName && oLineTypeTable.count(pszName) > 0)
     592          47 :         return oLineTypeTable[pszName];
     593             :     else
     594        2686 :         return std::vector<double>();  // empty, represents a continuous line
     595             : }
     596             : 
     597             : /************************************************************************/
     598             : /*                       ReadTextStyleDefinition()                      */
     599             : /************************************************************************/
     600             : 
     601         206 : bool OGRDXFDataSource::ReadTextStyleDefinition()
     602             : 
     603             : {
     604             :     char szLineBuf[257];
     605         206 :     int nCode = 0;
     606             : 
     607         412 :     CPLString osStyleHandle;
     608         412 :     CPLString osStyleName;
     609         206 :     bool bInsideAcadSection = false;
     610             : 
     611        2790 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     612             :     {
     613        2584 :         switch (nCode)
     614             :         {
     615         182 :             case 5:
     616         182 :                 osStyleHandle = szLineBuf;
     617         182 :                 break;
     618             : 
     619         206 :             case 2:
     620         412 :                 osStyleName = CPLString(szLineBuf)
     621         206 :                                   .Recode(GetEncoding(), CPL_ENC_UTF8)
     622         206 :                                   .toupper();
     623         206 :                 break;
     624             : 
     625         204 :             case 70:
     626             :                 // If the LSB is set, this is not a text style
     627         204 :                 if (atoi(szLineBuf) & 1)
     628           0 :                     return true;
     629         204 :                 break;
     630             : 
     631             :                 // Note: 40 and 41 group codes do not propagate from a text
     632             :                 // style down to TEXT objects. However, 41 does propagate down
     633             :                 // for MTEXT.
     634             : 
     635         206 :             case 41:
     636         206 :                 oTextStyleTable[osStyleName]["Width"] = szLineBuf;
     637         206 :                 break;
     638             : 
     639          11 :             case 1001:
     640          11 :                 bInsideAcadSection = EQUAL(szLineBuf, "ACAD");
     641          11 :                 break;
     642             : 
     643          11 :             case 1000:
     644          11 :                 if (bInsideAcadSection)
     645          11 :                     oTextStyleTable[osStyleName]["Font"] = szLineBuf;
     646          11 :                 break;
     647             : 
     648          11 :             case 1071:
     649             :                 // bold and italic are kept in this undocumented bitfield
     650          11 :                 if (bInsideAcadSection)
     651             :                 {
     652          11 :                     const int nFontFlags = atoi(szLineBuf);
     653          22 :                     oTextStyleTable[osStyleName]["Bold"] =
     654          33 :                         (nFontFlags & 0x2000000) ? "1" : "0";
     655          22 :                     oTextStyleTable[osStyleName]["Italic"] =
     656          33 :                         (nFontFlags & 0x1000000) ? "1" : "0";
     657             :                 }
     658          11 :                 break;
     659             : 
     660        1753 :             default:
     661        1753 :                 break;
     662             :         }
     663             :     }
     664         206 :     if (nCode < 0)
     665             :     {
     666           0 :         DXF_READER_ERROR();
     667           0 :         return false;
     668             :     }
     669             : 
     670         206 :     if (nCode == 0)
     671         206 :         UnreadValue();
     672             : 
     673         206 :     if (osStyleHandle != "")
     674         182 :         oTextStyleHandles[osStyleHandle] = std::move(osStyleName);
     675             : 
     676         206 :     return true;
     677             : }
     678             : 
     679             : /************************************************************************/
     680             : /*                           TextStyleExists()                          */
     681             : /************************************************************************/
     682             : 
     683           1 : bool OGRDXFDataSource::TextStyleExists(const char *pszTextStyle)
     684             : 
     685             : {
     686           1 :     if (!pszTextStyle)
     687           0 :         return false;
     688             : 
     689           1 :     CPLString osTextStyleUpper = pszTextStyle;
     690           1 :     osTextStyleUpper.toupper();
     691             : 
     692           1 :     return oTextStyleTable.count(osTextStyleUpper) > 0;
     693             : }
     694             : 
     695             : /************************************************************************/
     696             : /*                       LookupTextStyleProperty()                      */
     697             : /************************************************************************/
     698             : 
     699         395 : const char *OGRDXFDataSource::LookupTextStyleProperty(const char *pszTextStyle,
     700             :                                                       const char *pszProperty,
     701             :                                                       const char *pszDefault)
     702             : 
     703             : {
     704         395 :     if (!pszTextStyle)
     705           0 :         return pszDefault;
     706             : 
     707         790 :     CPLString osTextStyleUpper = pszTextStyle;
     708         395 :     osTextStyleUpper.toupper();
     709             : 
     710         653 :     if (pszProperty && oTextStyleTable.count(osTextStyleUpper) > 0 &&
     711         653 :         oTextStyleTable[osTextStyleUpper].count(pszProperty) > 0)
     712             :     {
     713          54 :         return (oTextStyleTable[osTextStyleUpper])[pszProperty];
     714             :     }
     715             :     else
     716             :     {
     717         341 :         return pszDefault;
     718             :     }
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                      GetTextStyleNameByHandle()                      */
     723             : /*                                                                      */
     724             : /*      Find the name of the text style with the given STYLE table      */
     725             : /*      handle. If there is no such style, an empty string is returned. */
     726             : /************************************************************************/
     727             : 
     728          10 : CPLString OGRDXFDataSource::GetTextStyleNameByHandle(const char *pszID)
     729             : 
     730             : {
     731          20 :     CPLString l_osID = pszID;
     732             : 
     733          10 :     if (oTextStyleHandles.count(l_osID) == 0)
     734           6 :         return "";
     735             :     else
     736           4 :         return oTextStyleHandles[l_osID];
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                  PopulateDefaultDimStyleProperties()                 */
     741             : /************************************************************************/
     742             : 
     743         250 : void OGRDXFDataSource::PopulateDefaultDimStyleProperties(
     744             :     std::map<CPLString, CPLString> &oDimStyleProperties)
     745             : 
     746             : {
     747         250 :     const int *piCode = ACGetKnownDimStyleCodes();
     748        3000 :     do
     749             :     {
     750        3250 :         const char *pszProperty = ACGetDimStylePropertyName(*piCode);
     751        6500 :         oDimStyleProperties[pszProperty] =
     752        9750 :             ACGetDimStylePropertyDefault(*piCode);
     753        3250 :     } while (*(++piCode));
     754         250 : }
     755             : 
     756             : /************************************************************************/
     757             : /*                       ReadDimStyleDefinition()                       */
     758             : /************************************************************************/
     759             : 
     760         190 : bool OGRDXFDataSource::ReadDimStyleDefinition()
     761             : 
     762             : {
     763             :     char szLineBuf[257];
     764         190 :     int nCode = 0;
     765         380 :     std::map<CPLString, CPLString> oDimStyleProperties;
     766         380 :     CPLString osDimStyleName = "";
     767             : 
     768         190 :     PopulateDefaultDimStyleProperties(oDimStyleProperties);
     769             : 
     770        2425 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     771             :     {
     772        2235 :         switch (nCode)
     773             :         {
     774         190 :             case 2:
     775             :                 osDimStyleName =
     776         190 :                     CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
     777         190 :                 break;
     778             : 
     779        2045 :             default:
     780        2045 :                 const char *pszProperty = ACGetDimStylePropertyName(nCode);
     781        2045 :                 if (pszProperty)
     782         277 :                     oDimStyleProperties[pszProperty] = szLineBuf;
     783        2045 :                 break;
     784             :         }
     785             :     }
     786         190 :     if (nCode < 0)
     787             :     {
     788           0 :         DXF_READER_ERROR();
     789           0 :         return false;
     790             :     }
     791             : 
     792         190 :     if (!oDimStyleProperties.empty())
     793         190 :         oDimStyleTable[osDimStyleName] = std::move(oDimStyleProperties);
     794             : 
     795         190 :     if (nCode == 0)
     796         190 :         UnreadValue();
     797         190 :     return true;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                           LookupDimStyle()                           */
     802             : /*                                                                      */
     803             : /*      If the specified DIMSTYLE does not exist, a default set of      */
     804             : /*      of style properties are copied into oDimStyleProperties and     */
     805             : /*      false is returned.  Otherwise true is returned.                 */
     806             : /************************************************************************/
     807             : 
     808          42 : bool OGRDXFDataSource::LookupDimStyle(
     809             :     const char *pszDimStyle,
     810             :     std::map<CPLString, CPLString> &oDimStyleProperties)
     811             : 
     812             : {
     813          42 :     if (pszDimStyle == nullptr || !oDimStyleTable.count(pszDimStyle))
     814             :     {
     815          16 :         PopulateDefaultDimStyleProperties(oDimStyleProperties);
     816          16 :         return false;
     817             :     }
     818             : 
     819             :     // make a copy of the DIMSTYLE properties, so no-one can mess around
     820             :     // with our original copy
     821          26 :     oDimStyleProperties = oDimStyleTable[pszDimStyle];
     822          26 :     return true;
     823             : }
     824             : 
     825             : /************************************************************************/
     826             : /*                         ReadHeaderSection()                          */
     827             : /************************************************************************/
     828             : 
     829         189 : bool OGRDXFDataSource::ReadHeaderSection()
     830             : 
     831             : {
     832             :     char szLineBuf[257];
     833         189 :     int nCode = 0;
     834             : 
     835       56212 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
     836       28106 :            !EQUAL(szLineBuf, "ENDSEC"))
     837             :     {
     838       27917 :         if (nCode != 9)
     839        7666 :             continue;
     840             : 
     841       20251 :         CPLString l_osName = szLineBuf;
     842             : 
     843       20251 :         if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
     844             :         {
     845           0 :             DXF_READER_ERROR();
     846           0 :             return false;
     847             :         }
     848             : 
     849       20251 :         oHeaderVariables[l_osName] = szLineBuf;
     850       20251 :         GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
     851             :                                      "DXF_HEADER_VARIABLES");
     852             :     }
     853         189 :     if (nCode < 0)
     854             :     {
     855           0 :         DXF_READER_ERROR();
     856           0 :         return false;
     857             :     }
     858             : 
     859         189 :     nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
     860         189 :     if (nCode < 0)
     861             :     {
     862           0 :         DXF_READER_ERROR();
     863           0 :         return false;
     864             :     }
     865         189 :     UnreadValue();
     866             : 
     867             :     /* Unusual DXF files produced by dxflib */
     868             :     /* such as http://www.ribbonsoft.com/library/architecture/plants/decd5.dxf
     869             :      */
     870             :     /* where there is a spurious ENDSEC in the middle of the header variables */
     871         189 :     if (nCode == 9 && STARTS_WITH_CI(szLineBuf, "$"))
     872             :     {
     873           0 :         while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
     874           0 :                !EQUAL(szLineBuf, "ENDSEC"))
     875             :         {
     876           0 :             if (nCode != 9)
     877           0 :                 continue;
     878             : 
     879           0 :             CPLString l_osName = szLineBuf;
     880             : 
     881           0 :             if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
     882             :             {
     883           0 :                 DXF_READER_ERROR();
     884           0 :                 return false;
     885             :             }
     886             : 
     887           0 :             oHeaderVariables[l_osName] = szLineBuf;
     888           0 :             GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
     889             :                                          "DXF_HEADER_VARIABLES");
     890             :         }
     891           0 :         if (nCode < 0)
     892             :         {
     893           0 :             DXF_READER_ERROR();
     894           0 :             return false;
     895             :         }
     896             :     }
     897             : 
     898         189 :     CPLDebug("DXF", "Read %d header variables.", (int)oHeaderVariables.size());
     899             : 
     900             :     /* -------------------------------------------------------------------- */
     901             :     /*      Decide on what CPLRecode() name to use for the files            */
     902             :     /*      encoding or allow the encoding to be overridden.                */
     903             :     /* -------------------------------------------------------------------- */
     904         189 :     CPLString osCodepage = GetVariable("$DWGCODEPAGE", "ANSI_1252");
     905             : 
     906             :     // not strictly accurate but works even without iconv.
     907         189 :     if (osCodepage == "ANSI_1252")
     908         185 :         osEncoding = CPL_ENC_ISO8859_1;
     909           4 :     else if (STARTS_WITH_CI(osCodepage, "ANSI_"))
     910             :     {
     911           4 :         osEncoding = "CP";
     912           4 :         osEncoding += osCodepage + 5;
     913             :     }
     914             :     else
     915             :     {
     916             :         // fallback to the default
     917           0 :         osEncoding = CPL_ENC_ISO8859_1;
     918             :     }
     919             : 
     920         189 :     const char *pszEncoding = CPLGetConfigOption("DXF_ENCODING", nullptr);
     921         189 :     if (pszEncoding != nullptr)
     922           0 :         osEncoding = pszEncoding;
     923             : 
     924         189 :     if (osEncoding != CPL_ENC_ISO8859_1)
     925           4 :         CPLDebug("DXF", "Treating DXF as encoding '%s', $DWGCODEPAGE='%s'",
     926             :                  osEncoding.c_str(), osCodepage.c_str());
     927         189 :     return true;
     928             : }
     929             : 
     930             : /************************************************************************/
     931             : /*                            GetVariable()                             */
     932             : /*                                                                      */
     933             : /*      Fetch a variable that came from the HEADER section.             */
     934             : /************************************************************************/
     935             : 
     936        2915 : const char *OGRDXFDataSource::GetVariable(const char *pszName,
     937             :                                           const char *pszDefault)
     938             : 
     939             : {
     940        2915 :     if (oHeaderVariables.count(pszName) == 0)
     941        1117 :         return pszDefault;
     942             :     else
     943        1798 :         return oHeaderVariables[pszName];
     944             : }
     945             : 
     946             : /************************************************************************/
     947             : /*                         AddStandardFields()                          */
     948             : /************************************************************************/
     949             : 
     950         304 : void OGRDXFDataSource::AddStandardFields(OGRFeatureDefn *poFeatureDefn,
     951             :                                          const int nFieldModes)
     952             : {
     953         608 :     OGRFieldDefn oLayerField("Layer", OFTString);
     954         304 :     poFeatureDefn->AddFieldDefn(&oLayerField);
     955             : 
     956         608 :     OGRFieldDefn oPaperSpaceField("PaperSpace", OFTInteger);
     957         304 :     oPaperSpaceField.SetSubType(OFSTBoolean);
     958         304 :     poFeatureDefn->AddFieldDefn(&oPaperSpaceField);
     959             : 
     960         608 :     OGRFieldDefn oClassField("SubClasses", OFTString);
     961         304 :     poFeatureDefn->AddFieldDefn(&oClassField);
     962             : 
     963         304 :     if (nFieldModes & ODFM_IncludeRawCodeValues)
     964             :     {
     965           2 :         OGRFieldDefn oRawCodeField("RawCodeValues", OFTStringList);
     966           1 :         poFeatureDefn->AddFieldDefn(&oRawCodeField);
     967             :     }
     968             : 
     969         608 :     OGRFieldDefn oLinetypeField("Linetype", OFTString);
     970         304 :     poFeatureDefn->AddFieldDefn(&oLinetypeField);
     971             : 
     972         608 :     OGRFieldDefn oEntityHandleField("EntityHandle", OFTString);
     973         304 :     poFeatureDefn->AddFieldDefn(&oEntityHandleField);
     974             : 
     975         608 :     OGRFieldDefn oTextField("Text", OFTString);
     976         304 :     poFeatureDefn->AddFieldDefn(&oTextField);
     977             : 
     978         304 :     if (nFieldModes & ODFM_Include3DModeFields)
     979             :     {
     980           2 :         OGRFieldDefn oASMBinaryField("ASMData", OFTBinary);
     981           1 :         poFeatureDefn->AddFieldDefn(&oASMBinaryField);
     982             : 
     983           2 :         OGRFieldDefn oASMTransformField("ASMTransform", OFTRealList);
     984           1 :         poFeatureDefn->AddFieldDefn(&oASMTransformField);
     985             :     }
     986             : 
     987         304 :     if (nFieldModes & ODFM_IncludeBlockFields)
     988             :     {
     989         184 :         OGRFieldDefn oBlockNameField("BlockName", OFTString);
     990          92 :         poFeatureDefn->AddFieldDefn(&oBlockNameField);
     991             : 
     992         184 :         OGRFieldDefn oScaleField("BlockScale", OFTRealList);
     993          92 :         poFeatureDefn->AddFieldDefn(&oScaleField);
     994             : 
     995         184 :         OGRFieldDefn oBlockAngleField("BlockAngle", OFTReal);
     996          92 :         poFeatureDefn->AddFieldDefn(&oBlockAngleField);
     997             : 
     998         184 :         OGRFieldDefn oBlockOCSNormalField("BlockOCSNormal", OFTRealList);
     999          92 :         poFeatureDefn->AddFieldDefn(&oBlockOCSNormalField);
    1000             : 
    1001         184 :         OGRFieldDefn oBlockOCSCoordsField("BlockOCSCoords", OFTRealList);
    1002          92 :         poFeatureDefn->AddFieldDefn(&oBlockOCSCoordsField);
    1003             : 
    1004         184 :         OGRFieldDefn oBlockAttribsField("BlockAttributes", OFTStringList);
    1005          92 :         poFeatureDefn->AddFieldDefn(&oBlockAttribsField);
    1006             : 
    1007             :         // This field holds the name of the block on which the entity lies.
    1008             :         // The BlockName field was previously used for this purpose; this
    1009             :         // was changed because of the ambiguity with the BlockName field
    1010             :         // used by INSERT entities.
    1011         184 :         OGRFieldDefn oBlockField("Block", OFTString);
    1012          92 :         poFeatureDefn->AddFieldDefn(&oBlockField);
    1013             : 
    1014             :         // Extra field to use with ATTDEF entities
    1015         184 :         OGRFieldDefn oAttributeTagField("AttributeTag", OFTString);
    1016          92 :         poFeatureDefn->AddFieldDefn(&oAttributeTagField);
    1017             :     }
    1018         304 : }
    1019             : 
    1020             : /************************************************************************/
    1021             : /*                    GetEntryFromAcDsDataSection()                     */
    1022             : /************************************************************************/
    1023             : 
    1024             : size_t
    1025           2 : OGRDXFDataSource::GetEntryFromAcDsDataSection(const char *pszEntityHandle,
    1026             :                                               const GByte **pabyBuffer)
    1027             : 
    1028             : {
    1029           2 :     if (!pszEntityHandle || !pabyBuffer)
    1030           0 :         return 0;
    1031             : 
    1032           2 :     if (bHaveReadSolidData)
    1033             :     {
    1034           1 :         if (oSolidBinaryData.count(pszEntityHandle) > 0)
    1035             :         {
    1036           1 :             *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
    1037           1 :             return oSolidBinaryData[pszEntityHandle].size();
    1038             :         }
    1039           0 :         return 0;
    1040             :     }
    1041             : 
    1042             :     // Keep track of our current position and line number in the file so we can
    1043             :     // return here later
    1044           1 :     uint64_t iPrevOffset = poReader->GetCurrentFilePos();
    1045           1 :     int nPrevLineNumber = poReader->nLineNumber;
    1046             : 
    1047             :     char szLineBuf[4096];
    1048           1 :     int nCode = 0;
    1049           1 :     bool bFound = false;
    1050             : 
    1051             :     // Search for the ACDSDATA section
    1052         313 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
    1053             :     {
    1054             :         // Check whether the ACDSDATA section starts here
    1055         313 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
    1056             :         {
    1057           3 :             if ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) < 0)
    1058             :             {
    1059           0 :                 break;
    1060             :             }
    1061             : 
    1062           3 :             if (nCode == 2 && EQUAL(szLineBuf, "ACDSDATA"))
    1063             :             {
    1064           1 :                 bFound = true;
    1065           1 :                 break;
    1066             :             }
    1067             :         }
    1068             :     }
    1069             : 
    1070           1 :     if (!bFound)
    1071             :     {
    1072           0 :         poReader->ResetReadPointer(iPrevOffset, nPrevLineNumber);
    1073           0 :         return 0;
    1074             :     }
    1075             : 
    1076           1 :     bool bInAcDsRecord = false;
    1077           1 :     bool bGotAsmData = false;
    1078           2 :     CPLString osThisHandle;
    1079             : 
    1080             :     // Search for the relevant ACDSRECORD and extract its binary data
    1081         129 :     while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
    1082             :     {
    1083         128 :         if (nCode == 0 && EQUAL(szLineBuf, "ENDSEC"))
    1084             :         {
    1085             :             // We've reached the end of the ACDSDATA section
    1086             :             break;
    1087             :         }
    1088         128 :         else if (nCode == 0)
    1089             :         {
    1090           9 :             bInAcDsRecord = EQUAL(szLineBuf, "ACDSRECORD");
    1091           9 :             bGotAsmData = false;
    1092           9 :             osThisHandle.clear();
    1093             :         }
    1094         119 :         else if (bInAcDsRecord && nCode == 320)
    1095             :         {
    1096           3 :             osThisHandle = szLineBuf;
    1097             :         }
    1098         116 :         else if (bInAcDsRecord && nCode == 2)
    1099             :         {
    1100           6 :             bGotAsmData = EQUAL(szLineBuf, "ASM_Data");
    1101             :         }
    1102         110 :         else if (bInAcDsRecord && bGotAsmData && nCode == 94)
    1103             :         {
    1104             :             // Group code 94 gives the length of the binary data that follows
    1105           2 :             int nLen = atoi(szLineBuf);
    1106             : 
    1107             :             // Enforce some limits (the upper limit is arbitrary)
    1108           2 :             if (nLen <= 0 || nLen > 1048576)
    1109             :             {
    1110           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1111             :                          "ACDSRECORD data for entity %s is too long (more than "
    1112             :                          "1MB in size) and was skipped.",
    1113             :                          pszEntityHandle);
    1114           0 :                 continue;
    1115             :             }
    1116             : 
    1117           2 :             oSolidBinaryData[osThisHandle].resize(nLen);
    1118             : 
    1119             :             // Read the binary data into the buffer
    1120           2 :             int nPos = 0;
    1121          14 :             while (ReadValue(szLineBuf, sizeof(szLineBuf)) == 310)
    1122             :             {
    1123             :                 int nBytesRead;
    1124             :                 std::unique_ptr<GByte, VSIFreeReleaser> pabyHex(
    1125          12 :                     CPLHexToBinary(szLineBuf, &nBytesRead));
    1126             : 
    1127          12 :                 if (nPos + nBytesRead > nLen)
    1128             :                 {
    1129           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1130             :                              "Too many bytes in ACDSRECORD data for entity %s. "
    1131             :                              "Is the length (group code 94) correct?",
    1132             :                              pszEntityHandle);
    1133           0 :                     break;
    1134             :                 }
    1135             :                 else
    1136             :                 {
    1137             :                     std::copy_n(pabyHex.get(), nBytesRead,
    1138          12 :                                 oSolidBinaryData[osThisHandle].begin() + nPos);
    1139          12 :                     nPos += nBytesRead;
    1140             :                 }
    1141             :             }
    1142             :         }
    1143             :     }
    1144             : 
    1145           1 :     poReader->ResetReadPointer(iPrevOffset, nPrevLineNumber);
    1146             : 
    1147           1 :     bHaveReadSolidData = true;
    1148             : 
    1149           1 :     if (oSolidBinaryData.count(pszEntityHandle) > 0)
    1150             :     {
    1151           1 :         *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
    1152           1 :         return oSolidBinaryData[pszEntityHandle].size();
    1153             :     }
    1154           0 :     return 0;
    1155             : }

Generated by: LCOV version 1.14