LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfwriterds.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 407 482 84.4 %
Date: 2025-01-18 02:53:07 Functions: 21 23 91.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements OGRDXFWriterDS - the OGRDataSource class used for
       5             :  *           writing a DXF file.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : 
      17             : #include <cmath>
      18             : #include <cstdlib>
      19             : #include <limits>
      20             : 
      21             : #include "ogr_dxf.h"
      22             : #include "cpl_conv.h"
      23             : #include "cpl_string.h"
      24             : #include "cpl_vsi_error.h"
      25             : 
      26             : #ifdef EMBED_RESOURCE_FILES
      27             : #include "embedded_resources.h"
      28             : #endif
      29             : 
      30             : /************************************************************************/
      31             : /*                          OGRDXFWriterDS()                          */
      32             : /************************************************************************/
      33             : 
      34          57 : OGRDXFWriterDS::OGRDXFWriterDS()
      35             :     : nNextFID(80), poLayer(nullptr), poBlocksLayer(nullptr), fp(nullptr),
      36          57 :       fpTemp(nullptr), papszLayersToCreate(nullptr), nHANDSEEDOffset(0)
      37             : {
      38          57 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                         ~OGRDXFWriterDS()                          */
      42             : /************************************************************************/
      43             : 
      44         114 : OGRDXFWriterDS::~OGRDXFWriterDS()
      45             : 
      46             : {
      47          57 :     if (fp != nullptr)
      48             :     {
      49             :         /* --------------------------------------------------------------------
      50             :          */
      51             :         /*      Transfer over the header into the destination file with any */
      52             :         /*      adjustments or insertions needed. */
      53             :         /* --------------------------------------------------------------------
      54             :          */
      55          56 :         CPLDebug("DXF", "Compose final DXF file from components.");
      56             : 
      57          56 :         if (IsMarkedSuppressOnClose() && fpTemp != nullptr)
      58             :         {
      59           1 :             CPLDebug("DXF", "Do not copy final DXF when 'suppress on close'.");
      60           1 :             VSIFCloseL(fpTemp);
      61           1 :             VSIUnlink(osTempFilename);
      62           1 :             fpTemp = nullptr;
      63             :         }
      64             : 
      65          56 :         TransferUpdateHeader(fp);
      66             : 
      67          56 :         if (fpTemp != nullptr)
      68             :         {
      69             :             /* --------------------------------------------------------------------
      70             :              */
      71             :             /*      Copy in the temporary file contents. */
      72             :             /* --------------------------------------------------------------------
      73             :              */
      74          55 :             VSIFCloseL(fpTemp);
      75          55 :             fpTemp = VSIFOpenL(osTempFilename, "r");
      76             : 
      77          55 :             const char *pszLine = nullptr;
      78        2071 :             while ((pszLine = CPLReadLineL(fpTemp)) != nullptr)
      79             :             {
      80        2016 :                 VSIFWriteL(pszLine, 1, strlen(pszLine), fp);
      81        2016 :                 VSIFWriteL("\n", 1, 1, fp);
      82             :             }
      83             : 
      84             :             /* --------------------------------------------------------------------
      85             :              */
      86             :             /*      Cleanup temporary file. */
      87             :             /* --------------------------------------------------------------------
      88             :              */
      89          55 :             VSIFCloseL(fpTemp);
      90          55 :             VSIUnlink(osTempFilename);
      91             :         }
      92             : 
      93             :         /* --------------------------------------------------------------------
      94             :          */
      95             :         /*      Write trailer. */
      96             :         /* --------------------------------------------------------------------
      97             :          */
      98          56 :         if (osTrailerFile != "")
      99          56 :             TransferUpdateTrailer(fp);
     100             : 
     101             :         /* --------------------------------------------------------------------
     102             :          */
     103             :         /*      Fixup the HANDSEED value now that we know all the entity ids */
     104             :         /*      created. */
     105             :         /* --------------------------------------------------------------------
     106             :          */
     107          56 :         FixupHANDSEED(fp);
     108             : 
     109             :         /* --------------------------------------------------------------------
     110             :          */
     111             :         /*      Close file. */
     112             :         /* --------------------------------------------------------------------
     113             :          */
     114             : 
     115          56 :         VSIFCloseL(fp);
     116          56 :         fp = nullptr;
     117             :     }
     118             : 
     119             :     /* -------------------------------------------------------------------- */
     120             :     /*      Destroy layers.                                                 */
     121             :     /* -------------------------------------------------------------------- */
     122          57 :     delete poLayer;
     123          57 :     delete poBlocksLayer;
     124             : 
     125          57 :     CSLDestroy(papszLayersToCreate);
     126             : 
     127          57 :     if (m_bHeaderFileIsTemp)
     128           0 :         VSIUnlink(osHeaderFile.c_str());
     129          57 :     if (m_bTrailerFileIsTemp)
     130           0 :         VSIUnlink(osTrailerFile.c_str());
     131         114 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                           TestCapability()                           */
     135             : /************************************************************************/
     136             : 
     137          48 : int OGRDXFWriterDS::TestCapability(const char *pszCap)
     138             : 
     139             : {
     140          48 :     if (EQUAL(pszCap, ODsCCreateLayer))
     141             :         // Unable to have more than one OGR entities layer in a DXF file, with
     142             :         // one options blocks layer.
     143          32 :         return poBlocksLayer == nullptr || poLayer == nullptr;
     144             :     else
     145          16 :         return FALSE;
     146             : }
     147             : 
     148             : /************************************************************************/
     149             : /*                              GetLayer()                              */
     150             : /************************************************************************/
     151             : 
     152           0 : OGRLayer *OGRDXFWriterDS::GetLayer(int iLayer)
     153             : 
     154             : {
     155           0 :     if (iLayer == 0)
     156           0 :         return poLayer;
     157             :     else
     158           0 :         return nullptr;
     159             : }
     160             : 
     161             : /************************************************************************/
     162             : /*                           GetLayerCount()                            */
     163             : /************************************************************************/
     164             : 
     165           0 : int OGRDXFWriterDS::GetLayerCount()
     166             : 
     167             : {
     168           0 :     if (poLayer)
     169           0 :         return 1;
     170             :     else
     171           0 :         return 0;
     172             : }
     173             : 
     174             : /************************************************************************/
     175             : /*                                Open()                                */
     176             : /************************************************************************/
     177             : 
     178          57 : int OGRDXFWriterDS::Open(const char *pszFilename, char **papszOptions)
     179             : 
     180             : {
     181             :     /* -------------------------------------------------------------------- */
     182             :     /*      Open the standard header, or a user provided header.            */
     183             :     /* -------------------------------------------------------------------- */
     184          57 :     if (CSLFetchNameValue(papszOptions, "HEADER") != nullptr)
     185           3 :         osHeaderFile = CSLFetchNameValue(papszOptions, "HEADER");
     186             :     else
     187             :     {
     188             : #ifdef EMBED_RESOURCE_FILES
     189             :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     190             : #endif
     191             : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
     192             :         const char *pszValue = nullptr;
     193             : #else
     194          54 :         const char *pszValue = CPLFindFile("gdal", "header.dxf");
     195          54 :         if (pszValue == nullptr)
     196             : #endif
     197             :         {
     198             : #ifdef EMBED_RESOURCE_FILES
     199             :             static const bool bOnce [[maybe_unused]] = []()
     200             :             {
     201             :                 CPLDebug("DXF", "Using embedded header.dxf");
     202             :                 return true;
     203             :             }();
     204             :             pszValue = VSIMemGenerateHiddenFilename("header.dxf");
     205             :             VSIFCloseL(VSIFileFromMemBuffer(
     206             :                 pszValue,
     207             :                 const_cast<GByte *>(
     208             :                     reinterpret_cast<const GByte *>(OGRDXFGetHEADER())),
     209             :                 static_cast<int>(strlen(OGRDXFGetHEADER())),
     210             :                 /* bTakeOwnership = */ false));
     211             :             m_bHeaderFileIsTemp = true;
     212             : #else
     213           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     214             :                      "Failed to find template header file header.dxf for "
     215             :                      "reading,\nis GDAL_DATA set properly?");
     216           0 :             return FALSE;
     217             : #endif
     218             :         }
     219          54 :         osHeaderFile = pszValue;
     220             :     }
     221             : 
     222             :     /* -------------------------------------------------------------------- */
     223             :     /*      Establish the name for our trailer file.                        */
     224             :     /* -------------------------------------------------------------------- */
     225          57 :     if (CSLFetchNameValue(papszOptions, "TRAILER") != nullptr)
     226           0 :         osTrailerFile = CSLFetchNameValue(papszOptions, "TRAILER");
     227             :     else
     228             :     {
     229             : #ifdef EMBED_RESOURCE_FILES
     230             :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     231             : #endif
     232             : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
     233             :         const char *pszValue = nullptr;
     234             : #else
     235          57 :         const char *pszValue = CPLFindFile("gdal", "trailer.dxf");
     236          57 :         if (pszValue != nullptr)
     237          57 :             osTrailerFile = pszValue;
     238             : #endif
     239             : #ifdef EMBED_RESOURCE_FILES
     240             :         if (!pszValue)
     241             :         {
     242             :             static const bool bOnce [[maybe_unused]] = []()
     243             :             {
     244             :                 CPLDebug("DXF", "Using embedded trailer.dxf");
     245             :                 return true;
     246             :             }();
     247             :             osTrailerFile = VSIMemGenerateHiddenFilename("trailer.dxf");
     248             :             m_bTrailerFileIsTemp = false;
     249             :             VSIFCloseL(VSIFileFromMemBuffer(
     250             :                 osTrailerFile.c_str(),
     251             :                 const_cast<GByte *>(
     252             :                     reinterpret_cast<const GByte *>(OGRDXFGetTRAILER())),
     253             :                 static_cast<int>(strlen(OGRDXFGetTRAILER())),
     254             :                 /* bTakeOwnership = */ false));
     255             :         }
     256             : #endif
     257             :     }
     258             : 
     259             : /* -------------------------------------------------------------------- */
     260             : /*      What entity id do we want to start with when writing?  Small    */
     261             : /*      values run a risk of overlapping some undetected entity in      */
     262             : /*      the header or trailer despite the prescanning we do.            */
     263             : /* -------------------------------------------------------------------- */
     264             : #ifdef DEBUG
     265          57 :     nNextFID = 80;
     266             : #else
     267             :     nNextFID = 131072;
     268             : #endif
     269             : 
     270          57 :     if (CSLFetchNameValue(papszOptions, "FIRST_ENTITY") != nullptr)
     271           1 :         nNextFID = atoi(CSLFetchNameValue(papszOptions, "FIRST_ENTITY"));
     272             : 
     273             :     m_osINSUNITS =
     274          57 :         CSLFetchNameValueDef(papszOptions, "INSUNITS", m_osINSUNITS.c_str());
     275             :     m_osMEASUREMENT = CSLFetchNameValueDef(papszOptions, "MEASUREMENT",
     276          57 :                                            m_osMEASUREMENT.c_str());
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      Prescan the header and trailer for entity codes.                */
     280             :     /* -------------------------------------------------------------------- */
     281          57 :     ScanForEntities(osHeaderFile, "HEADER");
     282          57 :     ScanForEntities(osTrailerFile, "TRAILER");
     283             : 
     284             :     /* -------------------------------------------------------------------- */
     285             :     /*      Attempt to read the template header file so we have a list      */
     286             :     /*      of layers, linestyles and blocks.                               */
     287             :     /* -------------------------------------------------------------------- */
     288          57 :     if (!oHeaderDS.Open(osHeaderFile, true, nullptr))
     289           0 :         return FALSE;
     290             : 
     291             :     /* -------------------------------------------------------------------- */
     292             :     /*      Create the output file.                                         */
     293             :     /* -------------------------------------------------------------------- */
     294          57 :     fp = VSIFOpenExL(pszFilename, "w+", true);
     295             : 
     296          57 :     if (fp == nullptr)
     297             :     {
     298           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     299             :                  "Failed to open '%s' for writing: %s", pszFilename,
     300             :                  VSIGetLastErrorMsg());
     301           1 :         return FALSE;
     302             :     }
     303             : 
     304             :     /* -------------------------------------------------------------------- */
     305             :     /*      Establish the temporary file.                                   */
     306             :     /* -------------------------------------------------------------------- */
     307          56 :     osTempFilename = pszFilename;
     308          56 :     osTempFilename += ".tmp";
     309             : 
     310          56 :     fpTemp = VSIFOpenL(osTempFilename, "w");
     311          56 :     if (fpTemp == nullptr)
     312             :     {
     313           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     314             :                  "Failed to open '%s' for writing.", osTempFilename.c_str());
     315           0 :         return FALSE;
     316             :     }
     317             : 
     318          56 :     return TRUE;
     319             : }
     320             : 
     321             : /************************************************************************/
     322             : /*                           ICreateLayer()                             */
     323             : /************************************************************************/
     324             : 
     325          66 : OGRLayer *OGRDXFWriterDS::ICreateLayer(const char *pszName,
     326             :                                        const OGRGeomFieldDefn *poGeomFieldDefn,
     327             :                                        CSLConstList /*papszOptions*/)
     328             : 
     329             : {
     330          66 :     if (poGeomFieldDefn)
     331             :     {
     332          63 :         const auto poSRS = poGeomFieldDefn->GetSpatialRef();
     333          63 :         if (poSRS)
     334           5 :             m_oSRS = *poSRS;
     335             :     }
     336          66 :     if (EQUAL(pszName, "blocks") && poBlocksLayer == nullptr)
     337             :     {
     338           2 :         poBlocksLayer = new OGRDXFBlocksWriterLayer(this);
     339           2 :         return poBlocksLayer;
     340             :     }
     341          64 :     else if (poLayer == nullptr)
     342             :     {
     343          48 :         poLayer = new OGRDXFWriterLayer(this, fpTemp);
     344          48 :         return poLayer;
     345             :     }
     346             :     else
     347             :     {
     348          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     349             :                  "Unable to have more than one OGR entities layer in a DXF "
     350             :                  "file, with one options blocks layer.");
     351          16 :         return nullptr;
     352             :     }
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                             WriteValue()                             */
     357             : /************************************************************************/
     358             : 
     359       44676 : static bool WriteValue(VSILFILE *fp, int nCode, const char *pszLine)
     360             : 
     361             : {
     362             :     char szLinePair[300];
     363             : 
     364       44676 :     snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine);
     365       44676 :     size_t nLen = strlen(szLinePair);
     366       44676 :     if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
     367             :     {
     368           0 :         CPLError(CE_Failure, CPLE_FileIO,
     369             :                  "Attempt to write line to DXF file failed, disk full?.");
     370           0 :         return false;
     371             :     }
     372             : 
     373       44676 :     return true;
     374             : }
     375             : 
     376             : /************************************************************************/
     377             : /*                             WriteValue()                             */
     378             : /************************************************************************/
     379             : 
     380         232 : static bool WriteValue(VSILFILE *fp, int nCode, double dfValue)
     381             : 
     382             : {
     383             :     char szLinePair[64];
     384             : 
     385         232 :     CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
     386         232 :     size_t nLen = strlen(szLinePair);
     387         232 :     if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
     388             :     {
     389           0 :         CPLError(CE_Failure, CPLE_FileIO,
     390             :                  "Attempt to write line to DXF file failed, disk full?.");
     391           0 :         return false;
     392             :     }
     393             : 
     394         232 :     return true;
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                        TransferUpdateHeader()                        */
     399             : /************************************************************************/
     400             : 
     401          56 : bool OGRDXFWriterDS::TransferUpdateHeader(VSILFILE *fpOut)
     402             : 
     403             : {
     404          56 :     oHeaderDS.ResetReadPointer(0);
     405             : 
     406             :     // We don't like non-finite extents. In this case, just write a generic
     407             :     // bounding box. Most CAD programs probably ignore this anyway.
     408          56 :     if (!std::isfinite(oGlobalEnvelope.MinX) ||
     409          26 :         !std::isfinite(oGlobalEnvelope.MinY) ||
     410         108 :         !std::isfinite(oGlobalEnvelope.MaxX) ||
     411          26 :         !std::isfinite(oGlobalEnvelope.MaxY))
     412             :     {
     413          30 :         oGlobalEnvelope.MinX = 0.0;
     414          30 :         oGlobalEnvelope.MinY = 0.0;
     415          30 :         oGlobalEnvelope.MaxX = 10.0;
     416          30 :         oGlobalEnvelope.MaxY = 10.0;
     417             :     }
     418             : 
     419             :     /* -------------------------------------------------------------------- */
     420             :     /*      Copy header, inserting in new objects as needed.                */
     421             :     /* -------------------------------------------------------------------- */
     422             :     char szLineBuf[257];
     423          56 :     int nCode = 0;
     424         112 :     CPLString osSection;
     425         112 :     CPLString osTable;
     426         112 :     CPLString osEntity;
     427             : 
     428       61536 :     while ((nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1 &&
     429       30740 :            osSection != "ENTITIES")
     430             :     {
     431       30740 :         if (nCode == 0 && EQUAL(szLineBuf, "ENDTAB"))
     432             :         {
     433             :             // If we are at the end of the LAYER TABLE consider inserting
     434             :             // missing definitions.
     435         504 :             if (osTable == "LAYER")
     436             :             {
     437          56 :                 if (!WriteNewLayerDefinitions(fp))
     438           0 :                     return false;
     439             :             }
     440             : 
     441             :             // If at the end of the BLOCK_RECORD TABLE consider inserting
     442             :             // missing definitions.
     443         504 :             if (osTable == "BLOCK_RECORD" && poBlocksLayer)
     444             :             {
     445           2 :                 if (!WriteNewBlockRecords(fp))
     446           0 :                     return false;
     447             :             }
     448             : 
     449             :             // If at the end of the LTYPE TABLE consider inserting
     450             :             // missing layer type definitions.
     451         504 :             if (osTable == "LTYPE")
     452             :             {
     453          56 :                 if (!WriteNewLineTypeRecords(fp))
     454           0 :                     return false;
     455             :             }
     456             : 
     457             :             // If at the end of the STYLE TABLE consider inserting
     458             :             // missing layer type definitions.
     459         504 :             if (osTable == "STYLE")
     460             :             {
     461          56 :                 if (!WriteNewTextStyleRecords(fp))
     462           0 :                     return false;
     463             :             }
     464             : 
     465         504 :             osTable = "";
     466             :         }
     467             : 
     468             :         // If we are at the end of the BLOCKS section, consider inserting
     469             :         // supplementary blocks.
     470       30796 :         if (nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf, "ENDSEC") &&
     471          56 :             poBlocksLayer != nullptr)
     472             :         {
     473           2 :             if (!WriteNewBlockDefinitions(fp))
     474           0 :                 return false;
     475             :         }
     476             : 
     477             :         // We need to keep track of where $HANDSEED is so that we can
     478             :         // come back and fix it up when we have generated all entity ids.
     479       30740 :         if (nCode == 9 && EQUAL(szLineBuf, "$HANDSEED"))
     480             :         {
     481          56 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     482           0 :                 return false;
     483             : 
     484          56 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     485             : 
     486             :             // ensure we have room to overwrite with a longer value.
     487         392 :             while (strlen(szLineBuf) < 8)
     488             :             {
     489         336 :                 memmove(szLineBuf + 1, szLineBuf, strlen(szLineBuf) + 1);
     490         336 :                 szLineBuf[0] = '0';
     491             :             }
     492             : 
     493          56 :             nHANDSEEDOffset = VSIFTellL(fpOut);
     494             :         }
     495             : 
     496             :         // Patch EXTMIN with minx and miny
     497       30740 :         if (nCode == 9 && EQUAL(szLineBuf, "$EXTMIN"))
     498             :         {
     499          56 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     500           0 :                 return false;
     501             : 
     502          56 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     503          56 :             if (nCode == 10)
     504             :             {
     505          56 :                 if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinX))
     506           0 :                     return false;
     507             : 
     508          56 :                 nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     509          56 :                 if (nCode == 20)
     510             :                 {
     511          56 :                     if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinY))
     512           0 :                         return false;
     513             : 
     514          56 :                     continue;
     515             :                 }
     516             :             }
     517             :         }
     518             : 
     519             :         // Patch EXTMAX with maxx and maxy
     520       30684 :         if (nCode == 9 && EQUAL(szLineBuf, "$EXTMAX"))
     521             :         {
     522          56 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     523           0 :                 return false;
     524             : 
     525          56 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     526          56 :             if (nCode == 10)
     527             :             {
     528          56 :                 if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxX))
     529           0 :                     return false;
     530             : 
     531          56 :                 nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     532          56 :                 if (nCode == 20)
     533             :                 {
     534          56 :                     if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxY))
     535           0 :                         return false;
     536             : 
     537          56 :                     continue;
     538             :                 }
     539             :             }
     540             :         }
     541             : 
     542             :         // Patch INSUNITS
     543       30684 :         if (nCode == 9 && EQUAL(szLineBuf, "$INSUNITS") &&
     544          56 :             m_osINSUNITS != "HEADER_VALUE")
     545             :         {
     546          56 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     547           0 :                 return false;
     548          56 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     549          56 :             if (nCode == 70)
     550             :             {
     551          56 :                 int nVal = -1;
     552          56 :                 if (m_osINSUNITS == "AUTO" && m_oSRS.IsProjected())
     553             :                 {
     554           4 :                     const char *pszUnits = nullptr;
     555           4 :                     const double dfUnits = m_oSRS.GetLinearUnits(&pszUnits);
     556           9 :                     const auto IsAlmostEqual = [](double x, double y)
     557           9 :                     { return std::fabs(x - y) <= 1e-10; };
     558           4 :                     if (IsAlmostEqual(dfUnits, 1)) /* METERS */
     559             :                     {
     560           1 :                         nVal = 6;
     561             :                     }
     562           3 :                     else if (IsAlmostEqual(dfUnits, CPLAtof(SRS_UL_FOOT_CONV)))
     563             :                     {
     564           1 :                         nVal = 2;
     565             :                     }
     566           2 :                     else if (IsAlmostEqual(dfUnits,
     567             :                                            CPLAtof(SRS_UL_US_FOOT_CONV)))
     568             :                     {
     569           1 :                         nVal = 21;
     570             :                     }
     571             :                     else
     572             :                     {
     573           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
     574             :                                  "Could not translate CRS unit %s to "
     575             :                                  "$INSUNIT. Using default value from "
     576             :                                  "template header file",
     577             :                                  pszUnits);
     578             :                     }
     579             :                 }
     580          52 :                 else if (m_osINSUNITS != "AUTO")
     581             :                 {
     582             :                     static const struct
     583             :                     {
     584             :                         const char *pszValue;
     585             :                         int nValue;
     586             :                     } INSUNITSMap[] = {
     587             :                         {"UNITLESS", 0},
     588             :                         {"INCHES", 1},
     589             :                         {"FEET", 2},
     590             :                         {"MILLIMETERS", 4},
     591             :                         {"CENTIMETERS", 5},
     592             :                         {"METERS", 6},
     593             :                         {"US_SURVEY_FEET", 21},
     594             :                     };
     595             : 
     596          21 :                     for (const auto &sTuple : INSUNITSMap)
     597             :                     {
     598          39 :                         if (m_osINSUNITS == sTuple.pszValue ||
     599          19 :                             m_osINSUNITS == CPLSPrintf("%d", sTuple.nValue))
     600             :                         {
     601           2 :                             nVal = sTuple.nValue;
     602           2 :                             break;
     603             :                         }
     604             :                     }
     605           3 :                     if (nVal < 0)
     606             :                     {
     607           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
     608             :                                  "Could not translate $INSUNITS=%s. "
     609             :                                  "Using default value from template header "
     610             :                                  "file",
     611             :                                  m_osINSUNITS.c_str());
     612             :                     }
     613             :                 }
     614             : 
     615          56 :                 if (nVal >= 0)
     616             :                 {
     617           5 :                     if (!WriteValue(fpOut, nCode, CPLSPrintf("%d", nVal)))
     618           0 :                         return false;
     619             : 
     620           5 :                     continue;
     621             :                 }
     622             :             }
     623             :         }
     624             : 
     625             :         // Patch MEASUREMENT
     626       30679 :         if (nCode == 9 && EQUAL(szLineBuf, "$MEASUREMENT") &&
     627          56 :             m_osMEASUREMENT != "HEADER_VALUE")
     628             :         {
     629           3 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     630           0 :                 return false;
     631           3 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     632           3 :             if (nCode == 70)
     633             :             {
     634           3 :                 int nVal = -1;
     635             : 
     636             :                 static const struct
     637             :                 {
     638             :                     const char *pszValue;
     639             :                     int nValue;
     640             :                 } MEASUREMENTMap[] = {
     641             :                     {"IMPERIAL", 0},
     642             :                     {"METRIC", 1},
     643             :                 };
     644             : 
     645           7 :                 for (const auto &sTuple : MEASUREMENTMap)
     646             :                 {
     647          11 :                     if (m_osMEASUREMENT == sTuple.pszValue ||
     648           5 :                         m_osMEASUREMENT == CPLSPrintf("%d", sTuple.nValue))
     649             :                     {
     650           2 :                         nVal = sTuple.nValue;
     651           2 :                         break;
     652             :                     }
     653             :                 }
     654           3 :                 if (nVal < 0)
     655             :                 {
     656           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     657             :                              "Could not translate $MEASUREMENT=%s. "
     658             :                              "Using default value from template header file",
     659             :                              m_osMEASUREMENT.c_str());
     660             :                 }
     661             :                 else
     662             :                 {
     663           2 :                     if (!WriteValue(fpOut, nCode, CPLSPrintf("%d", nVal)))
     664           0 :                         return false;
     665             : 
     666           2 :                     continue;
     667             :                 }
     668             :             }
     669             :         }
     670             : 
     671             :         // Copy over the source line.
     672       30621 :         if (!WriteValue(fpOut, nCode, szLineBuf))
     673           0 :             return false;
     674             : 
     675             :         // Track what entity we are in - that is the last "code 0" object.
     676       30621 :         if (nCode == 0)
     677        2391 :             osEntity = szLineBuf;
     678             : 
     679             :         // Track what section we are in.
     680       30621 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
     681             :         {
     682         280 :             nCode = oHeaderDS.ReadValue(szLineBuf);
     683         280 :             if (nCode == -1)
     684           0 :                 break;
     685             : 
     686         280 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     687           0 :                 return false;
     688             : 
     689         280 :             osSection = szLineBuf;
     690             :         }
     691             : 
     692             :         // Track what TABLE we are in.
     693       30621 :         if (nCode == 0 && EQUAL(szLineBuf, "TABLE"))
     694             :         {
     695         504 :             nCode = oHeaderDS.ReadValue(szLineBuf);
     696         504 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     697           0 :                 return false;
     698             : 
     699         504 :             osTable = szLineBuf;
     700             :         }
     701             : 
     702             :         // If we are starting the first layer, then capture
     703             :         // the layer contents while copying so we can duplicate
     704             :         // it for any new layer definitions.
     705       30677 :         if (nCode == 0 && EQUAL(szLineBuf, "LAYER") && osTable == "LAYER" &&
     706          56 :             aosDefaultLayerText.empty())
     707             :         {
     708         560 :             do
     709             :             {
     710         616 :                 anDefaultLayerCode.push_back(nCode);
     711         616 :                 aosDefaultLayerText.push_back(szLineBuf);
     712             : 
     713         616 :                 if (nCode != 0 && !WriteValue(fpOut, nCode, szLineBuf))
     714           0 :                     return false;
     715             : 
     716         616 :                 nCode = oHeaderDS.ReadValue(szLineBuf);
     717             : 
     718         616 :                 if (nCode == 2 && !EQUAL(szLineBuf, "0"))
     719             :                 {
     720           0 :                     anDefaultLayerCode.resize(0);
     721           0 :                     aosDefaultLayerText.resize(0);
     722           0 :                     break;
     723             :                 }
     724         616 :             } while (nCode != 0);
     725             : 
     726          56 :             oHeaderDS.UnreadValue();
     727             :         }
     728             :     }
     729             : 
     730          56 :     return true;
     731             : }
     732             : 
     733             : /************************************************************************/
     734             : /*                       TransferUpdateTrailer()                        */
     735             : /************************************************************************/
     736             : 
     737          56 : bool OGRDXFWriterDS::TransferUpdateTrailer(VSILFILE *fpOut)
     738             : {
     739             :     /* -------------------------------------------------------------------- */
     740             :     /*      Open the file and setup a reader.                               */
     741             :     /* -------------------------------------------------------------------- */
     742          56 :     VSILFILE *l_fp = VSIFOpenL(osTrailerFile, "r");
     743             : 
     744          56 :     if (l_fp == nullptr)
     745           0 :         return false;
     746             : 
     747         112 :     OGRDXFReader oReader;
     748          56 :     oReader.Initialize(l_fp);
     749             : 
     750             :     /* -------------------------------------------------------------------- */
     751             :     /*      Scan ahead to find the OBJECTS section.                         */
     752             :     /* -------------------------------------------------------------------- */
     753             :     char szLineBuf[257];
     754          56 :     int nCode = 0;
     755             : 
     756         112 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
     757             :     {
     758         112 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
     759             :         {
     760          56 :             nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
     761          56 :             if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
     762          56 :                 break;
     763             :         }
     764             :     }
     765             : 
     766          56 :     if (nCode == -1)
     767             :     {
     768           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     769             :                  "Failed to find OBJECTS section in trailer file '%s'.",
     770             :                  osTrailerFile.c_str());
     771           0 :         return false;
     772             :     }
     773             : 
     774             :     /* -------------------------------------------------------------------- */
     775             :     /*      Insert the "end of section" for ENTITIES, and the start of      */
     776             :     /*      the OBJECTS section.                                            */
     777             :     /* -------------------------------------------------------------------- */
     778          56 :     WriteValue(fpOut, 0, "ENDSEC");
     779          56 :     WriteValue(fpOut, 0, "SECTION");
     780          56 :     WriteValue(fpOut, 2, "OBJECTS");
     781             : 
     782             :     /* -------------------------------------------------------------------- */
     783             :     /*      Copy the remainder of the file.                                 */
     784             :     /* -------------------------------------------------------------------- */
     785          56 :     bool ret = true;
     786       12040 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
     787             :     {
     788       11984 :         if (!WriteValue(fpOut, nCode, szLineBuf))
     789             :         {
     790           0 :             ret = false;
     791           0 :             break;
     792             :         }
     793             :     }
     794             : 
     795          56 :     VSIFCloseL(l_fp);
     796             : 
     797          56 :     return ret;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                           FixupHANDSEED()                            */
     802             : /*                                                                      */
     803             : /*      Fixup the next entity id information in the $HANDSEED header    */
     804             : /*      variable.                                                       */
     805             : /************************************************************************/
     806             : 
     807          56 : bool OGRDXFWriterDS::FixupHANDSEED(VSILFILE *fpIn)
     808             : 
     809             : {
     810             :     /* -------------------------------------------------------------------- */
     811             :     /*      What is a good next handle seed?  Scan existing values.         */
     812             :     /* -------------------------------------------------------------------- */
     813          56 :     unsigned int nHighestHandle = 0;
     814          56 :     std::set<CPLString>::iterator it;
     815             : 
     816        2175 :     for (it = aosUsedEntities.begin(); it != aosUsedEntities.end(); ++it)
     817             :     {
     818        2119 :         unsigned int nHandle = 0;
     819        2119 :         if (sscanf((*it).c_str(), "%x", &nHandle) == 1)
     820             :         {
     821        2119 :             if (nHandle > nHighestHandle)
     822        1389 :                 nHighestHandle = nHandle;
     823             :         }
     824             :     }
     825             : 
     826             :     /* -------------------------------------------------------------------- */
     827             :     /*      Read the existing handseed value, replace it, and write back.   */
     828             :     /* -------------------------------------------------------------------- */
     829          56 :     if (nHANDSEEDOffset == 0)
     830           0 :         return false;
     831             : 
     832             :     char szWorkBuf[30];
     833          56 :     VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
     834          56 :     VSIFReadL(szWorkBuf, 1, sizeof(szWorkBuf), fpIn);
     835             : 
     836          56 :     int i = 0;
     837         224 :     while (szWorkBuf[i] != '\n')
     838         168 :         i++;
     839             : 
     840          56 :     i++;
     841          56 :     if (szWorkBuf[i] == '\r')
     842           0 :         i++;
     843             : 
     844          56 :     CPLString osNewValue;
     845             : 
     846          56 :     osNewValue.Printf("%08X", nHighestHandle + 1);
     847          56 :     strncpy(szWorkBuf + i, osNewValue.c_str(), osNewValue.size());
     848             : 
     849          56 :     VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
     850          56 :     VSIFWriteL(szWorkBuf, 1, sizeof(szWorkBuf), fp);
     851             : 
     852          56 :     return true;
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                      WriteNewLayerDefinitions()                      */
     857             : /************************************************************************/
     858             : 
     859          56 : bool OGRDXFWriterDS::WriteNewLayerDefinitions(VSILFILE *fpOut)
     860             : 
     861             : {
     862          56 :     const int nNewLayers = CSLCount(papszLayersToCreate);
     863             : 
     864          59 :     for (int iLayer = 0; iLayer < nNewLayers; iLayer++)
     865             :     {
     866           3 :         bool bIsDefPoints = false;
     867           3 :         bool bWrote290 = false;
     868          36 :         for (unsigned i = 0; i < aosDefaultLayerText.size(); i++)
     869             :         {
     870          33 :             if (anDefaultLayerCode[i] == 2)
     871             :             {
     872           3 :                 if (EQUAL(papszLayersToCreate[iLayer], "DEFPOINTS"))
     873           0 :                     bIsDefPoints = true;
     874             : 
     875           3 :                 if (!WriteValue(fpOut, 2, papszLayersToCreate[iLayer]))
     876           0 :                     return false;
     877             :             }
     878          30 :             else if (anDefaultLayerCode[i] == 5)
     879             :             {
     880             :                 unsigned int nIgnored;
     881           3 :                 if (!WriteEntityID(fpOut, nIgnored))
     882           0 :                     return false;
     883             :             }
     884             :             else
     885             :             {
     886          27 :                 if (anDefaultLayerCode[i] == 290)
     887           0 :                     bWrote290 = true;
     888             : 
     889          27 :                 if (!WriteValue(fpOut, anDefaultLayerCode[i],
     890          27 :                                 aosDefaultLayerText[i]))
     891           0 :                     return false;
     892             :             }
     893             :         }
     894           3 :         if (bIsDefPoints && !bWrote290)
     895             :         {
     896             :             // The Defpoints layer must be explicitly set to not plotted to
     897             :             // please Autocad. See https://trac.osgeo.org/gdal/ticket/7078
     898           0 :             if (!WriteValue(fpOut, 290, "0"))
     899           0 :                 return false;
     900             :         }
     901             :     }
     902             : 
     903          56 :     return true;
     904             : }
     905             : 
     906             : /************************************************************************/
     907             : /*                      WriteNewLineTypeRecords()                       */
     908             : /************************************************************************/
     909             : 
     910          56 : bool OGRDXFWriterDS::WriteNewLineTypeRecords(VSILFILE *fpIn)
     911             : 
     912             : {
     913          56 :     if (poLayer == nullptr)
     914           8 :         return true;
     915             : 
     916             :     const std::map<CPLString, std::vector<double>> &oNewLineTypes =
     917          48 :         poLayer->GetNewLineTypeMap();
     918             : 
     919          48 :     bool bRet = true;
     920          50 :     for (const auto &oPair : oNewLineTypes)
     921             :     {
     922           2 :         bRet &= WriteValue(fpIn, 0, "LTYPE");
     923             :         unsigned int nIgnored;
     924           2 :         bRet &= WriteEntityID(fpIn, nIgnored);
     925           2 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
     926           2 :         bRet &= WriteValue(fpIn, 100, "AcDbLinetypeTableRecord");
     927           2 :         bRet &= WriteValue(fpIn, 2, oPair.first);
     928           2 :         bRet &= WriteValue(fpIn, 70, "0");
     929           2 :         bRet &= WriteValue(fpIn, 3, "");
     930           2 :         bRet &= WriteValue(fpIn, 72, "65");
     931           2 :         bRet &= WriteValue(fpIn, 73, static_cast<int>(oPair.second.size()));
     932             : 
     933           2 :         double dfTotalLength = 0.0;
     934           6 :         for (const double &dfSegment : oPair.second)
     935           4 :             dfTotalLength += fabs(dfSegment);
     936           2 :         bRet &= WriteValue(fpIn, 40, dfTotalLength);
     937             : 
     938           6 :         for (const double &dfSegment : oPair.second)
     939             :         {
     940           4 :             bRet &= WriteValue(fpIn, 49, dfSegment);
     941           4 :             bRet &= WriteValue(fpIn, 74, "0");
     942             :         }
     943             :     }
     944             : 
     945          48 :     return bRet;
     946             : }
     947             : 
     948             : /************************************************************************/
     949             : /*                      WriteNewTextStyleRecords()                      */
     950             : /************************************************************************/
     951             : 
     952          56 : bool OGRDXFWriterDS::WriteNewTextStyleRecords(VSILFILE *fpIn)
     953             : 
     954             : {
     955          56 :     if (poLayer == nullptr)
     956           8 :         return true;
     957             : 
     958          48 :     auto &oNewTextStyles = poLayer->GetNewTextStyleMap();
     959             : 
     960          48 :     bool bRet = true;
     961          49 :     for (auto &oPair : oNewTextStyles)
     962             :     {
     963           1 :         bRet &= WriteValue(fpIn, 0, "STYLE");
     964             :         unsigned int nIgnored;
     965           1 :         bRet &= WriteEntityID(fpIn, nIgnored);
     966           1 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
     967           1 :         bRet &= WriteValue(fpIn, 100, "AcDbTextStyleTableRecord");
     968           1 :         bRet &= WriteValue(fpIn, 2, oPair.first);
     969           1 :         bRet &= WriteValue(fpIn, 70, "0");
     970           1 :         bRet &= WriteValue(fpIn, 40, "0.0");
     971             : 
     972           1 :         if (oPair.second.count("Width"))
     973           1 :             bRet &= WriteValue(fpIn, 41, oPair.second["Width"]);
     974             :         else
     975           0 :             bRet &= WriteValue(fpIn, 41, "1.0");
     976             : 
     977           1 :         bRet &= WriteValue(fpIn, 50, "0.0");
     978           1 :         bRet &= WriteValue(fpIn, 71, "0");
     979           1 :         bRet &= WriteValue(fpIn, 1001, "ACAD");
     980             : 
     981           1 :         if (oPair.second.count("Font"))
     982           1 :             bRet &= WriteValue(fpIn, 1000, oPair.second["Font"]);
     983             : 
     984           1 :         int nStyleValue = 0;
     985           1 :         if (oPair.second.count("Italic") && oPair.second["Italic"] == "1")
     986           0 :             nStyleValue |= 0x1000000;
     987           1 :         if (oPair.second.count("Bold") && oPair.second["Bold"] == "1")
     988           1 :             nStyleValue |= 0x2000000;
     989           1 :         bRet &= WriteValue(fpIn, 1071,
     990           2 :                            CPLString().Printf("%d", nStyleValue).c_str());
     991             :     }
     992             : 
     993          48 :     return bRet;
     994             : }
     995             : 
     996             : /************************************************************************/
     997             : /*                        WriteNewBlockRecords()                        */
     998             : /************************************************************************/
     999             : 
    1000           2 : bool OGRDXFWriterDS::WriteNewBlockRecords(VSILFILE *fpIn)
    1001             : 
    1002             : {
    1003           2 :     std::set<CPLString> aosAlreadyHandled;
    1004             : 
    1005             :     /* ==================================================================== */
    1006             :     /*      Loop over all block objects written via the blocks layer.       */
    1007             :     /* ==================================================================== */
    1008           2 :     bool bRet = true;
    1009           6 :     for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
    1010             :     {
    1011           4 :         OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
    1012             : 
    1013             :         /* --------------------------------------------------------------------
    1014             :          */
    1015             :         /*      Is this block already defined in the template header? */
    1016             :         /* --------------------------------------------------------------------
    1017             :          */
    1018           4 :         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
    1019             : 
    1020           4 :         if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
    1021           0 :             continue;
    1022             : 
    1023             :         /* --------------------------------------------------------------------
    1024             :          */
    1025             :         /*      Have we already written a BLOCK_RECORD for this block? */
    1026             :         /* --------------------------------------------------------------------
    1027             :          */
    1028           4 :         if (aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end())
    1029           0 :             continue;
    1030             : 
    1031           4 :         aosAlreadyHandled.insert(osBlockName);
    1032             : 
    1033             :         /* --------------------------------------------------------------------
    1034             :          */
    1035             :         /*      Write the block record. */
    1036             :         /* --------------------------------------------------------------------
    1037             :          */
    1038           4 :         bRet &= WriteValue(fpIn, 0, "BLOCK_RECORD");
    1039             :         unsigned int nIgnored;
    1040           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
    1041           4 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
    1042           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockTableRecord");
    1043           4 :         bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
    1044           4 :         bRet &= WriteValue(fpIn, 340, "0");
    1045             :     }
    1046             : 
    1047           4 :     return bRet;
    1048             : }
    1049             : 
    1050             : /************************************************************************/
    1051             : /*                      WriteNewBlockDefinitions()                      */
    1052             : /************************************************************************/
    1053             : 
    1054           2 : bool OGRDXFWriterDS::WriteNewBlockDefinitions(VSILFILE *fpIn)
    1055             : 
    1056             : {
    1057           2 :     if (poLayer == nullptr)
    1058           1 :         poLayer = new OGRDXFWriterLayer(this, fpTemp);
    1059           2 :     poLayer->ResetFP(fpIn);
    1060             : 
    1061             :     /* ==================================================================== */
    1062             :     /*      Loop over all block objects written via the blocks layer.       */
    1063             :     /* ==================================================================== */
    1064           2 :     bool bRet = true;
    1065           6 :     for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
    1066             :     {
    1067           4 :         OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
    1068             : 
    1069             :         /* --------------------------------------------------------------------
    1070             :          */
    1071             :         /*      Is this block already defined in the template header? */
    1072             :         /* --------------------------------------------------------------------
    1073             :          */
    1074           4 :         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
    1075             : 
    1076           4 :         if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
    1077           0 :             continue;
    1078             : 
    1079             :         /* --------------------------------------------------------------------
    1080             :          */
    1081             :         /*      Write the block definition preamble. */
    1082             :         /* --------------------------------------------------------------------
    1083             :          */
    1084           4 :         CPLDebug("DXF", "Writing BLOCK definition for '%s'.",
    1085             :                  poThisBlockFeat->GetFieldAsString("Block"));
    1086             : 
    1087           4 :         bRet &= WriteValue(fpIn, 0, "BLOCK");
    1088             :         unsigned int nIgnored;
    1089           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
    1090           4 :         bRet &= WriteValue(fpIn, 100, "AcDbEntity");
    1091           4 :         if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
    1092           0 :             bRet &=
    1093           0 :                 WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
    1094             :         else
    1095           4 :             bRet &= WriteValue(fpIn, 8, "0");
    1096           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockBegin");
    1097           4 :         bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
    1098           4 :         bRet &= WriteValue(fpIn, 70, "0");
    1099             : 
    1100             :         // Origin
    1101           4 :         bRet &= WriteValue(fpIn, 10, "0.0");
    1102           4 :         bRet &= WriteValue(fpIn, 20, "0.0");
    1103           4 :         bRet &= WriteValue(fpIn, 30, "0.0");
    1104             : 
    1105           4 :         bRet &= WriteValue(fpIn, 3, poThisBlockFeat->GetFieldAsString("Block"));
    1106           4 :         bRet &= WriteValue(fpIn, 1, "");
    1107             : 
    1108             :         /* --------------------------------------------------------------------
    1109             :          */
    1110             :         /*      Write out the feature entities. */
    1111             :         /* --------------------------------------------------------------------
    1112             :          */
    1113           4 :         if (poLayer->CreateFeature(poThisBlockFeat) != OGRERR_NONE)
    1114           0 :             return false;
    1115             : 
    1116             :         /* --------------------------------------------------------------------
    1117             :          */
    1118             :         /*      Write out following features if they are the same block. */
    1119             :         /* --------------------------------------------------------------------
    1120             :          */
    1121           6 :         while (iBlock < poBlocksLayer->apoBlocks.size() - 1 &&
    1122           2 :                EQUAL(poBlocksLayer->apoBlocks[iBlock + 1]->GetFieldAsString(
    1123             :                          "Block"),
    1124             :                      osBlockName))
    1125             :         {
    1126           0 :             iBlock++;
    1127             : 
    1128           0 :             if (poLayer->CreateFeature(poBlocksLayer->apoBlocks[iBlock]) !=
    1129             :                 OGRERR_NONE)
    1130           0 :                 return false;
    1131             :         }
    1132             : 
    1133             :         /* --------------------------------------------------------------------
    1134             :          */
    1135             :         /*      Write out the block definition postamble. */
    1136             :         /* --------------------------------------------------------------------
    1137             :          */
    1138           4 :         bRet &= WriteValue(fpIn, 0, "ENDBLK");
    1139           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
    1140           4 :         bRet &= WriteValue(fpIn, 100, "AcDbEntity");
    1141           4 :         if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
    1142           0 :             bRet &=
    1143           0 :                 WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
    1144             :         else
    1145           4 :             bRet &= WriteValue(fpIn, 8, "0");
    1146           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockEnd");
    1147             :     }
    1148             : 
    1149           2 :     return bRet;
    1150             : }
    1151             : 
    1152             : /************************************************************************/
    1153             : /*                          ScanForEntities()                           */
    1154             : /*                                                                      */
    1155             : /*      Scan the indicated file for entity ids ("5" records) and        */
    1156             : /*      build them up as a set so we can be careful to avoid            */
    1157             : /*      creating new entities with conflicting ids.                     */
    1158             : /************************************************************************/
    1159             : 
    1160         114 : void OGRDXFWriterDS::ScanForEntities(const char *pszFilename,
    1161             :                                      const char *pszTarget)
    1162             : 
    1163             : {
    1164             :     /* -------------------------------------------------------------------- */
    1165             :     /*      Open the file and setup a reader.                               */
    1166             :     /* -------------------------------------------------------------------- */
    1167         114 :     VSILFILE *l_fp = VSIFOpenL(pszFilename, "r");
    1168             : 
    1169         114 :     if (l_fp == nullptr)
    1170           0 :         return;
    1171             : 
    1172         228 :     OGRDXFReader oReader;
    1173         114 :     oReader.Initialize(l_fp);
    1174             : 
    1175             :     /* -------------------------------------------------------------------- */
    1176             :     /*      Add every code "5" line to our entities list.                   */
    1177             :     /* -------------------------------------------------------------------- */
    1178             :     char szLineBuf[257];
    1179         114 :     int nCode = 0;
    1180         114 :     const char *pszPortion = "HEADER";
    1181             : 
    1182       45126 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
    1183             :     {
    1184       45012 :         if ((nCode == 5 || nCode == 105) && EQUAL(pszTarget, pszPortion))
    1185             :         {
    1186        3948 :             CPLString osEntity(szLineBuf);
    1187             : 
    1188        1974 :             if (CheckEntityID(osEntity))
    1189           6 :                 CPLDebug("DXF", "Encountered entity '%s' multiple times.",
    1190             :                          osEntity.c_str());
    1191             :             else
    1192        1968 :                 aosUsedEntities.insert(osEntity);
    1193             :         }
    1194             : 
    1195       45012 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
    1196             :         {
    1197         342 :             nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
    1198         342 :             if (nCode == 2 && EQUAL(szLineBuf, "ENTITIES"))
    1199          57 :                 pszPortion = "BODY";
    1200         342 :             if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
    1201          57 :                 pszPortion = "TRAILER";
    1202             :         }
    1203             :     }
    1204             : 
    1205         114 :     VSIFCloseL(l_fp);
    1206             : }
    1207             : 
    1208             : /************************************************************************/
    1209             : /*                           CheckEntityID()                            */
    1210             : /*                                                                      */
    1211             : /*      Does the mentioned entity already exist?                        */
    1212             : /************************************************************************/
    1213             : 
    1214        2174 : bool OGRDXFWriterDS::CheckEntityID(const char *pszEntityID)
    1215             : 
    1216             : {
    1217        2174 :     std::set<CPLString>::iterator it;
    1218             : 
    1219        2174 :     it = aosUsedEntities.find(pszEntityID);
    1220        2174 :     return it != aosUsedEntities.end();
    1221             : }
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                           WriteEntityID()                            */
    1225             : /************************************************************************/
    1226             : 
    1227         185 : bool OGRDXFWriterDS::WriteEntityID(VSILFILE *fpIn, unsigned int &nAssignedFID,
    1228             :                                    GIntBig nPreferredFID)
    1229             : 
    1230             : {
    1231         370 :     CPLString osEntityID;
    1232             : 
    1233             :     // From https://github.com/OSGeo/gdal/issues/11299 it seems that 0 is an
    1234             :     // invalid handle value.
    1235         201 :     if (nPreferredFID > 0 &&
    1236             :         nPreferredFID <=
    1237          16 :             static_cast<GIntBig>(std::numeric_limits<unsigned int>::max()))
    1238             :     {
    1239             : 
    1240          16 :         osEntityID.Printf("%X", static_cast<unsigned int>(nPreferredFID));
    1241          16 :         if (!CheckEntityID(osEntityID))
    1242             :         {
    1243           1 :             aosUsedEntities.insert(osEntityID);
    1244           1 :             if (!WriteValue(fpIn, 5, osEntityID))
    1245           0 :                 return false;
    1246           1 :             nAssignedFID = static_cast<unsigned int>(nPreferredFID);
    1247           1 :             return true;
    1248             :         }
    1249             :     }
    1250             : 
    1251           0 :     do
    1252             :     {
    1253         184 :         osEntityID.Printf("%X", nNextFID++);
    1254         184 :     } while (CheckEntityID(osEntityID));
    1255             : 
    1256         184 :     aosUsedEntities.insert(osEntityID);
    1257         184 :     if (!WriteValue(fpIn, 5, osEntityID))
    1258           0 :         return false;
    1259             : 
    1260         184 :     nAssignedFID = nNextFID - 1;
    1261         184 :     return true;
    1262             : }
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                           UpdateExtent()                             */
    1266             : /************************************************************************/
    1267             : 
    1268         175 : void OGRDXFWriterDS::UpdateExtent(OGREnvelope *psEnvelope)
    1269             : {
    1270         175 :     oGlobalEnvelope.Merge(*psEnvelope);
    1271         175 : }

Generated by: LCOV version 1.14