LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfwriterlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 430 501 85.8 %
Date: 2024-11-21 22:18:42 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements OGRDXFWriterLayer - the OGRLayer 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) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_dxf.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_string.h"
      18             : #include "ogr_featurestyle.h"
      19             : 
      20             : #include <cstdlib>
      21             : 
      22             : /************************************************************************/
      23             : /*                         OGRDXFWriterLayer()                          */
      24             : /************************************************************************/
      25             : 
      26          44 : OGRDXFWriterLayer::OGRDXFWriterLayer(OGRDXFWriterDS *poDSIn, VSILFILE *fpIn)
      27             :     : fp(fpIn),
      28             :       poFeatureDefn(nullptr),  // TODO(schwehr): Can I move the new here?
      29          44 :       poDS(poDSIn)
      30             : {
      31          44 :     nNextAutoID = 1;
      32          44 :     bWriteHatch = CPLTestBool(CPLGetConfigOption("DXF_WRITE_HATCH", "YES"));
      33             : 
      34          44 :     poFeatureDefn = new OGRFeatureDefn("entities");
      35          44 :     poFeatureDefn->Reference();
      36             : 
      37          44 :     OGRDXFDataSource::AddStandardFields(poFeatureDefn, ODFM_IncludeBlockFields);
      38          44 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                         ~OGRDXFWriterLayer()                         */
      42             : /************************************************************************/
      43             : 
      44          88 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
      45             : 
      46             : {
      47          44 :     if (poFeatureDefn)
      48          44 :         poFeatureDefn->Release();
      49          88 : }
      50             : 
      51             : /************************************************************************/
      52             : /*                              ResetFP()                               */
      53             : /*                                                                      */
      54             : /*      Redirect output.  Mostly used for writing block definitions.    */
      55             : /************************************************************************/
      56             : 
      57           2 : void OGRDXFWriterLayer::ResetFP(VSILFILE *fpNew)
      58             : 
      59             : {
      60           2 :     fp = fpNew;
      61           2 : }
      62             : 
      63             : /************************************************************************/
      64             : /*                           TestCapability()                           */
      65             : /************************************************************************/
      66             : 
      67          88 : int OGRDXFWriterLayer::TestCapability(const char *pszCap)
      68             : 
      69             : {
      70          88 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
      71           0 :         return TRUE;
      72          88 :     else if (EQUAL(pszCap, OLCSequentialWrite))
      73          16 :         return TRUE;
      74             :     else
      75          72 :         return FALSE;
      76             : }
      77             : 
      78             : /************************************************************************/
      79             : /*                            CreateField()                             */
      80             : /*                                                                      */
      81             : /*      This is really a dummy as our fields are precreated.            */
      82             : /************************************************************************/
      83             : 
      84          80 : OGRErr OGRDXFWriterLayer::CreateField(const OGRFieldDefn *poField,
      85             :                                       int bApproxOK)
      86             : 
      87             : {
      88          80 :     if (poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0 && bApproxOK)
      89           0 :         return OGRERR_NONE;
      90          80 :     if (EQUAL(poField->GetNameRef(), "OGR_STYLE"))
      91             :     {
      92           0 :         poFeatureDefn->AddFieldDefn(poField);
      93           0 :         return OGRERR_NONE;
      94             :     }
      95             : 
      96          80 :     CPLError(CE_Failure, CPLE_AppDefined,
      97             :              "DXF layer does not support arbitrary field creation, field '%s' "
      98             :              "not created.",
      99             :              poField->GetNameRef());
     100             : 
     101          80 :     return OGRERR_FAILURE;
     102             : }
     103             : 
     104             : /************************************************************************/
     105             : /*                             WriteValue()                             */
     106             : /************************************************************************/
     107             : 
     108         703 : int OGRDXFWriterLayer::WriteValue(int nCode, const char *pszValue)
     109             : 
     110             : {
     111         703 :     CPLString osLinePair;
     112             : 
     113         703 :     osLinePair.Printf("%3d\n", nCode);
     114             : 
     115         703 :     if (strlen(pszValue) < 255)
     116         703 :         osLinePair += pszValue;
     117             :     else
     118           0 :         osLinePair.append(pszValue, 255);
     119             : 
     120         703 :     osLinePair += "\n";
     121             : 
     122         703 :     return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
     123        1406 :            osLinePair.size();
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                             WriteValue()                             */
     128             : /************************************************************************/
     129             : 
     130         355 : int OGRDXFWriterLayer::WriteValue(int nCode, int nValue)
     131             : 
     132             : {
     133         355 :     CPLString osLinePair;
     134             : 
     135         355 :     osLinePair.Printf("%3d\n%d\n", nCode, nValue);
     136             : 
     137         355 :     return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
     138         710 :            osLinePair.size();
     139             : }
     140             : 
     141             : /************************************************************************/
     142             : /*                             WriteValue()                             */
     143             : /************************************************************************/
     144             : 
     145         597 : int OGRDXFWriterLayer::WriteValue(int nCode, double dfValue)
     146             : 
     147             : {
     148             :     char szLinePair[64];
     149             : 
     150         597 :     CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
     151         597 :     size_t nLen = strlen(szLinePair);
     152             : 
     153         597 :     return VSIFWriteL(szLinePair, 1, nLen, fp) == nLen;
     154             : }
     155             : 
     156             : /************************************************************************/
     157             : /*                             WriteCore()                              */
     158             : /*                                                                      */
     159             : /*      Write core fields common to all sorts of elements.              */
     160             : /************************************************************************/
     161             : 
     162         167 : OGRErr OGRDXFWriterLayer::WriteCore(OGRFeature *poFeature)
     163             : 
     164             : {
     165             :     /* -------------------------------------------------------------------- */
     166             :     /*      Write out an entity id.  I'm not sure why this is critical,     */
     167             :     /*      but it seems that VoloView will just quietly fail to open       */
     168             :     /*      dxf files without entity ids set on most/all entities.          */
     169             :     /*      Also, for reasons I don't understand these ids seem to have     */
     170             :     /*      to start somewhere around 0x50 hex (80 decimal).                */
     171             :     /* -------------------------------------------------------------------- */
     172         167 :     unsigned int nGotFID = 0;
     173         167 :     poDS->WriteEntityID(fp, nGotFID, poFeature->GetFID());
     174         167 :     poFeature->SetFID(nGotFID);
     175             : 
     176         167 :     WriteValue(100, "AcDbEntity");
     177             : 
     178             :     /* -------------------------------------------------------------------- */
     179             :     /*      For now we assign everything to the default layer - layer       */
     180             :     /*      "0" - if there is no layer property on the source features.     */
     181             :     /* -------------------------------------------------------------------- */
     182         167 :     const char *pszLayer = poFeature->GetFieldAsString("Layer");
     183         167 :     if (pszLayer == nullptr || strlen(pszLayer) == 0)
     184             :     {
     185         160 :         WriteValue(8, "0");
     186             :     }
     187             :     else
     188             :     {
     189          14 :         CPLString osSanitizedLayer(pszLayer);
     190             :         // Replaced restricted characters with underscore
     191             :         // See
     192             :         // http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
     193           7 :         const char achForbiddenChars[] = {'<', '>', '/', '\\', '"', ':',
     194             :                                           ';', '?', '*', '|',  '=', '\''};
     195          91 :         for (size_t i = 0; i < CPL_ARRAYSIZE(achForbiddenChars); ++i)
     196             :         {
     197          84 :             osSanitizedLayer.replaceAll(achForbiddenChars[i], '_');
     198             :         }
     199             : 
     200             :         // also remove newline characters (#15067)
     201           7 :         osSanitizedLayer.replaceAll("\r\n", "_");
     202           7 :         osSanitizedLayer.replaceAll('\r', '_');
     203           7 :         osSanitizedLayer.replaceAll('\n', '_');
     204             : 
     205             :         const char *pszExists =
     206           7 :             poDS->oHeaderDS.LookupLayerProperty(osSanitizedLayer, "Exists");
     207          14 :         if ((pszExists == nullptr || strlen(pszExists) == 0) &&
     208           7 :             CSLFindString(poDS->papszLayersToCreate, osSanitizedLayer) == -1)
     209             :         {
     210           6 :             poDS->papszLayersToCreate =
     211           3 :                 CSLAddString(poDS->papszLayersToCreate, osSanitizedLayer);
     212             :         }
     213             : 
     214           7 :         WriteValue(8, osSanitizedLayer);
     215             :     }
     216             : 
     217         167 :     return OGRERR_NONE;
     218             : }
     219             : 
     220             : /************************************************************************/
     221             : /*                            WriteINSERT()                             */
     222             : /************************************************************************/
     223             : 
     224           7 : OGRErr OGRDXFWriterLayer::WriteINSERT(OGRFeature *poFeature)
     225             : 
     226             : {
     227           7 :     WriteValue(0, "INSERT");
     228           7 :     WriteCore(poFeature);
     229           7 :     WriteValue(100, "AcDbBlockReference");
     230           7 :     WriteValue(2, poFeature->GetFieldAsString("BlockName"));
     231             : 
     232             :     // Write style symbol color
     233           7 :     OGRStyleTool *poTool = nullptr;
     234          14 :     OGRStyleMgr oSM;
     235           7 :     if (poFeature->GetStyleString() != nullptr)
     236             :     {
     237           0 :         oSM.InitFromFeature(poFeature);
     238             : 
     239           0 :         if (oSM.GetPartCount() > 0)
     240           0 :             poTool = oSM.GetPart(0);
     241             :     }
     242           7 :     if (poTool && poTool->GetType() == OGRSTCSymbol)
     243             :     {
     244           0 :         OGRStyleSymbol *poSymbol = (OGRStyleSymbol *)poTool;
     245             :         GBool bDefault;
     246             : 
     247           0 :         if (poSymbol->Color(bDefault) != nullptr && !bDefault)
     248           0 :             WriteValue(62, ColorStringToDXFColor(poSymbol->Color(bDefault)));
     249             :     }
     250           7 :     delete poTool;
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Write location in OCS.                                          */
     254             :     /* -------------------------------------------------------------------- */
     255           7 :     int nCoordCount = 0;
     256             :     const double *padfCoords =
     257           7 :         poFeature->GetFieldAsDoubleList("BlockOCSCoords", &nCoordCount);
     258             : 
     259           7 :     if (nCoordCount == 3)
     260             :     {
     261           0 :         WriteValue(10, padfCoords[0]);
     262           0 :         WriteValue(20, padfCoords[1]);
     263           0 :         if (!WriteValue(30, padfCoords[2]))
     264           0 :             return OGRERR_FAILURE;
     265             :     }
     266             :     else
     267             :     {
     268             :         // We don't have an OCS; we will just assume that the location of
     269             :         // the geometry (in WCS) is the correct insertion point.
     270           7 :         OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     271             : 
     272           7 :         WriteValue(10, poPoint->getX());
     273           7 :         if (!WriteValue(20, poPoint->getY()))
     274           0 :             return OGRERR_FAILURE;
     275             : 
     276           7 :         if (poPoint->getGeometryType() == wkbPoint25D)
     277             :         {
     278           0 :             if (!WriteValue(30, poPoint->getZ()))
     279           0 :                 return OGRERR_FAILURE;
     280             :         }
     281             :     }
     282             : 
     283             :     /* -------------------------------------------------------------------- */
     284             :     /*      Write scaling.                                                  */
     285             :     /* -------------------------------------------------------------------- */
     286           7 :     int nScaleCount = 0;
     287             :     const double *padfScale =
     288           7 :         poFeature->GetFieldAsDoubleList("BlockScale", &nScaleCount);
     289             : 
     290           7 :     if (nScaleCount == 3)
     291             :     {
     292           1 :         WriteValue(41, padfScale[0]);
     293           1 :         WriteValue(42, padfScale[1]);
     294           1 :         WriteValue(43, padfScale[2]);
     295             :     }
     296             : 
     297             :     /* -------------------------------------------------------------------- */
     298             :     /*      Write rotation.                                                 */
     299             :     /* -------------------------------------------------------------------- */
     300           7 :     const double dfAngle = poFeature->GetFieldAsDouble("BlockAngle");
     301             : 
     302           7 :     if (dfAngle != 0.0)
     303             :     {
     304           1 :         WriteValue(50, dfAngle);  // degrees
     305             :     }
     306             : 
     307             :     /* -------------------------------------------------------------------- */
     308             :     /*      Write OCS normal vector.                                        */
     309             :     /* -------------------------------------------------------------------- */
     310           7 :     int nOCSCount = 0;
     311             :     const double *padfOCS =
     312           7 :         poFeature->GetFieldAsDoubleList("BlockOCSNormal", &nOCSCount);
     313             : 
     314           7 :     if (nOCSCount == 3)
     315             :     {
     316           0 :         WriteValue(210, padfOCS[0]);
     317           0 :         WriteValue(220, padfOCS[1]);
     318           0 :         WriteValue(230, padfOCS[2]);
     319             :     }
     320             : 
     321           7 :     return OGRERR_NONE;
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                             WritePOINT()                             */
     326             : /************************************************************************/
     327             : 
     328         111 : OGRErr OGRDXFWriterLayer::WritePOINT(OGRFeature *poFeature)
     329             : 
     330             : {
     331         111 :     WriteValue(0, "POINT");
     332         111 :     WriteCore(poFeature);
     333         111 :     WriteValue(100, "AcDbPoint");
     334             : 
     335             :     // Write style pen color
     336         111 :     OGRStyleTool *poTool = nullptr;
     337         222 :     OGRStyleMgr oSM;
     338         111 :     if (poFeature->GetStyleString() != nullptr)
     339             :     {
     340           0 :         oSM.InitFromFeature(poFeature);
     341             : 
     342           0 :         if (oSM.GetPartCount() > 0)
     343           0 :             poTool = oSM.GetPart(0);
     344             :     }
     345         111 :     if (poTool && poTool->GetType() == OGRSTCPen)
     346             :     {
     347           0 :         OGRStylePen *poPen = (OGRStylePen *)poTool;
     348             :         GBool bDefault;
     349             : 
     350           0 :         if (poPen->Color(bDefault) != nullptr && !bDefault)
     351           0 :             WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
     352             :     }
     353         111 :     delete poTool;
     354             : 
     355         111 :     OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     356             : 
     357         111 :     WriteValue(10, poPoint->getX());
     358         111 :     if (!WriteValue(20, poPoint->getY()))
     359           0 :         return OGRERR_FAILURE;
     360             : 
     361         111 :     if (poPoint->getGeometryType() == wkbPoint25D)
     362             :     {
     363           4 :         if (!WriteValue(30, poPoint->getZ()))
     364           0 :             return OGRERR_FAILURE;
     365             :     }
     366             : 
     367         111 :     return OGRERR_NONE;
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                             TextEscape()                             */
     372             : /*                                                                      */
     373             : /*      Translate UTF8 to Win1252 and escape special characters like    */
     374             : /*      newline and space with DXF style escapes.  Note that            */
     375             : /*      non-win1252 unicode characters are translated using the         */
     376             : /*      unicode escape sequence.                                        */
     377             : /************************************************************************/
     378             : 
     379           1 : CPLString OGRDXFWriterLayer::TextEscape(const char *pszInput)
     380             : 
     381             : {
     382           1 :     CPLString osResult;
     383           1 :     wchar_t *panInput = CPLRecodeToWChar(pszInput, CPL_ENC_UTF8, CPL_ENC_UCS2);
     384          31 :     for (int i = 0; panInput[i] != 0; i++)
     385             :     {
     386          30 :         if (panInput[i] == '\n')
     387             :         {
     388           0 :             osResult += "\\P";
     389             :         }
     390          30 :         else if (panInput[i] == ' ')
     391             :         {
     392           2 :             osResult += "\\~";
     393             :         }
     394          28 :         else if (panInput[i] == '\\')
     395             :         {
     396           0 :             osResult += "\\\\";
     397             :         }
     398          28 :         else if (panInput[i] == '^')
     399             :         {
     400           1 :             osResult += "^ ";
     401             :         }
     402          27 :         else if (panInput[i] < ' ')
     403             :         {
     404           1 :             osResult += '^';
     405           1 :             osResult += static_cast<char>(panInput[i] + '@');
     406             :         }
     407          26 :         else if (panInput[i] > 255)
     408             :         {
     409           0 :             CPLString osUnicode;
     410           0 :             osUnicode.Printf("\\U+%04x", (int)panInput[i]);
     411           0 :             osResult += osUnicode;
     412             :         }
     413             :         else
     414             :         {
     415          26 :             osResult += (char)panInput[i];
     416             :         }
     417             :     }
     418             : 
     419           1 :     CPLFree(panInput);
     420             : 
     421           1 :     return osResult;
     422             : }
     423             : 
     424             : /************************************************************************/
     425             : /*                     PrepareTextStyleDefinition()                     */
     426             : /************************************************************************/
     427             : std::map<CPLString, CPLString>
     428           1 : OGRDXFWriterLayer::PrepareTextStyleDefinition(OGRStyleLabel *poLabelTool)
     429             : {
     430             :     GBool bDefault;
     431             : 
     432           1 :     std::map<CPLString, CPLString> oTextStyleDef;
     433             : 
     434             :     /* -------------------------------------------------------------------- */
     435             :     /*      Fetch the data for this text style.                             */
     436             :     /* -------------------------------------------------------------------- */
     437           1 :     const char *pszFontName = poLabelTool->FontName(bDefault);
     438           1 :     if (!bDefault)
     439           1 :         oTextStyleDef["Font"] = pszFontName;
     440             : 
     441           1 :     const GBool bBold = poLabelTool->Bold(bDefault);
     442           1 :     if (!bDefault)
     443           1 :         oTextStyleDef["Bold"] = bBold ? "1" : "0";
     444             : 
     445           1 :     const GBool bItalic = poLabelTool->Italic(bDefault);
     446           1 :     if (!bDefault)
     447           0 :         oTextStyleDef["Italic"] = bItalic ? "1" : "0";
     448             : 
     449           1 :     const double dfStretch = poLabelTool->Stretch(bDefault);
     450           1 :     if (!bDefault)
     451             :     {
     452           1 :         oTextStyleDef["Width"] = CPLString().Printf("%f", dfStretch / 100.0);
     453             :     }
     454             : 
     455           2 :     return oTextStyleDef;
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                             WriteTEXT()                              */
     460             : /************************************************************************/
     461             : 
     462           1 : OGRErr OGRDXFWriterLayer::WriteTEXT(OGRFeature *poFeature)
     463             : 
     464             : {
     465           1 :     WriteValue(0, "MTEXT");
     466           1 :     WriteCore(poFeature);
     467           1 :     WriteValue(100, "AcDbMText");
     468             : 
     469             :     /* -------------------------------------------------------------------- */
     470             :     /*      Do we have styling information?                                 */
     471             :     /* -------------------------------------------------------------------- */
     472           1 :     OGRStyleTool *poTool = nullptr;
     473           2 :     OGRStyleMgr oSM;
     474             : 
     475           1 :     if (poFeature->GetStyleString() != nullptr)
     476             :     {
     477           1 :         oSM.InitFromFeature(poFeature);
     478             : 
     479           1 :         if (oSM.GetPartCount() > 0)
     480           1 :             poTool = oSM.GetPart(0);
     481             :     }
     482             : 
     483             :     /* ==================================================================== */
     484             :     /*      Process the LABEL tool.                                         */
     485             :     /* ==================================================================== */
     486           1 :     double dfDx = 0.0;
     487           1 :     double dfDy = 0.0;
     488             : 
     489           1 :     if (poTool && poTool->GetType() == OGRSTCLabel)
     490             :     {
     491           1 :         OGRStyleLabel *poLabel = (OGRStyleLabel *)poTool;
     492             :         GBool bDefault;
     493             : 
     494             :         /* --------------------------------------------------------------------
     495             :          */
     496             :         /*      Color */
     497             :         /* --------------------------------------------------------------------
     498             :          */
     499           1 :         if (poLabel->ForeColor(bDefault) != nullptr && !bDefault)
     500           1 :             WriteValue(62, ColorStringToDXFColor(poLabel->ForeColor(bDefault)));
     501             : 
     502             :         /* --------------------------------------------------------------------
     503             :          */
     504             :         /*      Angle */
     505             :         /* --------------------------------------------------------------------
     506             :          */
     507           1 :         const double dfAngle = poLabel->Angle(bDefault);
     508             : 
     509           1 :         if (!bDefault)
     510           1 :             WriteValue(50, dfAngle);
     511             : 
     512             :         /* --------------------------------------------------------------------
     513             :          */
     514             :         /*      Height - We need to fetch this in georeferenced units - I'm */
     515             :         /*      doubt the default translation mechanism will be much good. */
     516             :         /* --------------------------------------------------------------------
     517             :          */
     518           1 :         poTool->SetUnit(OGRSTUGround);
     519           1 :         const double dfHeight = poLabel->Size(bDefault);
     520             : 
     521           1 :         if (!bDefault)
     522           1 :             WriteValue(40, dfHeight);
     523             : 
     524             :         /* --------------------------------------------------------------------
     525             :          */
     526             :         /*      Anchor / Attachment Point */
     527             :         /* --------------------------------------------------------------------
     528             :          */
     529           1 :         const int nAnchor = poLabel->Anchor(bDefault);
     530             : 
     531           1 :         if (!bDefault)
     532             :         {
     533             :             const static int anAnchorMap[] = {-1, 7, 8, 9, 4, 5, 6,
     534             :                                               1,  2, 3, 7, 8, 9};
     535             : 
     536           0 :             if (nAnchor > 0 && nAnchor < 13)
     537           0 :                 WriteValue(71, anAnchorMap[nAnchor]);
     538             :         }
     539             : 
     540             :         /* --------------------------------------------------------------------
     541             :          */
     542             :         /*      Offset */
     543             :         /* --------------------------------------------------------------------
     544             :          */
     545           1 :         dfDx = poLabel->SpacingX(bDefault);
     546           1 :         dfDy = poLabel->SpacingY(bDefault);
     547             : 
     548             :         /* --------------------------------------------------------------------
     549             :          */
     550             :         /*      Escape the text, and convert to ISO8859. */
     551             :         /* --------------------------------------------------------------------
     552             :          */
     553           1 :         const char *pszText = poLabel->TextString(bDefault);
     554             : 
     555           1 :         if (pszText != nullptr && !bDefault)
     556             :         {
     557           2 :             CPLString osEscaped = TextEscape(pszText);
     558           1 :             while (osEscaped.size() > 250)
     559             :             {
     560           0 :                 WriteValue(3, osEscaped.substr(0, 250).c_str());
     561           0 :                 osEscaped.erase(0, 250);
     562             :             }
     563           1 :             WriteValue(1, osEscaped);
     564             :         }
     565             : 
     566             :         /* --------------------------------------------------------------------
     567             :          */
     568             :         /*      Store the text style in the map. */
     569             :         /* --------------------------------------------------------------------
     570             :          */
     571             :         std::map<CPLString, CPLString> oTextStyleDef =
     572           2 :             PrepareTextStyleDefinition(poLabel);
     573           2 :         CPLString osStyleName;
     574             : 
     575           1 :         for (const auto &oPair : oNewTextStyles)
     576             :         {
     577           0 :             if (oPair.second == oTextStyleDef)
     578             :             {
     579           0 :                 osStyleName = oPair.first;
     580           0 :                 break;
     581             :             }
     582             :         }
     583             : 
     584           1 :         if (osStyleName == "")
     585             :         {
     586             : 
     587           0 :             do
     588             :             {
     589           1 :                 osStyleName.Printf("AutoTextStyle-%d", nNextAutoID++);
     590           1 :             } while (poDS->oHeaderDS.TextStyleExists(osStyleName));
     591             : 
     592           1 :             oNewTextStyles[osStyleName] = std::move(oTextStyleDef);
     593             :         }
     594             : 
     595           1 :         WriteValue(7, osStyleName);
     596             :     }
     597             : 
     598           1 :     delete poTool;
     599             : 
     600             :     /* -------------------------------------------------------------------- */
     601             :     /*      Write the location.                                             */
     602             :     /* -------------------------------------------------------------------- */
     603           1 :     OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     604             : 
     605           1 :     WriteValue(10, poPoint->getX() + dfDx);
     606           1 :     if (!WriteValue(20, poPoint->getY() + dfDy))
     607           0 :         return OGRERR_FAILURE;
     608             : 
     609           1 :     if (poPoint->getGeometryType() == wkbPoint25D)
     610             :     {
     611           1 :         if (!WriteValue(30, poPoint->getZ()))
     612           0 :             return OGRERR_FAILURE;
     613             :     }
     614             : 
     615           1 :     return OGRERR_NONE;
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                     PrepareLineTypeDefinition()                      */
     620             : /************************************************************************/
     621             : std::vector<double>
     622           5 : OGRDXFWriterLayer::PrepareLineTypeDefinition(OGRStylePen *poPen)
     623             : {
     624             : 
     625             :     /* -------------------------------------------------------------------- */
     626             :     /*      Fetch pattern.                                                  */
     627             :     /* -------------------------------------------------------------------- */
     628             :     GBool bDefault;
     629           5 :     const char *pszPattern = poPen->Pattern(bDefault);
     630             : 
     631           5 :     if (bDefault || strlen(pszPattern) == 0)
     632           0 :         return std::vector<double>();
     633             : 
     634             :     /* -------------------------------------------------------------------- */
     635             :     /*      Split into pen up / pen down bits.                              */
     636             :     /* -------------------------------------------------------------------- */
     637           5 :     char **papszTokens = CSLTokenizeString(pszPattern);
     638          10 :     std::vector<double> adfWeightTokens;
     639             : 
     640          15 :     for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
     641             :     {
     642          10 :         const char *pszToken = papszTokens[i];
     643          20 :         CPLString osAmount;
     644          20 :         CPLString osDXFEntry;
     645             : 
     646             :         // Split amount and unit.
     647          10 :         const char *pszUnit = pszToken;  // Used after for.
     648          48 :         for (; strchr("0123456789.", *pszUnit) != nullptr; pszUnit++)
     649             :         {
     650             :         }
     651             : 
     652          10 :         osAmount.assign(pszToken, (int)(pszUnit - pszToken));
     653             : 
     654             :         // If the unit is other than 'g' we really should be trying to
     655             :         // do some type of transformation - but what to do?  Pretty hard.
     656             : 
     657             :         // Even entries are "pen down" represented as positive in DXF.
     658             :         // "Pen up" entries (gaps) are represented as negative.
     659          10 :         if (i % 2 == 0)
     660           5 :             adfWeightTokens.push_back(CPLAtof(osAmount));
     661             :         else
     662           5 :             adfWeightTokens.push_back(-CPLAtof(osAmount));
     663             :     }
     664             : 
     665           5 :     CSLDestroy(papszTokens);
     666             : 
     667           5 :     return adfWeightTokens;
     668             : }
     669             : 
     670             : /************************************************************************/
     671             : /*                       IsLineTypeProportional()                       */
     672             : /************************************************************************/
     673             : 
     674           7 : static double IsLineTypeProportional(const std::vector<double> &adfA,
     675             :                                      const std::vector<double> &adfB)
     676             : {
     677             :     // If they are not the same length, they are not the same linetype
     678           7 :     if (adfA.size() != adfB.size())
     679           0 :         return 0.0;
     680             : 
     681             :     // Determine the proportion of the first elements
     682           7 :     const double dfRatio = (adfA[0] != 0.0) ? (adfB[0] / adfA[0]) : 0.0;
     683             : 
     684             :     // Check if all elements follow this proportionality
     685           9 :     for (size_t iIndex = 1; iIndex < adfA.size(); iIndex++)
     686           7 :         if (fabs(adfB[iIndex] - (adfA[iIndex] * dfRatio)) > 1e-6)
     687           5 :             return 0.0;
     688             : 
     689           2 :     return dfRatio;
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                           WritePOLYLINE()                            */
     694             : /************************************************************************/
     695             : 
     696          30 : OGRErr OGRDXFWriterLayer::WritePOLYLINE(OGRFeature *poFeature,
     697             :                                         const OGRGeometry *poGeom)
     698             : 
     699             : {
     700             :     /* -------------------------------------------------------------------- */
     701             :     /*      For now we handle multilinestrings by writing a series of       */
     702             :     /*      entities.                                                       */
     703             :     /* -------------------------------------------------------------------- */
     704          30 :     if (poGeom == nullptr)
     705          26 :         poGeom = poFeature->GetGeometryRef();
     706             : 
     707          30 :     if (poGeom->IsEmpty())
     708             :     {
     709           0 :         return OGRERR_NONE;
     710             :     }
     711             : 
     712          60 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
     713          30 :         wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
     714             :     {
     715           4 :         const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
     716           4 :         OGRErr eErr = OGRERR_NONE;
     717           8 :         for (auto &&poMember : *poGC)
     718             :         {
     719           4 :             eErr = WritePOLYLINE(poFeature, poMember);
     720           4 :             if (eErr != OGRERR_NONE)
     721           0 :                 break;
     722             :         }
     723             : 
     724           4 :         return eErr;
     725             :     }
     726             : 
     727             :     /* -------------------------------------------------------------------- */
     728             :     /*      Polygons are written with on entity per ring.                   */
     729             :     /* -------------------------------------------------------------------- */
     730          52 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
     731          26 :         wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
     732             :     {
     733           0 :         const OGRPolygon *poPoly = poGeom->toPolygon();
     734           0 :         OGRErr eErr = OGRERR_NONE;
     735           0 :         for (auto &&poRing : *poPoly)
     736             :         {
     737           0 :             eErr = WritePOLYLINE(poFeature, poRing);
     738           0 :             if (eErr != OGRERR_NONE)
     739           0 :                 break;
     740             :         }
     741             : 
     742           0 :         return eErr;
     743             :     }
     744             : 
     745             :     /* -------------------------------------------------------------------- */
     746             :     /*      Do we now have a geometry we can work with?                     */
     747             :     /* -------------------------------------------------------------------- */
     748          26 :     if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
     749           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     750             : 
     751          26 :     const OGRLineString *poLS = poGeom->toLineString();
     752             : 
     753             :     /* -------------------------------------------------------------------- */
     754             :     /*      Write as a lightweight polygon,                                 */
     755             :     /*       or as POLYLINE if the line contains different heights          */
     756             :     /* -------------------------------------------------------------------- */
     757          26 :     int bHasDifferentZ = FALSE;
     758          26 :     if (poLS->getGeometryType() == wkbLineString25D)
     759             :     {
     760           8 :         double z0 = poLS->getZ(0);
     761          23 :         for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
     762             :         {
     763          16 :             if (z0 != poLS->getZ(iVert))
     764             :             {
     765           1 :                 bHasDifferentZ = TRUE;
     766           1 :                 break;
     767             :             }
     768             :         }
     769             :     }
     770             : 
     771          26 :     WriteValue(0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE");
     772          26 :     WriteCore(poFeature);
     773          26 :     if (bHasDifferentZ)
     774             :     {
     775           1 :         WriteValue(100, "AcDb3dPolyline");
     776           1 :         WriteValue(10, 0.0);
     777           1 :         WriteValue(20, 0.0);
     778           1 :         WriteValue(30, 0.0);
     779             :     }
     780             :     else
     781          25 :         WriteValue(100, "AcDbPolyline");
     782          26 :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
     783           0 :         WriteValue(70, 1 + (bHasDifferentZ ? 8 : 0));
     784             :     else
     785          26 :         WriteValue(70, 0 + (bHasDifferentZ ? 8 : 0));
     786          26 :     if (!bHasDifferentZ)
     787          25 :         WriteValue(90, poLS->getNumPoints());
     788             :     else
     789           1 :         WriteValue(66, "1");  // Vertex Flag
     790             : 
     791             :     /* -------------------------------------------------------------------- */
     792             :     /*      Do we have styling information?                                 */
     793             :     /* -------------------------------------------------------------------- */
     794          26 :     OGRStyleTool *poTool = nullptr;
     795          52 :     OGRStyleMgr oSM;
     796             : 
     797          26 :     if (poFeature->GetStyleString() != nullptr)
     798             :     {
     799           5 :         oSM.InitFromFeature(poFeature);
     800             : 
     801           5 :         if (oSM.GetPartCount() > 0)
     802           5 :             poTool = oSM.GetPart(0);
     803             :     }
     804             : 
     805             :     /* -------------------------------------------------------------------- */
     806             :     /*      Handle a PEN tool to control drawing color and width.           */
     807             :     /*      Perhaps one day also dottedness, etc.                           */
     808             :     /* -------------------------------------------------------------------- */
     809          26 :     if (poTool && poTool->GetType() == OGRSTCPen)
     810             :     {
     811           5 :         OGRStylePen *poPen = (OGRStylePen *)poTool;
     812             :         GBool bDefault;
     813             : 
     814           5 :         if (poPen->Color(bDefault) != nullptr && !bDefault)
     815           5 :             WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
     816             : 
     817             :         // we want to fetch the width in ground units.
     818           5 :         poPen->SetUnit(OGRSTUGround, 1.0);
     819           5 :         const double dfWidth = poPen->Width(bDefault);
     820             : 
     821           5 :         if (!bDefault)
     822           5 :             WriteValue(370, (int)floor(dfWidth * 100 + 0.5));
     823             :     }
     824             : 
     825             :     /* -------------------------------------------------------------------- */
     826             :     /*      Do we have a Linetype for the feature?                          */
     827             :     /* -------------------------------------------------------------------- */
     828          52 :     CPLString osLineType = poFeature->GetFieldAsString("Linetype");
     829          26 :     double dfLineTypeScale = 0.0;
     830             : 
     831          26 :     bool bGotLinetype = false;
     832             : 
     833          26 :     if (!osLineType.empty())
     834             :     {
     835             :         std::vector<double> adfLineType =
     836           4 :             poDS->oHeaderDS.LookupLineType(osLineType);
     837             : 
     838           2 :         if (adfLineType.empty() && oNewLineTypes.count(osLineType) > 0)
     839           0 :             adfLineType = oNewLineTypes[osLineType];
     840             : 
     841           2 :         if (!adfLineType.empty())
     842             :         {
     843           1 :             bGotLinetype = true;
     844           1 :             WriteValue(6, osLineType);
     845             : 
     846             :             // If the given linetype is proportional to the linetype data
     847             :             // in the style string, then apply a linetype scale
     848           1 :             if (poTool != nullptr && poTool->GetType() == OGRSTCPen)
     849             :             {
     850             :                 std::vector<double> adfDefinition = PrepareLineTypeDefinition(
     851           2 :                     static_cast<OGRStylePen *>(poTool));
     852             : 
     853           1 :                 if (!adfDefinition.empty())
     854             :                 {
     855             :                     dfLineTypeScale =
     856           1 :                         IsLineTypeProportional(adfLineType, adfDefinition);
     857             : 
     858           1 :                     if (dfLineTypeScale != 0.0 &&
     859           0 :                         fabs(dfLineTypeScale - 1.0) > 1e-4)
     860             :                     {
     861           0 :                         WriteValue(48, dfLineTypeScale);
     862             :                     }
     863             :                 }
     864             :             }
     865             :         }
     866             :     }
     867             : 
     868          26 :     if (!bGotLinetype && poTool != nullptr && poTool->GetType() == OGRSTCPen)
     869             :     {
     870             :         std::vector<double> adfDefinition =
     871           8 :             PrepareLineTypeDefinition(static_cast<OGRStylePen *>(poTool));
     872             : 
     873           4 :         if (!adfDefinition.empty())
     874             :         {
     875             :             // Is this definition already created and named?
     876           7 :             for (const auto &oPair : poDS->oHeaderDS.GetLineTypeTable())
     877             :             {
     878             :                 dfLineTypeScale =
     879           4 :                     IsLineTypeProportional(oPair.second, adfDefinition);
     880           4 :                 if (dfLineTypeScale != 0.0)
     881             :                 {
     882           1 :                     osLineType = oPair.first;
     883           1 :                     break;
     884             :                 }
     885             :             }
     886             : 
     887           4 :             if (dfLineTypeScale == 0.0)
     888             :             {
     889           4 :                 for (const auto &oPair : oNewLineTypes)
     890             :                 {
     891             :                     dfLineTypeScale =
     892           2 :                         IsLineTypeProportional(oPair.second, adfDefinition);
     893           2 :                     if (dfLineTypeScale != 0.0)
     894             :                     {
     895           1 :                         osLineType = oPair.first;
     896           1 :                         break;
     897             :                     }
     898             :                 }
     899             :             }
     900             : 
     901             :             // If not, create an automatic name for it.
     902           4 :             if (osLineType == "")
     903             :             {
     904           1 :                 dfLineTypeScale = 1.0;
     905           0 :                 do
     906             :                 {
     907           1 :                     osLineType.Printf("AutoLineType-%d", nNextAutoID++);
     908           1 :                 } while (poDS->oHeaderDS.LookupLineType(osLineType).size() > 0);
     909             :             }
     910             : 
     911             :             // If it isn't already defined, add it now.
     912           7 :             if (poDS->oHeaderDS.LookupLineType(osLineType).empty() &&
     913           3 :                 oNewLineTypes.count(osLineType) == 0)
     914             :             {
     915           2 :                 oNewLineTypes[osLineType] = std::move(adfDefinition);
     916             :             }
     917             : 
     918           4 :             WriteValue(6, osLineType);
     919             : 
     920           4 :             if (dfLineTypeScale != 0.0 && fabs(dfLineTypeScale - 1.0) > 1e-4)
     921             :             {
     922           2 :                 WriteValue(48, dfLineTypeScale);
     923             :             }
     924             :         }
     925             :     }
     926             : 
     927             :     /* -------------------------------------------------------------------- */
     928             :     /*      Write the vertices                                              */
     929             :     /* -------------------------------------------------------------------- */
     930             : 
     931          26 :     if (!bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D)
     932             :     {
     933             :         // if LWPOLYLINE with Z write it only once
     934           7 :         if (!WriteValue(38, poLS->getZ(0)))
     935           0 :             return OGRERR_FAILURE;
     936             :     }
     937             : 
     938          78 :     for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
     939             :     {
     940          52 :         if (bHasDifferentZ)
     941             :         {
     942           2 :             WriteValue(0, "VERTEX");
     943           2 :             WriteCore(poFeature);
     944           2 :             WriteValue(100, "AcDbVertex");
     945           2 :             WriteValue(100, "AcDb3dPolylineVertex");
     946             :         }
     947          52 :         WriteValue(10, poLS->getX(iVert));
     948          52 :         if (!WriteValue(20, poLS->getY(iVert)))
     949           0 :             return OGRERR_FAILURE;
     950             : 
     951          52 :         if (bHasDifferentZ)
     952             :         {
     953           2 :             if (!WriteValue(30, poLS->getZ(iVert)))
     954           0 :                 return OGRERR_FAILURE;
     955           2 :             WriteValue(70, 32);
     956             :         }
     957             :     }
     958             : 
     959          26 :     if (bHasDifferentZ)
     960             :     {
     961           1 :         WriteValue(0, "SEQEND");
     962           1 :         WriteCore(poFeature);
     963             :     }
     964             : 
     965          26 :     delete poTool;
     966             : 
     967          26 :     return OGRERR_NONE;
     968             : 
     969             : #ifdef notdef
     970             :     /* -------------------------------------------------------------------- */
     971             :     /*      Alternate unmaintained implementation as a polyline entity.     */
     972             :     /* -------------------------------------------------------------------- */
     973             :     WriteValue(0, "POLYLINE");
     974             :     WriteCore(poFeature);
     975             :     WriteValue(100, "AcDbPolyline");
     976             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
     977             :         WriteValue(70, 1);
     978             :     else
     979             :         WriteValue(70, 0);
     980             :     WriteValue(66, "1");
     981             : 
     982             :     for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
     983             :     {
     984             :         WriteValue(0, "VERTEX");
     985             :         WriteValue(8, "0");
     986             :         WriteValue(10, poLS->getX(iVert));
     987             :         if (!WriteValue(20, poLS->getY(iVert)))
     988             :             return OGRERR_FAILURE;
     989             : 
     990             :         if (poLS->getGeometryType() == wkbLineString25D)
     991             :         {
     992             :             if (!WriteValue(30, poLS->getZ(iVert)))
     993             :                 return OGRERR_FAILURE;
     994             :         }
     995             :     }
     996             : 
     997             :     WriteValue(0, "SEQEND");
     998             :     WriteValue(8, "0");
     999             : 
    1000             :     return OGRERR_NONE;
    1001             : #endif
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                             WriteHATCH()                             */
    1006             : /************************************************************************/
    1007             : 
    1008          23 : OGRErr OGRDXFWriterLayer::WriteHATCH(OGRFeature *poFeature, OGRGeometry *poGeom)
    1009             : 
    1010             : {
    1011             :     /* -------------------------------------------------------------------- */
    1012             :     /*      For now we handle multipolygons by writing a series of          */
    1013             :     /*      entities.                                                       */
    1014             :     /* -------------------------------------------------------------------- */
    1015          23 :     if (poGeom == nullptr)
    1016          19 :         poGeom = poFeature->GetGeometryRef();
    1017             : 
    1018          23 :     if (poGeom->IsEmpty())
    1019             :     {
    1020           0 :         return OGRERR_NONE;
    1021             :     }
    1022             : 
    1023          23 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
    1024             :     {
    1025           4 :         OGRErr eErr = OGRERR_NONE;
    1026           8 :         for (auto &&poMember : poGeom->toMultiPolygon())
    1027             :         {
    1028           4 :             eErr = WriteHATCH(poFeature, poMember);
    1029           4 :             if (eErr != OGRERR_NONE)
    1030           0 :                 break;
    1031             :         }
    1032             : 
    1033           4 :         return eErr;
    1034             :     }
    1035             : 
    1036             :     /* -------------------------------------------------------------------- */
    1037             :     /*      Do we now have a geometry we can work with?                     */
    1038             :     /* -------------------------------------------------------------------- */
    1039          20 :     if (wkbFlatten(poGeom->getGeometryType()) != wkbPolygon &&
    1040           1 :         wkbFlatten(poGeom->getGeometryType()) != wkbTriangle)
    1041             :     {
    1042           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    1043             :     }
    1044             : 
    1045             :     /* -------------------------------------------------------------------- */
    1046             :     /*      Write as a hatch.                                               */
    1047             :     /* -------------------------------------------------------------------- */
    1048          19 :     WriteValue(0, "HATCH");
    1049          19 :     WriteCore(poFeature);
    1050          19 :     WriteValue(100, "AcDbHatch");
    1051             : 
    1052             :     // Figure out "average" elevation
    1053          19 :     OGREnvelope3D oEnv;
    1054          19 :     poGeom->getEnvelope(&oEnv);
    1055          19 :     WriteValue(10, 0);  // elevation point X = 0
    1056          19 :     WriteValue(20, 0);  // elevation point Y = 0
    1057             :     // elevation point Z = constant elevation
    1058          19 :     WriteValue(30, oEnv.MinZ + (oEnv.MaxZ - oEnv.MinZ) / 2);
    1059             : 
    1060          19 :     WriteValue(210, 0);    // extrusion direction X
    1061          19 :     WriteValue(220, 0);    // extrusion direction Y
    1062          19 :     WriteValue(230, 1.0);  // extrusion direction Z
    1063             : 
    1064          19 :     WriteValue(2, "SOLID");  // fill pattern
    1065          19 :     WriteValue(70, 1);       // solid fill
    1066          19 :     WriteValue(71, 0);       // associativity
    1067             : 
    1068             :     /* -------------------------------------------------------------------- */
    1069             :     /*      Do we have styling information?                                 */
    1070             :     /* -------------------------------------------------------------------- */
    1071          19 :     OGRStyleTool *poTool = nullptr;
    1072          19 :     OGRStyleMgr oSM;
    1073             : 
    1074          19 :     if (poFeature->GetStyleString() != nullptr)
    1075             :     {
    1076           1 :         oSM.InitFromFeature(poFeature);
    1077             : 
    1078           1 :         if (oSM.GetPartCount() > 0)
    1079           1 :             poTool = oSM.GetPart(0);
    1080             :     }
    1081             :     // Write style brush fore color
    1082          19 :     if (poTool && poTool->GetType() == OGRSTCBrush)
    1083             :     {
    1084           1 :         OGRStyleBrush *poBrush = (OGRStyleBrush *)poTool;
    1085             :         GBool bDefault;
    1086             : 
    1087           1 :         if (poBrush->ForeColor(bDefault) != nullptr && !bDefault)
    1088           1 :             WriteValue(62, ColorStringToDXFColor(poBrush->ForeColor(bDefault)));
    1089             :     }
    1090          19 :     delete poTool;
    1091             : 
    1092             : /* -------------------------------------------------------------------- */
    1093             : /*      Handle a PEN tool to control drawing color and width.           */
    1094             : /*      Perhaps one day also dottedness, etc.                           */
    1095             : /* -------------------------------------------------------------------- */
    1096             : #ifdef notdef
    1097             :     if (poTool && poTool->GetType() == OGRSTCPen)
    1098             :     {
    1099             :         OGRStylePen *poPen = (OGRStylePen *)poTool;
    1100             :         GBool bDefault;
    1101             : 
    1102             :         if (poPen->Color(bDefault) != NULL && !bDefault)
    1103             :             WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
    1104             : 
    1105             :         double dfWidthInMM = poPen->Width(bDefault);
    1106             : 
    1107             :         if (!bDefault)
    1108             :             WriteValue(370, (int)floor(dfWidthInMM * 100 + 0.5));
    1109             :     }
    1110             : 
    1111             :     /* -------------------------------------------------------------------- */
    1112             :     /*      Do we have a Linetype for the feature?                          */
    1113             :     /* -------------------------------------------------------------------- */
    1114             :     CPLString osLineType = poFeature->GetFieldAsString("Linetype");
    1115             : 
    1116             :     if (!osLineType.empty() &&
    1117             :         (poDS->oHeaderDS.LookupLineType(osLineType) != NULL ||
    1118             :          oNewLineTypes.count(osLineType) > 0))
    1119             :     {
    1120             :         // Already define -> just reference it.
    1121             :         WriteValue(6, osLineType);
    1122             :     }
    1123             :     else if (poTool != NULL && poTool->GetType() == OGRSTCPen)
    1124             :     {
    1125             :         CPLString osDefinition = PrepareLineTypeDefinition(poFeature, poTool);
    1126             : 
    1127             :         if (osDefinition != "" && osLineType == "")
    1128             :         {
    1129             :             // Is this definition already created and named?
    1130             :             std::map<CPLString, CPLString>::iterator it;
    1131             : 
    1132             :             for (it = oNewLineTypes.begin(); it != oNewLineTypes.end(); it++)
    1133             :             {
    1134             :                 if ((*it).second == osDefinition)
    1135             :                 {
    1136             :                     osLineType = (*it).first;
    1137             :                     break;
    1138             :                 }
    1139             :             }
    1140             : 
    1141             :             // create an automatic name for it.
    1142             :             if (osLineType == "")
    1143             :             {
    1144             :                 do
    1145             :                 {
    1146             :                     osLineType.Printf("AutoLineType-%d", nNextAutoID++);
    1147             :                 } while (poDS->oHeaderDS.LookupLineType(osLineType) != NULL);
    1148             :             }
    1149             :         }
    1150             : 
    1151             :         // If it isn't already defined, add it now.
    1152             :         if (osDefinition != "" && oNewLineTypes.count(osLineType) == 0)
    1153             :         {
    1154             :             oNewLineTypes[osLineType] = osDefinition;
    1155             :             WriteValue(6, osLineType);
    1156             :         }
    1157             :     }
    1158             :     delete poTool;
    1159             : #endif
    1160             : 
    1161             :     /* -------------------------------------------------------------------- */
    1162             :     /*      Process the loops (rings).                                      */
    1163             :     /* -------------------------------------------------------------------- */
    1164          19 :     OGRPolygon *poPoly = poGeom->toPolygon();
    1165             : 
    1166          19 :     WriteValue(91, poPoly->getNumInteriorRings() + 1);
    1167             : 
    1168          39 :     for (auto &&poLR : *poPoly)
    1169             :     {
    1170          20 :         WriteValue(92, 2);  // Polyline
    1171          20 :         WriteValue(72, 0);  // has bulge
    1172          20 :         WriteValue(73, 1);  // is closed
    1173          20 :         WriteValue(93, poLR->getNumPoints());
    1174             : 
    1175         116 :         for (int iVert = 0; iVert < poLR->getNumPoints(); iVert++)
    1176             :         {
    1177          96 :             WriteValue(10, poLR->getX(iVert));
    1178          96 :             WriteValue(20, poLR->getY(iVert));
    1179             :         }
    1180             : 
    1181          20 :         WriteValue(97, 0);  // 0 source boundary objects
    1182             :     }
    1183             : 
    1184          19 :     WriteValue(75, 0);  // hatch style = Hatch "odd parity" area (Normal style)
    1185          19 :     WriteValue(76, 1);  // hatch pattern type = predefined
    1186          19 :     WriteValue(98, 0);  // 0 seed points
    1187             : 
    1188          19 :     return OGRERR_NONE;
    1189             : 
    1190             : #ifdef notdef
    1191             :     /* -------------------------------------------------------------------- */
    1192             :     /*      Alternate unmaintained implementation as a polyline entity.     */
    1193             :     /* -------------------------------------------------------------------- */
    1194             :     WriteValue(0, "POLYLINE");
    1195             :     WriteCore(poFeature);
    1196             :     WriteValue(100, "AcDbPolyline");
    1197             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    1198             :         WriteValue(70, 1);
    1199             :     else
    1200             :         WriteValue(70, 0);
    1201             :     WriteValue(66, "1");
    1202             : 
    1203             :     for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
    1204             :     {
    1205             :         WriteValue(0, "VERTEX");
    1206             :         WriteValue(8, "0");
    1207             :         WriteValue(10, poLS->getX(iVert));
    1208             :         if (!WriteValue(20, poLS->getY(iVert)))
    1209             :             return OGRERR_FAILURE;
    1210             : 
    1211             :         if (poLS->getGeometryType() == wkbLineString25D)
    1212             :         {
    1213             :             if (!WriteValue(30, poLS->getZ(iVert)))
    1214             :                 return OGRERR_FAILURE;
    1215             :         }
    1216             :     }
    1217             : 
    1218             :     WriteValue(0, "SEQEND");
    1219             :     WriteValue(8, "0");
    1220             : 
    1221             :     return OGRERR_NONE;
    1222             : #endif
    1223             : }
    1224             : 
    1225             : /************************************************************************/
    1226             : /*                           ICreateFeature()                            */
    1227             : /************************************************************************/
    1228             : 
    1229         210 : OGRErr OGRDXFWriterLayer::ICreateFeature(OGRFeature *poFeature)
    1230             : 
    1231             : {
    1232         210 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1233         210 :     OGRwkbGeometryType eGType = wkbNone;
    1234             : 
    1235         210 :     if (poGeom != nullptr)
    1236             :     {
    1237         177 :         if (!poGeom->IsEmpty())
    1238             :         {
    1239         175 :             OGREnvelope sEnvelope;
    1240         175 :             poGeom->getEnvelope(&sEnvelope);
    1241         175 :             poDS->UpdateExtent(&sEnvelope);
    1242             :         }
    1243         177 :         eGType = wkbFlatten(poGeom->getGeometryType());
    1244             :     }
    1245             : 
    1246         210 :     if (eGType == wkbPoint)
    1247             :     {
    1248         119 :         const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
    1249             : 
    1250             :         // We don't want to treat as a blocks ref if the block is not defined
    1251         238 :         if (pszBlockName &&
    1252         119 :             poDS->oHeaderDS.LookupBlock(pszBlockName) == nullptr)
    1253             :         {
    1254         124 :             if (poDS->poBlocksLayer == nullptr ||
    1255           7 :                 poDS->poBlocksLayer->FindBlock(pszBlockName) == nullptr)
    1256         112 :                 pszBlockName = nullptr;
    1257             :         }
    1258             : 
    1259         119 :         if (pszBlockName != nullptr)
    1260           7 :             return WriteINSERT(poFeature);
    1261             : 
    1262         113 :         else if (poFeature->GetStyleString() != nullptr &&
    1263           1 :                  STARTS_WITH_CI(poFeature->GetStyleString(), "LABEL"))
    1264           1 :             return WriteTEXT(poFeature);
    1265             :         else
    1266         111 :             return WritePOINT(poFeature);
    1267             :     }
    1268          91 :     else if (eGType == wkbLineString || eGType == wkbMultiLineString)
    1269          26 :         return WritePOLYLINE(poFeature);
    1270             : 
    1271          65 :     else if (eGType == wkbPolygon || eGType == wkbTriangle ||
    1272             :              eGType == wkbMultiPolygon)
    1273             :     {
    1274          19 :         if (bWriteHatch)
    1275          19 :             return WriteHATCH(poFeature);
    1276             :         else
    1277           0 :             return WritePOLYLINE(poFeature);
    1278             :     }
    1279             : 
    1280             :     // Explode geometry collections into multiple entities.
    1281          46 :     else if (eGType == wkbGeometryCollection)
    1282             :     {
    1283             :         OGRGeometryCollection *poGC =
    1284           8 :             poFeature->StealGeometry()->toGeometryCollection();
    1285          26 :         for (auto &&poMember : poGC)
    1286             :         {
    1287          19 :             poFeature->SetGeometry(poMember);
    1288             : 
    1289          19 :             OGRErr eErr = CreateFeature(poFeature);
    1290             : 
    1291          19 :             if (eErr != OGRERR_NONE)
    1292             :             {
    1293           1 :                 delete poGC;
    1294           1 :                 return eErr;
    1295             :             }
    1296             :         }
    1297             : 
    1298           7 :         poFeature->SetGeometryDirectly(poGC);
    1299           7 :         return OGRERR_NONE;
    1300             :     }
    1301             :     else
    1302             :     {
    1303          38 :         CPLError(CE_Failure, CPLE_AppDefined,
    1304             :                  "No known way to write feature with geometry '%s'.",
    1305             :                  OGRGeometryTypeToName(eGType));
    1306          38 :         return OGRERR_FAILURE;
    1307             :     }
    1308             : }
    1309             : 
    1310             : /************************************************************************/
    1311             : /*                       ColorStringToDXFColor()                        */
    1312             : /************************************************************************/
    1313             : 
    1314           7 : int OGRDXFWriterLayer::ColorStringToDXFColor(const char *pszRGB)
    1315             : 
    1316             : {
    1317             :     /* -------------------------------------------------------------------- */
    1318             :     /*      Parse the RGB string.                                           */
    1319             :     /* -------------------------------------------------------------------- */
    1320           7 :     if (pszRGB == nullptr)
    1321           0 :         return -1;
    1322             : 
    1323           7 :     unsigned int nRed = 0;
    1324           7 :     unsigned int nGreen = 0;
    1325           7 :     unsigned int nBlue = 0;
    1326           7 :     unsigned int nTransparency = 255;
    1327             : 
    1328             :     const int nCount =
    1329           7 :         sscanf(pszRGB, "#%2x%2x%2x%2x", &nRed, &nGreen, &nBlue, &nTransparency);
    1330             : 
    1331           7 :     if (nCount < 3)
    1332           0 :         return -1;
    1333             : 
    1334             :     /* -------------------------------------------------------------------- */
    1335             :     /*      Find near color in DXF palette.                                 */
    1336             :     /* -------------------------------------------------------------------- */
    1337           7 :     const unsigned char *pabyDXFColors = ACGetColorTable();
    1338           7 :     int nMinDist = 768;
    1339           7 :     int nBestColor = -1;
    1340             : 
    1341        1792 :     for (int i = 1; i < 256; i++)
    1342             :     {
    1343        1785 :         const int nDist =
    1344        1785 :             std::abs(static_cast<int>(nRed) - pabyDXFColors[i * 3 + 0]) +
    1345        1785 :             std::abs(static_cast<int>(nGreen) - pabyDXFColors[i * 3 + 1]) +
    1346        1785 :             std::abs(static_cast<int>(nBlue) - pabyDXFColors[i * 3 + 2]);
    1347             : 
    1348        1785 :         if (nDist < nMinDist)
    1349             :         {
    1350          12 :             nBestColor = i;
    1351          12 :             nMinDist = nDist;
    1352             :         }
    1353             :     }
    1354             : 
    1355           7 :     return nBestColor;
    1356             : }
    1357             : 
    1358             : /************************************************************************/
    1359             : /*                             GetDataset()                             */
    1360             : /************************************************************************/
    1361             : 
    1362          17 : GDALDataset *OGRDXFWriterLayer::GetDataset()
    1363             : {
    1364          17 :     return poDS;
    1365             : }

Generated by: LCOV version 1.14