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

Generated by: LCOV version 1.14