LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfwriterds.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 350 423 82.7 %
Date: 2024-05-03 15:49:35 Functions: 20 22 90.9 %

          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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : 
      33             : #include <cstdlib>
      34             : 
      35             : #include "ogr_dxf.h"
      36             : #include "cpl_conv.h"
      37             : #include "cpl_string.h"
      38             : #include "cpl_vsi_error.h"
      39             : 
      40             : /************************************************************************/
      41             : /*                          OGRDXFWriterDS()                          */
      42             : /************************************************************************/
      43             : 
      44          45 : OGRDXFWriterDS::OGRDXFWriterDS()
      45             :     : nNextFID(80), poLayer(nullptr), poBlocksLayer(nullptr), fp(nullptr),
      46          45 :       fpTemp(nullptr), papszLayersToCreate(nullptr), nHANDSEEDOffset(0)
      47             : {
      48          45 : }
      49             : 
      50             : /************************************************************************/
      51             : /*                         ~OGRDXFWriterDS()                          */
      52             : /************************************************************************/
      53             : 
      54          90 : OGRDXFWriterDS::~OGRDXFWriterDS()
      55             : 
      56             : {
      57          45 :     if (fp != nullptr)
      58             :     {
      59             :         /* --------------------------------------------------------------------
      60             :          */
      61             :         /*      Transfer over the header into the destination file with any */
      62             :         /*      adjustments or insertions needed. */
      63             :         /* --------------------------------------------------------------------
      64             :          */
      65          44 :         CPLDebug("DXF", "Compose final DXF file from components.");
      66             : 
      67          44 :         if (IsMarkedSuppressOnClose() && fpTemp != nullptr)
      68             :         {
      69           1 :             CPLDebug("DXF", "Do not copy final DXF when 'suppress on close'.");
      70           1 :             VSIFCloseL(fpTemp);
      71           1 :             VSIUnlink(osTempFilename);
      72           1 :             fpTemp = nullptr;
      73             :         }
      74             : 
      75          44 :         TransferUpdateHeader(fp);
      76             : 
      77          44 :         if (fpTemp != nullptr)
      78             :         {
      79             :             /* --------------------------------------------------------------------
      80             :              */
      81             :             /*      Copy in the temporary file contents. */
      82             :             /* --------------------------------------------------------------------
      83             :              */
      84          43 :             VSIFCloseL(fpTemp);
      85          43 :             fpTemp = VSIFOpenL(osTempFilename, "r");
      86             : 
      87          43 :             const char *pszLine = nullptr;
      88        2059 :             while ((pszLine = CPLReadLineL(fpTemp)) != nullptr)
      89             :             {
      90        2016 :                 VSIFWriteL(pszLine, 1, strlen(pszLine), fp);
      91        2016 :                 VSIFWriteL("\n", 1, 1, fp);
      92             :             }
      93             : 
      94             :             /* --------------------------------------------------------------------
      95             :              */
      96             :             /*      Cleanup temporary file. */
      97             :             /* --------------------------------------------------------------------
      98             :              */
      99          43 :             VSIFCloseL(fpTemp);
     100          43 :             VSIUnlink(osTempFilename);
     101             :         }
     102             : 
     103             :         /* --------------------------------------------------------------------
     104             :          */
     105             :         /*      Write trailer. */
     106             :         /* --------------------------------------------------------------------
     107             :          */
     108          44 :         if (osTrailerFile != "")
     109          44 :             TransferUpdateTrailer(fp);
     110             : 
     111             :         /* --------------------------------------------------------------------
     112             :          */
     113             :         /*      Fixup the HANDSEED value now that we know all the entity ids */
     114             :         /*      created. */
     115             :         /* --------------------------------------------------------------------
     116             :          */
     117          44 :         FixupHANDSEED(fp);
     118             : 
     119             :         /* --------------------------------------------------------------------
     120             :          */
     121             :         /*      Close file. */
     122             :         /* --------------------------------------------------------------------
     123             :          */
     124             : 
     125          44 :         VSIFCloseL(fp);
     126          44 :         fp = nullptr;
     127             :     }
     128             : 
     129             :     /* -------------------------------------------------------------------- */
     130             :     /*      Destroy layers.                                                 */
     131             :     /* -------------------------------------------------------------------- */
     132          45 :     delete poLayer;
     133          45 :     delete poBlocksLayer;
     134             : 
     135          45 :     CSLDestroy(papszLayersToCreate);
     136          90 : }
     137             : 
     138             : /************************************************************************/
     139             : /*                           TestCapability()                           */
     140             : /************************************************************************/
     141             : 
     142          48 : int OGRDXFWriterDS::TestCapability(const char *pszCap)
     143             : 
     144             : {
     145          48 :     if (EQUAL(pszCap, ODsCCreateLayer))
     146             :         // Unable to have more than one OGR entities layer in a DXF file, with
     147             :         // one options blocks layer.
     148          32 :         return poBlocksLayer == nullptr || poLayer == nullptr;
     149             :     else
     150          16 :         return FALSE;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                              GetLayer()                              */
     155             : /************************************************************************/
     156             : 
     157           0 : OGRLayer *OGRDXFWriterDS::GetLayer(int iLayer)
     158             : 
     159             : {
     160           0 :     if (iLayer == 0)
     161           0 :         return poLayer;
     162             :     else
     163           0 :         return nullptr;
     164             : }
     165             : 
     166             : /************************************************************************/
     167             : /*                           GetLayerCount()                            */
     168             : /************************************************************************/
     169             : 
     170           0 : int OGRDXFWriterDS::GetLayerCount()
     171             : 
     172             : {
     173           0 :     if (poLayer)
     174           0 :         return 1;
     175             :     else
     176           0 :         return 0;
     177             : }
     178             : 
     179             : /************************************************************************/
     180             : /*                                Open()                                */
     181             : /************************************************************************/
     182             : 
     183          45 : int OGRDXFWriterDS::Open(const char *pszFilename, char **papszOptions)
     184             : 
     185             : {
     186             :     /* -------------------------------------------------------------------- */
     187             :     /*      Open the standard header, or a user provided header.            */
     188             :     /* -------------------------------------------------------------------- */
     189          45 :     if (CSLFetchNameValue(papszOptions, "HEADER") != nullptr)
     190           3 :         osHeaderFile = CSLFetchNameValue(papszOptions, "HEADER");
     191             :     else
     192             :     {
     193          42 :         const char *pszValue = CPLFindFile("gdal", "header.dxf");
     194          42 :         if (pszValue == nullptr)
     195             :         {
     196           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     197             :                      "Failed to find template header file header.dxf for "
     198             :                      "reading,\nis GDAL_DATA set properly?");
     199           0 :             return FALSE;
     200             :         }
     201          42 :         osHeaderFile = pszValue;
     202             :     }
     203             : 
     204             :     /* -------------------------------------------------------------------- */
     205             :     /*      Establish the name for our trailer file.                        */
     206             :     /* -------------------------------------------------------------------- */
     207          45 :     if (CSLFetchNameValue(papszOptions, "TRAILER") != nullptr)
     208           0 :         osTrailerFile = CSLFetchNameValue(papszOptions, "TRAILER");
     209             :     else
     210             :     {
     211          45 :         const char *pszValue = CPLFindFile("gdal", "trailer.dxf");
     212          45 :         if (pszValue != nullptr)
     213          45 :             osTrailerFile = pszValue;
     214             :     }
     215             : 
     216             : /* -------------------------------------------------------------------- */
     217             : /*      What entity id do we want to start with when writing?  Small    */
     218             : /*      values run a risk of overlapping some undetected entity in      */
     219             : /*      the header or trailer despite the prescanning we do.            */
     220             : /* -------------------------------------------------------------------- */
     221             : #ifdef DEBUG
     222          45 :     nNextFID = 80;
     223             : #else
     224             :     nNextFID = 131072;
     225             : #endif
     226             : 
     227          45 :     if (CSLFetchNameValue(papszOptions, "FIRST_ENTITY") != nullptr)
     228           1 :         nNextFID = atoi(CSLFetchNameValue(papszOptions, "FIRST_ENTITY"));
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      Prescan the header and trailer for entity codes.                */
     232             :     /* -------------------------------------------------------------------- */
     233          45 :     ScanForEntities(osHeaderFile, "HEADER");
     234          45 :     ScanForEntities(osTrailerFile, "TRAILER");
     235             : 
     236             :     /* -------------------------------------------------------------------- */
     237             :     /*      Attempt to read the template header file so we have a list      */
     238             :     /*      of layers, linestyles and blocks.                               */
     239             :     /* -------------------------------------------------------------------- */
     240          45 :     if (!oHeaderDS.Open(osHeaderFile, TRUE))
     241           0 :         return FALSE;
     242             : 
     243             :     /* -------------------------------------------------------------------- */
     244             :     /*      Create the output file.                                         */
     245             :     /* -------------------------------------------------------------------- */
     246          45 :     fp = VSIFOpenExL(pszFilename, "w+", true);
     247             : 
     248          45 :     if (fp == nullptr)
     249             :     {
     250           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     251             :                  "Failed to open '%s' for writing: %s", pszFilename,
     252             :                  VSIGetLastErrorMsg());
     253           1 :         return FALSE;
     254             :     }
     255             : 
     256             :     /* -------------------------------------------------------------------- */
     257             :     /*      Establish the temporary file.                                   */
     258             :     /* -------------------------------------------------------------------- */
     259          44 :     osTempFilename = pszFilename;
     260          44 :     osTempFilename += ".tmp";
     261             : 
     262          44 :     fpTemp = VSIFOpenL(osTempFilename, "w");
     263          44 :     if (fpTemp == nullptr)
     264             :     {
     265           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     266             :                  "Failed to open '%s' for writing.", osTempFilename.c_str());
     267           0 :         return FALSE;
     268             :     }
     269             : 
     270          44 :     return TRUE;
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*                           ICreateLayer()                             */
     275             : /************************************************************************/
     276             : 
     277             : OGRLayer *
     278          61 : OGRDXFWriterDS::ICreateLayer(const char *pszName,
     279             :                              const OGRGeomFieldDefn * /*poGeomFieldDefn*/,
     280             :                              CSLConstList /*papszOptions*/)
     281             : 
     282             : {
     283          61 :     if (EQUAL(pszName, "blocks") && poBlocksLayer == nullptr)
     284             :     {
     285           2 :         poBlocksLayer = new OGRDXFBlocksWriterLayer(this);
     286           2 :         return poBlocksLayer;
     287             :     }
     288          59 :     else if (poLayer == nullptr)
     289             :     {
     290          43 :         poLayer = new OGRDXFWriterLayer(this, fpTemp);
     291          43 :         return poLayer;
     292             :     }
     293             :     else
     294             :     {
     295          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     296             :                  "Unable to have more than one OGR entities layer in a DXF "
     297             :                  "file, with one options blocks layer.");
     298          16 :         return nullptr;
     299             :     }
     300             : }
     301             : 
     302             : /************************************************************************/
     303             : /*                             WriteValue()                             */
     304             : /************************************************************************/
     305             : 
     306       35376 : static bool WriteValue(VSILFILE *fp, int nCode, const char *pszLine)
     307             : 
     308             : {
     309             :     char szLinePair[300];
     310             : 
     311       35376 :     snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine);
     312       35376 :     size_t nLen = strlen(szLinePair);
     313       35376 :     if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
     314             :     {
     315           0 :         CPLError(CE_Failure, CPLE_FileIO,
     316             :                  "Attempt to write line to DXF file failed, disk full?.");
     317           0 :         return false;
     318             :     }
     319             : 
     320       35376 :     return true;
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                             WriteValue()                             */
     325             : /************************************************************************/
     326             : 
     327         184 : static bool WriteValue(VSILFILE *fp, int nCode, double dfValue)
     328             : 
     329             : {
     330             :     char szLinePair[64];
     331             : 
     332         184 :     CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
     333         184 :     size_t nLen = strlen(szLinePair);
     334         184 :     if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
     335             :     {
     336           0 :         CPLError(CE_Failure, CPLE_FileIO,
     337             :                  "Attempt to write line to DXF file failed, disk full?.");
     338           0 :         return false;
     339             :     }
     340             : 
     341         184 :     return true;
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                        TransferUpdateHeader()                        */
     346             : /************************************************************************/
     347             : 
     348          44 : bool OGRDXFWriterDS::TransferUpdateHeader(VSILFILE *fpOut)
     349             : 
     350             : {
     351          44 :     oHeaderDS.ResetReadPointer(0);
     352             : 
     353             :     // We don't like non-finite extents. In this case, just write a generic
     354             :     // bounding box. Most CAD programs probably ignore this anyway.
     355          44 :     if (!CPLIsFinite(oGlobalEnvelope.MinX) ||
     356          26 :         !CPLIsFinite(oGlobalEnvelope.MinY) ||
     357          26 :         !CPLIsFinite(oGlobalEnvelope.MaxX) ||
     358          26 :         !CPLIsFinite(oGlobalEnvelope.MaxY))
     359             :     {
     360          18 :         oGlobalEnvelope.MinX = 0.0;
     361          18 :         oGlobalEnvelope.MinY = 0.0;
     362          18 :         oGlobalEnvelope.MaxX = 10.0;
     363          18 :         oGlobalEnvelope.MaxY = 10.0;
     364             :     }
     365             : 
     366             :     /* -------------------------------------------------------------------- */
     367             :     /*      Copy header, inserting in new objects as needed.                */
     368             :     /* -------------------------------------------------------------------- */
     369             :     char szLineBuf[257];
     370          44 :     int nCode = 0;
     371          88 :     CPLString osSection;
     372          88 :     CPLString osTable;
     373          88 :     CPLString osEntity;
     374             : 
     375       48850 :     while ((nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1 &&
     376       24403 :            osSection != "ENTITIES")
     377             :     {
     378       24403 :         if (nCode == 0 && EQUAL(szLineBuf, "ENDTAB"))
     379             :         {
     380             :             // If we are at the end of the LAYER TABLE consider inserting
     381             :             // missing definitions.
     382         396 :             if (osTable == "LAYER")
     383             :             {
     384          44 :                 if (!WriteNewLayerDefinitions(fp))
     385           0 :                     return false;
     386             :             }
     387             : 
     388             :             // If at the end of the BLOCK_RECORD TABLE consider inserting
     389             :             // missing definitions.
     390         396 :             if (osTable == "BLOCK_RECORD" && poBlocksLayer)
     391             :             {
     392           2 :                 if (!WriteNewBlockRecords(fp))
     393           0 :                     return false;
     394             :             }
     395             : 
     396             :             // If at the end of the LTYPE TABLE consider inserting
     397             :             // missing layer type definitions.
     398         396 :             if (osTable == "LTYPE")
     399             :             {
     400          44 :                 if (!WriteNewLineTypeRecords(fp))
     401           0 :                     return false;
     402             :             }
     403             : 
     404             :             // If at the end of the STYLE TABLE consider inserting
     405             :             // missing layer type definitions.
     406         396 :             if (osTable == "STYLE")
     407             :             {
     408          44 :                 if (!WriteNewTextStyleRecords(fp))
     409           0 :                     return false;
     410             :             }
     411             : 
     412         396 :             osTable = "";
     413             :         }
     414             : 
     415             :         // If we are at the end of the BLOCKS section, consider inserting
     416             :         // supplementary blocks.
     417       24447 :         if (nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf, "ENDSEC") &&
     418          44 :             poBlocksLayer != nullptr)
     419             :         {
     420           2 :             if (!WriteNewBlockDefinitions(fp))
     421           0 :                 return false;
     422             :         }
     423             : 
     424             :         // We need to keep track of where $HANDSEED is so that we can
     425             :         // come back and fix it up when we have generated all entity ids.
     426       24403 :         if (nCode == 9 && EQUAL(szLineBuf, "$HANDSEED"))
     427             :         {
     428          44 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     429           0 :                 return false;
     430             : 
     431          44 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     432             : 
     433             :             // ensure we have room to overwrite with a longer value.
     434         308 :             while (strlen(szLineBuf) < 8)
     435             :             {
     436         264 :                 memmove(szLineBuf + 1, szLineBuf, strlen(szLineBuf) + 1);
     437         264 :                 szLineBuf[0] = '0';
     438             :             }
     439             : 
     440          44 :             nHANDSEEDOffset = VSIFTellL(fpOut);
     441             :         }
     442             : 
     443             :         // Patch EXTMIN with minx and miny
     444       24403 :         if (nCode == 9 && EQUAL(szLineBuf, "$EXTMIN"))
     445             :         {
     446          44 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     447           0 :                 return false;
     448             : 
     449          44 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     450          44 :             if (nCode == 10)
     451             :             {
     452          44 :                 if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinX))
     453           0 :                     return false;
     454             : 
     455          44 :                 nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     456          44 :                 if (nCode == 20)
     457             :                 {
     458          44 :                     if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinY))
     459           0 :                         return false;
     460             : 
     461          44 :                     continue;
     462             :                 }
     463             :             }
     464             :         }
     465             : 
     466             :         // Patch EXTMAX with maxx and maxy
     467       24359 :         if (nCode == 9 && EQUAL(szLineBuf, "$EXTMAX"))
     468             :         {
     469          44 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     470           0 :                 return false;
     471             : 
     472          44 :             nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     473          44 :             if (nCode == 10)
     474             :             {
     475          44 :                 if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxX))
     476           0 :                     return false;
     477             : 
     478          44 :                 nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
     479          44 :                 if (nCode == 20)
     480             :                 {
     481          44 :                     if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxY))
     482           0 :                         return false;
     483             : 
     484          44 :                     continue;
     485             :                 }
     486             :             }
     487             :         }
     488             : 
     489             :         // Copy over the source line.
     490       24315 :         if (!WriteValue(fpOut, nCode, szLineBuf))
     491           0 :             return false;
     492             : 
     493             :         // Track what entity we are in - that is the last "code 0" object.
     494       24315 :         if (nCode == 0)
     495        1887 :             osEntity = szLineBuf;
     496             : 
     497             :         // Track what section we are in.
     498       24315 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
     499             :         {
     500         220 :             nCode = oHeaderDS.ReadValue(szLineBuf);
     501         220 :             if (nCode == -1)
     502           0 :                 break;
     503             : 
     504         220 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     505           0 :                 return false;
     506             : 
     507         220 :             osSection = szLineBuf;
     508             :         }
     509             : 
     510             :         // Track what TABLE we are in.
     511       24315 :         if (nCode == 0 && EQUAL(szLineBuf, "TABLE"))
     512             :         {
     513         396 :             nCode = oHeaderDS.ReadValue(szLineBuf);
     514         396 :             if (!WriteValue(fpOut, nCode, szLineBuf))
     515           0 :                 return false;
     516             : 
     517         396 :             osTable = szLineBuf;
     518             :         }
     519             : 
     520             :         // If we are starting the first layer, then capture
     521             :         // the layer contents while copying so we can duplicate
     522             :         // it for any new layer definitions.
     523       24359 :         if (nCode == 0 && EQUAL(szLineBuf, "LAYER") && osTable == "LAYER" &&
     524          44 :             aosDefaultLayerText.empty())
     525             :         {
     526         440 :             do
     527             :             {
     528         484 :                 anDefaultLayerCode.push_back(nCode);
     529         484 :                 aosDefaultLayerText.push_back(szLineBuf);
     530             : 
     531         484 :                 if (nCode != 0 && !WriteValue(fpOut, nCode, szLineBuf))
     532           0 :                     return false;
     533             : 
     534         484 :                 nCode = oHeaderDS.ReadValue(szLineBuf);
     535             : 
     536         484 :                 if (nCode == 2 && !EQUAL(szLineBuf, "0"))
     537             :                 {
     538           0 :                     anDefaultLayerCode.resize(0);
     539           0 :                     aosDefaultLayerText.resize(0);
     540           0 :                     break;
     541             :                 }
     542         484 :             } while (nCode != 0);
     543             : 
     544          44 :             oHeaderDS.UnreadValue();
     545             :         }
     546             :     }
     547             : 
     548          44 :     return true;
     549             : }
     550             : 
     551             : /************************************************************************/
     552             : /*                       TransferUpdateTrailer()                        */
     553             : /************************************************************************/
     554             : 
     555          44 : bool OGRDXFWriterDS::TransferUpdateTrailer(VSILFILE *fpOut)
     556             : {
     557             :     /* -------------------------------------------------------------------- */
     558             :     /*      Open the file and setup a reader.                               */
     559             :     /* -------------------------------------------------------------------- */
     560          44 :     VSILFILE *l_fp = VSIFOpenL(osTrailerFile, "r");
     561             : 
     562          44 :     if (l_fp == nullptr)
     563           0 :         return false;
     564             : 
     565          88 :     OGRDXFReader oReader;
     566          44 :     oReader.Initialize(l_fp);
     567             : 
     568             :     /* -------------------------------------------------------------------- */
     569             :     /*      Scan ahead to find the OBJECTS section.                         */
     570             :     /* -------------------------------------------------------------------- */
     571             :     char szLineBuf[257];
     572          44 :     int nCode = 0;
     573             : 
     574          88 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
     575             :     {
     576          88 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
     577             :         {
     578          44 :             nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
     579          44 :             if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
     580          44 :                 break;
     581             :         }
     582             :     }
     583             : 
     584          44 :     if (nCode == -1)
     585             :     {
     586           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     587             :                  "Failed to find OBJECTS section in trailer file '%s'.",
     588             :                  osTrailerFile.c_str());
     589           0 :         return false;
     590             :     }
     591             : 
     592             :     /* -------------------------------------------------------------------- */
     593             :     /*      Insert the "end of section" for ENTITIES, and the start of      */
     594             :     /*      the OBJECTS section.                                            */
     595             :     /* -------------------------------------------------------------------- */
     596          44 :     WriteValue(fpOut, 0, "ENDSEC");
     597          44 :     WriteValue(fpOut, 0, "SECTION");
     598          44 :     WriteValue(fpOut, 2, "OBJECTS");
     599             : 
     600             :     /* -------------------------------------------------------------------- */
     601             :     /*      Copy the remainder of the file.                                 */
     602             :     /* -------------------------------------------------------------------- */
     603          44 :     bool ret = true;
     604        9460 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
     605             :     {
     606        9416 :         if (!WriteValue(fpOut, nCode, szLineBuf))
     607             :         {
     608           0 :             ret = false;
     609           0 :             break;
     610             :         }
     611             :     }
     612             : 
     613          44 :     VSIFCloseL(l_fp);
     614             : 
     615          44 :     return ret;
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                           FixupHANDSEED()                            */
     620             : /*                                                                      */
     621             : /*      Fixup the next entity id information in the $HANDSEED header    */
     622             : /*      variable.                                                       */
     623             : /************************************************************************/
     624             : 
     625          44 : bool OGRDXFWriterDS::FixupHANDSEED(VSILFILE *fpIn)
     626             : 
     627             : {
     628             :     /* -------------------------------------------------------------------- */
     629             :     /*      What is a good next handle seed?  Scan existing values.         */
     630             :     /* -------------------------------------------------------------------- */
     631          44 :     unsigned int nHighestHandle = 0;
     632          44 :     std::set<CPLString>::iterator it;
     633             : 
     634        1755 :     for (it = aosUsedEntities.begin(); it != aosUsedEntities.end(); ++it)
     635             :     {
     636        1711 :         unsigned int nHandle = 0;
     637        1711 :         if (sscanf((*it).c_str(), "%x", &nHandle) == 1)
     638             :         {
     639        1711 :             if (nHandle > nHighestHandle)
     640        1125 :                 nHighestHandle = nHandle;
     641             :         }
     642             :     }
     643             : 
     644             :     /* -------------------------------------------------------------------- */
     645             :     /*      Read the existing handseed value, replace it, and write back.   */
     646             :     /* -------------------------------------------------------------------- */
     647          44 :     if (nHANDSEEDOffset == 0)
     648           0 :         return false;
     649             : 
     650             :     char szWorkBuf[30];
     651          44 :     VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
     652          44 :     VSIFReadL(szWorkBuf, 1, sizeof(szWorkBuf), fpIn);
     653             : 
     654          44 :     int i = 0;
     655         176 :     while (szWorkBuf[i] != '\n')
     656         132 :         i++;
     657             : 
     658          44 :     i++;
     659          44 :     if (szWorkBuf[i] == '\r')
     660           0 :         i++;
     661             : 
     662          44 :     CPLString osNewValue;
     663             : 
     664          44 :     osNewValue.Printf("%08X", nHighestHandle + 1);
     665          44 :     strncpy(szWorkBuf + i, osNewValue.c_str(), osNewValue.size());
     666             : 
     667          44 :     VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
     668          44 :     VSIFWriteL(szWorkBuf, 1, sizeof(szWorkBuf), fp);
     669             : 
     670          44 :     return true;
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*                      WriteNewLayerDefinitions()                      */
     675             : /************************************************************************/
     676             : 
     677          44 : bool OGRDXFWriterDS::WriteNewLayerDefinitions(VSILFILE *fpOut)
     678             : 
     679             : {
     680          44 :     const int nNewLayers = CSLCount(papszLayersToCreate);
     681             : 
     682          47 :     for (int iLayer = 0; iLayer < nNewLayers; iLayer++)
     683             :     {
     684           3 :         bool bIsDefPoints = false;
     685           3 :         bool bWrote290 = false;
     686          36 :         for (unsigned i = 0; i < aosDefaultLayerText.size(); i++)
     687             :         {
     688          33 :             if (anDefaultLayerCode[i] == 2)
     689             :             {
     690           3 :                 if (EQUAL(papszLayersToCreate[iLayer], "DEFPOINTS"))
     691           0 :                     bIsDefPoints = true;
     692             : 
     693           3 :                 if (!WriteValue(fpOut, 2, papszLayersToCreate[iLayer]))
     694           0 :                     return false;
     695             :             }
     696          30 :             else if (anDefaultLayerCode[i] == 5)
     697             :             {
     698             :                 long nIgnored;
     699           3 :                 if (!WriteEntityID(fpOut, nIgnored))
     700           0 :                     return false;
     701             :             }
     702             :             else
     703             :             {
     704          27 :                 if (anDefaultLayerCode[i] == 290)
     705           0 :                     bWrote290 = true;
     706             : 
     707          27 :                 if (!WriteValue(fpOut, anDefaultLayerCode[i],
     708          27 :                                 aosDefaultLayerText[i]))
     709           0 :                     return false;
     710             :             }
     711             :         }
     712           3 :         if (bIsDefPoints && !bWrote290)
     713             :         {
     714             :             // The Defpoints layer must be explicitly set to not plotted to
     715             :             // please Autocad. See https://trac.osgeo.org/gdal/ticket/7078
     716           0 :             if (!WriteValue(fpOut, 290, "0"))
     717           0 :                 return false;
     718             :         }
     719             :     }
     720             : 
     721          44 :     return true;
     722             : }
     723             : 
     724             : /************************************************************************/
     725             : /*                      WriteNewLineTypeRecords()                       */
     726             : /************************************************************************/
     727             : 
     728          44 : bool OGRDXFWriterDS::WriteNewLineTypeRecords(VSILFILE *fpIn)
     729             : 
     730             : {
     731          44 :     if (poLayer == nullptr)
     732           1 :         return true;
     733             : 
     734             :     const std::map<CPLString, std::vector<double>> &oNewLineTypes =
     735          43 :         poLayer->GetNewLineTypeMap();
     736             : 
     737          43 :     bool bRet = true;
     738          45 :     for (const auto &oPair : oNewLineTypes)
     739             :     {
     740           2 :         bRet &= WriteValue(fpIn, 0, "LTYPE");
     741             :         long nIgnored;
     742           2 :         bRet &= WriteEntityID(fpIn, nIgnored);
     743           2 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
     744           2 :         bRet &= WriteValue(fpIn, 100, "AcDbLinetypeTableRecord");
     745           2 :         bRet &= WriteValue(fpIn, 2, oPair.first);
     746           2 :         bRet &= WriteValue(fpIn, 70, "0");
     747           2 :         bRet &= WriteValue(fpIn, 3, "");
     748           2 :         bRet &= WriteValue(fpIn, 72, "65");
     749           2 :         bRet &= WriteValue(fpIn, 73, static_cast<int>(oPair.second.size()));
     750             : 
     751           2 :         double dfTotalLength = 0.0;
     752           6 :         for (const double &dfSegment : oPair.second)
     753           4 :             dfTotalLength += fabs(dfSegment);
     754           2 :         bRet &= WriteValue(fpIn, 40, dfTotalLength);
     755             : 
     756           6 :         for (const double &dfSegment : oPair.second)
     757             :         {
     758           4 :             bRet &= WriteValue(fpIn, 49, dfSegment);
     759           4 :             bRet &= WriteValue(fpIn, 74, "0");
     760             :         }
     761             :     }
     762             : 
     763          43 :     return bRet;
     764             : }
     765             : 
     766             : /************************************************************************/
     767             : /*                      WriteNewTextStyleRecords()                      */
     768             : /************************************************************************/
     769             : 
     770          44 : bool OGRDXFWriterDS::WriteNewTextStyleRecords(VSILFILE *fpIn)
     771             : 
     772             : {
     773          44 :     if (poLayer == nullptr)
     774           1 :         return true;
     775             : 
     776          43 :     auto &oNewTextStyles = poLayer->GetNewTextStyleMap();
     777             : 
     778          43 :     bool bRet = true;
     779          44 :     for (auto &oPair : oNewTextStyles)
     780             :     {
     781           1 :         bRet &= WriteValue(fpIn, 0, "STYLE");
     782             :         long nIgnored;
     783           1 :         bRet &= WriteEntityID(fpIn, nIgnored);
     784           1 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
     785           1 :         bRet &= WriteValue(fpIn, 100, "AcDbTextStyleTableRecord");
     786           1 :         bRet &= WriteValue(fpIn, 2, oPair.first);
     787           1 :         bRet &= WriteValue(fpIn, 70, "0");
     788           1 :         bRet &= WriteValue(fpIn, 40, "0.0");
     789             : 
     790           1 :         if (oPair.second.count("Width"))
     791           1 :             bRet &= WriteValue(fpIn, 41, oPair.second["Width"]);
     792             :         else
     793           0 :             bRet &= WriteValue(fpIn, 41, "1.0");
     794             : 
     795           1 :         bRet &= WriteValue(fpIn, 50, "0.0");
     796           1 :         bRet &= WriteValue(fpIn, 71, "0");
     797           1 :         bRet &= WriteValue(fpIn, 1001, "ACAD");
     798             : 
     799           1 :         if (oPair.second.count("Font"))
     800           1 :             bRet &= WriteValue(fpIn, 1000, oPair.second["Font"]);
     801             : 
     802           1 :         int nStyleValue = 0;
     803           1 :         if (oPair.second.count("Italic") && oPair.second["Italic"] == "1")
     804           0 :             nStyleValue |= 0x1000000;
     805           1 :         if (oPair.second.count("Bold") && oPair.second["Bold"] == "1")
     806           1 :             nStyleValue |= 0x2000000;
     807           1 :         bRet &= WriteValue(fpIn, 1071,
     808           2 :                            CPLString().Printf("%d", nStyleValue).c_str());
     809             :     }
     810             : 
     811          43 :     return bRet;
     812             : }
     813             : 
     814             : /************************************************************************/
     815             : /*                        WriteNewBlockRecords()                        */
     816             : /************************************************************************/
     817             : 
     818           2 : bool OGRDXFWriterDS::WriteNewBlockRecords(VSILFILE *fpIn)
     819             : 
     820             : {
     821           2 :     std::set<CPLString> aosAlreadyHandled;
     822             : 
     823             :     /* ==================================================================== */
     824             :     /*      Loop over all block objects written via the blocks layer.       */
     825             :     /* ==================================================================== */
     826           2 :     bool bRet = true;
     827           6 :     for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
     828             :     {
     829           4 :         OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
     830             : 
     831             :         /* --------------------------------------------------------------------
     832             :          */
     833             :         /*      Is this block already defined in the template header? */
     834             :         /* --------------------------------------------------------------------
     835             :          */
     836           4 :         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
     837             : 
     838           4 :         if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
     839           0 :             continue;
     840             : 
     841             :         /* --------------------------------------------------------------------
     842             :          */
     843             :         /*      Have we already written a BLOCK_RECORD for this block? */
     844             :         /* --------------------------------------------------------------------
     845             :          */
     846           4 :         if (aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end())
     847           0 :             continue;
     848             : 
     849           4 :         aosAlreadyHandled.insert(osBlockName);
     850             : 
     851             :         /* --------------------------------------------------------------------
     852             :          */
     853             :         /*      Write the block record. */
     854             :         /* --------------------------------------------------------------------
     855             :          */
     856           4 :         bRet &= WriteValue(fpIn, 0, "BLOCK_RECORD");
     857             :         long nIgnored;
     858           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
     859           4 :         bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
     860           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockTableRecord");
     861           4 :         bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
     862           4 :         bRet &= WriteValue(fpIn, 340, "0");
     863             :     }
     864             : 
     865           4 :     return bRet;
     866             : }
     867             : 
     868             : /************************************************************************/
     869             : /*                      WriteNewBlockDefinitions()                      */
     870             : /************************************************************************/
     871             : 
     872           2 : bool OGRDXFWriterDS::WriteNewBlockDefinitions(VSILFILE *fpIn)
     873             : 
     874             : {
     875           2 :     if (poLayer == nullptr)
     876           1 :         poLayer = new OGRDXFWriterLayer(this, fpTemp);
     877           2 :     poLayer->ResetFP(fpIn);
     878             : 
     879             :     /* ==================================================================== */
     880             :     /*      Loop over all block objects written via the blocks layer.       */
     881             :     /* ==================================================================== */
     882           2 :     bool bRet = true;
     883           6 :     for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
     884             :     {
     885           4 :         OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
     886             : 
     887             :         /* --------------------------------------------------------------------
     888             :          */
     889             :         /*      Is this block already defined in the template header? */
     890             :         /* --------------------------------------------------------------------
     891             :          */
     892           4 :         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
     893             : 
     894           4 :         if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
     895           0 :             continue;
     896             : 
     897             :         /* --------------------------------------------------------------------
     898             :          */
     899             :         /*      Write the block definition preamble. */
     900             :         /* --------------------------------------------------------------------
     901             :          */
     902           4 :         CPLDebug("DXF", "Writing BLOCK definition for '%s'.",
     903             :                  poThisBlockFeat->GetFieldAsString("Block"));
     904             : 
     905           4 :         bRet &= WriteValue(fpIn, 0, "BLOCK");
     906             :         long nIgnored;
     907           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
     908           4 :         bRet &= WriteValue(fpIn, 100, "AcDbEntity");
     909           4 :         if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
     910           0 :             bRet &=
     911           0 :                 WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
     912             :         else
     913           4 :             bRet &= WriteValue(fpIn, 8, "0");
     914           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockBegin");
     915           4 :         bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
     916           4 :         bRet &= WriteValue(fpIn, 70, "0");
     917             : 
     918             :         // Origin
     919           4 :         bRet &= WriteValue(fpIn, 10, "0.0");
     920           4 :         bRet &= WriteValue(fpIn, 20, "0.0");
     921           4 :         bRet &= WriteValue(fpIn, 30, "0.0");
     922             : 
     923           4 :         bRet &= WriteValue(fpIn, 3, poThisBlockFeat->GetFieldAsString("Block"));
     924           4 :         bRet &= WriteValue(fpIn, 1, "");
     925             : 
     926             :         /* --------------------------------------------------------------------
     927             :          */
     928             :         /*      Write out the feature entities. */
     929             :         /* --------------------------------------------------------------------
     930             :          */
     931           4 :         if (poLayer->CreateFeature(poThisBlockFeat) != OGRERR_NONE)
     932           0 :             return false;
     933             : 
     934             :         /* --------------------------------------------------------------------
     935             :          */
     936             :         /*      Write out following features if they are the same block. */
     937             :         /* --------------------------------------------------------------------
     938             :          */
     939           6 :         while (iBlock < poBlocksLayer->apoBlocks.size() - 1 &&
     940           2 :                EQUAL(poBlocksLayer->apoBlocks[iBlock + 1]->GetFieldAsString(
     941             :                          "Block"),
     942             :                      osBlockName))
     943             :         {
     944           0 :             iBlock++;
     945             : 
     946           0 :             if (poLayer->CreateFeature(poBlocksLayer->apoBlocks[iBlock]) !=
     947             :                 OGRERR_NONE)
     948           0 :                 return false;
     949             :         }
     950             : 
     951             :         /* --------------------------------------------------------------------
     952             :          */
     953             :         /*      Write out the block definition postamble. */
     954             :         /* --------------------------------------------------------------------
     955             :          */
     956           4 :         bRet &= WriteValue(fpIn, 0, "ENDBLK");
     957           4 :         bRet &= WriteEntityID(fpIn, nIgnored);
     958           4 :         bRet &= WriteValue(fpIn, 100, "AcDbEntity");
     959           4 :         if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
     960           0 :             bRet &=
     961           0 :                 WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
     962             :         else
     963           4 :             bRet &= WriteValue(fpIn, 8, "0");
     964           4 :         bRet &= WriteValue(fpIn, 100, "AcDbBlockEnd");
     965             :     }
     966             : 
     967           2 :     return bRet;
     968             : }
     969             : 
     970             : /************************************************************************/
     971             : /*                          ScanForEntities()                           */
     972             : /*                                                                      */
     973             : /*      Scan the indicated file for entity ids ("5" records) and        */
     974             : /*      build them up as a set so we can be careful to avoid            */
     975             : /*      creating new entities with conflicting ids.                     */
     976             : /************************************************************************/
     977             : 
     978          90 : void OGRDXFWriterDS::ScanForEntities(const char *pszFilename,
     979             :                                      const char *pszTarget)
     980             : 
     981             : {
     982             :     /* -------------------------------------------------------------------- */
     983             :     /*      Open the file and setup a reader.                               */
     984             :     /* -------------------------------------------------------------------- */
     985          90 :     VSILFILE *l_fp = VSIFOpenL(pszFilename, "r");
     986             : 
     987          90 :     if (l_fp == nullptr)
     988           0 :         return;
     989             : 
     990         180 :     OGRDXFReader oReader;
     991          90 :     oReader.Initialize(l_fp);
     992             : 
     993             :     /* -------------------------------------------------------------------- */
     994             :     /*      Add every code "5" line to our entities list.                   */
     995             :     /* -------------------------------------------------------------------- */
     996             :     char szLineBuf[257];
     997          90 :     int nCode = 0;
     998          90 :     const char *pszPortion = "HEADER";
     999             : 
    1000       35826 :     while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
    1001             :     {
    1002       35736 :         if ((nCode == 5 || nCode == 105) && EQUAL(pszTarget, pszPortion))
    1003             :         {
    1004        3132 :             CPLString osEntity(szLineBuf);
    1005             : 
    1006        1566 :             if (CheckEntityID(osEntity))
    1007           6 :                 CPLDebug("DXF", "Encountered entity '%s' multiple times.",
    1008             :                          osEntity.c_str());
    1009             :             else
    1010        1560 :                 aosUsedEntities.insert(osEntity);
    1011             :         }
    1012             : 
    1013       35736 :         if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
    1014             :         {
    1015         270 :             nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
    1016         270 :             if (nCode == 2 && EQUAL(szLineBuf, "ENTITIES"))
    1017          45 :                 pszPortion = "BODY";
    1018         270 :             if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
    1019          45 :                 pszPortion = "TRAILER";
    1020             :         }
    1021             :     }
    1022             : 
    1023          90 :     VSIFCloseL(l_fp);
    1024             : }
    1025             : 
    1026             : /************************************************************************/
    1027             : /*                           CheckEntityID()                            */
    1028             : /*                                                                      */
    1029             : /*      Does the mentioned entity already exist?                        */
    1030             : /************************************************************************/
    1031             : 
    1032        1765 : bool OGRDXFWriterDS::CheckEntityID(const char *pszEntityID)
    1033             : 
    1034             : {
    1035        1765 :     std::set<CPLString>::iterator it;
    1036             : 
    1037        1765 :     it = aosUsedEntities.find(pszEntityID);
    1038        1765 :     return it != aosUsedEntities.end();
    1039             : }
    1040             : 
    1041             : /************************************************************************/
    1042             : /*                           WriteEntityID()                            */
    1043             : /************************************************************************/
    1044             : 
    1045         185 : bool OGRDXFWriterDS::WriteEntityID(VSILFILE *fpIn, long &nAssignedFID,
    1046             :                                    long nPreferredFID)
    1047             : 
    1048             : {
    1049         370 :     CPLString osEntityID;
    1050             : 
    1051         185 :     if (nPreferredFID != OGRNullFID)
    1052             :     {
    1053             : 
    1054          14 :         osEntityID.Printf("%X", (unsigned int)nPreferredFID);
    1055          14 :         if (!CheckEntityID(osEntityID))
    1056             :         {
    1057           0 :             aosUsedEntities.insert(osEntityID);
    1058           0 :             if (!WriteValue(fpIn, 5, osEntityID))
    1059           0 :                 return false;
    1060           0 :             nAssignedFID = nPreferredFID;
    1061           0 :             return true;
    1062             :         }
    1063             :     }
    1064             : 
    1065           0 :     do
    1066             :     {
    1067         185 :         osEntityID.Printf("%X", nNextFID++);
    1068         185 :     } while (CheckEntityID(osEntityID));
    1069             : 
    1070         185 :     aosUsedEntities.insert(osEntityID);
    1071         185 :     if (!WriteValue(fpIn, 5, osEntityID))
    1072           0 :         return false;
    1073             : 
    1074         185 :     nAssignedFID = nNextFID - 1;
    1075         185 :     return true;
    1076             : }
    1077             : 
    1078             : /************************************************************************/
    1079             : /*                           UpdateExtent()                             */
    1080             : /************************************************************************/
    1081             : 
    1082         175 : void OGRDXFWriterDS::UpdateExtent(OGREnvelope *psEnvelope)
    1083             : {
    1084         175 :     oGlobalEnvelope.Merge(*psEnvelope);
    1085         175 : }

Generated by: LCOV version 1.14