LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1734 1895 91.5 %
Date: 2025-08-01 10:10:57 Functions: 41 41 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements OGRDXFLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2017-2020, Alan Thomas <alant@outlook.com.au>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_dxf.h"
      16             : #include "cpl_conv.h"
      17             : #include "ogrdxf_polyline_smooth.h"
      18             : #include "ogr_api.h"
      19             : 
      20             : #include <cmath>
      21             : #include <algorithm>
      22             : #include <limits>
      23             : #include <stdexcept>
      24             : #include <memory>
      25             : 
      26             : /************************************************************************/
      27             : /*                                push()                               */
      28             : /************************************************************************/
      29             : 
      30        4383 : void OGRDXFFeatureQueue::push(OGRDXFFeature *poFeature)
      31             : {
      32        4383 :     apoFeatures.push(poFeature);
      33        4383 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                                 pop()                                */
      37             : /************************************************************************/
      38             : 
      39        4383 : void OGRDXFFeatureQueue::pop()
      40             : {
      41        4383 :     CPLAssert(!apoFeatures.empty());
      42        4383 :     apoFeatures.pop();
      43        4383 : }
      44             : 
      45             : /************************************************************************/
      46             : /*                            OGRDXFLayer()                             */
      47             : /************************************************************************/
      48             : 
      49         238 : OGRDXFLayer::OGRDXFLayer(OGRDXFDataSource *poDSIn)
      50         238 :     : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("entities")), iNextFID(0)
      51             : {
      52         238 :     poFeatureDefn->Reference();
      53             : 
      54         238 :     int nModes = ODFM_None;
      55         238 :     if (!poDS->InlineBlocks())
      56          26 :         nModes |= ODFM_IncludeBlockFields;
      57         238 :     if (poDS->ShouldIncludeRawCodeValues())
      58           1 :         nModes |= ODFM_IncludeRawCodeValues;
      59         238 :     if (poDS->In3DExtensibleMode())
      60           1 :         nModes |= ODFM_Include3DModeFields;
      61         238 :     OGRDXFDataSource::AddStandardFields(poFeatureDefn, nModes);
      62             : 
      63         238 :     SetDescription(poFeatureDefn->GetName());
      64         238 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                           ~OGRDXFLayer()                           */
      68             : /************************************************************************/
      69             : 
      70         455 : OGRDXFLayer::~OGRDXFLayer()
      71             : 
      72             : {
      73         238 :     ClearPendingFeatures();
      74         238 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
      75             :     {
      76          92 :         CPLDebug("DXF", "%d features read on layer '%s'.", (int)m_nFeaturesRead,
      77          92 :                  poFeatureDefn->GetName());
      78             :     }
      79             : 
      80         238 :     if (poFeatureDefn)
      81         238 :         poFeatureDefn->Release();
      82         455 : }
      83             : 
      84             : /************************************************************************/
      85             : /*                        ClearPendingFeatures()                        */
      86             : /************************************************************************/
      87             : 
      88         509 : void OGRDXFLayer::ClearPendingFeatures()
      89             : 
      90             : {
      91         509 :     while (!apoPendingFeatures.empty())
      92             :     {
      93          55 :         OGRDXFFeature *poFeature = apoPendingFeatures.front();
      94          55 :         apoPendingFeatures.pop();
      95          55 :         delete poFeature;
      96             :     }
      97         454 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                            ResetReading()                            */
     101             : /************************************************************************/
     102             : 
     103         216 : void OGRDXFLayer::ResetReading()
     104             : 
     105             : {
     106         216 :     iNextFID = 0;
     107         216 :     ClearPendingFeatures();
     108         216 :     m_oInsertState.m_nRowCount = 0;
     109         216 :     m_oInsertState.m_nColumnCount = 0;
     110         216 :     poDS->RestartEntities();
     111         216 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                      TranslateGenericProperty()                      */
     115             : /*                                                                      */
     116             : /*      Try and convert entity properties handled similarly for most    */
     117             : /*      or all entity types.                                            */
     118             : /************************************************************************/
     119             : 
     120        7422 : void OGRDXFLayer::TranslateGenericProperty(OGRDXFFeature *poFeature, int nCode,
     121             :                                            char *pszValue)
     122             : 
     123             : {
     124        7422 :     switch (nCode)
     125             :     {
     126         818 :         case 8:
     127         818 :             poFeature->SetField("Layer", TextRecode(pszValue));
     128         818 :             break;
     129             : 
     130        1420 :         case 100:
     131             :         {
     132        2840 :             CPLString osSubClass = poFeature->GetFieldAsString("SubClasses");
     133        1420 :             if (!osSubClass.empty())
     134         766 :                 osSubClass += ":";
     135        1420 :             osSubClass += pszValue;
     136        1420 :             poFeature->SetField("SubClasses", osSubClass.c_str());
     137             :         }
     138        1420 :         break;
     139             : 
     140          38 :         case 101:
     141             :             // Embedded objects mark the end of meaningful DXF data
     142             :             // See
     143             :             // http://docs.autodesk.com/ACDMAC/2016/ENU/ObjectARX_Dev_Guide/files/GUID-C953866F-A335-4FFD-AE8C-256A76065552.htm
     144             :             {
     145             :                 char szLineBuf[257];
     146             :                 // Eat the rest of this entity
     147          38 :                 while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) >
     148             :                        0)
     149             :                 {
     150             :                 }
     151             : 
     152           2 :                 if (nCode < 0)
     153             :                 {
     154             :                     // Let the entity reader function discover this error for
     155             :                     // itself
     156           0 :                     return;
     157             :                 }
     158             : 
     159           2 :                 CPLAssert(nCode == 0);
     160           2 :                 poDS->UnreadValue();
     161             :             }
     162           2 :             break;
     163             : 
     164           5 :         case 60:
     165           5 :             if (atoi(pszValue))
     166           5 :                 poFeature->oStyleProperties["Hidden"] = "1";
     167           5 :             break;
     168             : 
     169          12 :         case 67:
     170          12 :             if (atoi(pszValue))
     171          12 :                 poFeature->SetField("PaperSpace", 1);
     172          12 :             break;
     173             : 
     174         390 :         case 62:
     175         390 :             poFeature->oStyleProperties["Color"] = pszValue;
     176         390 :             break;
     177             : 
     178          21 :         case 420:
     179          21 :             poFeature->oStyleProperties["TrueColor"] = pszValue;
     180          21 :             break;
     181             : 
     182          35 :         case 440:
     183          35 :             poFeature->oStyleProperties["Transparency"] = pszValue;
     184          35 :             break;
     185             : 
     186         311 :         case 6:
     187         311 :             poFeature->SetField("Linetype", TextRecode(pszValue));
     188         311 :             break;
     189             : 
     190           7 :         case 48:
     191           7 :             poFeature->oStyleProperties["LinetypeScale"] = pszValue;
     192           7 :             break;
     193             : 
     194         212 :         case 370:
     195             :         case 39:
     196         212 :             poFeature->oStyleProperties["LineWeight"] = pszValue;
     197         212 :             break;
     198             : 
     199         809 :         case 5:
     200         809 :             poFeature->SetField("EntityHandle", pszValue);
     201         809 :             break;
     202             : 
     203             :         // OCS vector.
     204         161 :         case 210:
     205         161 :             poFeature->oOCS.dfX = CPLAtof(pszValue);
     206         161 :             break;
     207             : 
     208         161 :         case 220:
     209         161 :             poFeature->oOCS.dfY = CPLAtof(pszValue);
     210         161 :             break;
     211             : 
     212         161 :         case 230:
     213         161 :             poFeature->oOCS.dfZ = CPLAtof(pszValue);
     214         161 :             break;
     215             : 
     216        2897 :         default:
     217        2897 :             if (poDS->ShouldIncludeRawCodeValues())
     218             :             {
     219             :                 char **papszRawCodeValues =
     220         467 :                     poFeature->GetFieldAsStringList("RawCodeValues");
     221             : 
     222         467 :                 papszRawCodeValues = CSLDuplicate(papszRawCodeValues);
     223             : 
     224         467 :                 papszRawCodeValues = CSLAddString(
     225             :                     papszRawCodeValues,
     226         467 :                     CPLString()
     227         934 :                         .Printf("%d %s", nCode, TextRecode(pszValue).c_str())
     228             :                         .c_str());
     229             : 
     230         467 :                 poFeature->SetField("RawCodeValues", papszRawCodeValues);
     231             : 
     232         467 :                 CSLDestroy(papszRawCodeValues);
     233             :             }
     234        2897 :             break;
     235             :     }
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                        PrepareFeatureStyle()                         */
     240             : /*                                                                      */
     241             : /*     - poBlockFeature: If this is not NULL, style properties on       */
     242             : /*       poFeature with ByBlock values will be replaced with the        */
     243             : /*       corresponding property from poBlockFeature.  If this           */
     244             : /*       parameter is supplied it is assumed that poFeature is a        */
     245             : /*       clone, not an "original" feature object.                       */
     246             : /************************************************************************/
     247             : 
     248        2287 : void OGRDXFLayer::PrepareFeatureStyle(
     249             :     OGRDXFFeature *const poFeature,
     250             :     OGRDXFFeature *const poBlockFeature /* = NULL */)
     251             : 
     252             : {
     253        2287 :     const char *pszStyleString = poFeature->GetStyleString();
     254             : 
     255        2287 :     if (pszStyleString && STARTS_WITH_CI(pszStyleString, "BRUSH("))
     256             :     {
     257          51 :         PrepareBrushStyle(poFeature, poBlockFeature);
     258             :     }
     259        2236 :     else if (pszStyleString && STARTS_WITH_CI(pszStyleString, "LABEL("))
     260             :     {
     261             :         // Find the new color of this feature, and replace it into
     262             :         // the style string
     263         128 :         const CPLString osNewColor = poFeature->GetColor(poDS, poBlockFeature);
     264             : 
     265         128 :         CPLString osNewStyle = pszStyleString;
     266          64 :         const size_t nColorStartPos = osNewStyle.rfind(",c:");
     267          64 :         if (nColorStartPos != std::string::npos)
     268             :         {
     269             :             const size_t nColorEndPos =
     270          64 :                 osNewStyle.find_first_of(",)", nColorStartPos + 3);
     271             : 
     272          64 :             if (nColorEndPos != std::string::npos)
     273             :             {
     274             :                 osNewStyle.replace(nColorStartPos + 3,
     275          64 :                                    nColorEndPos - (nColorStartPos + 3),
     276          64 :                                    osNewColor);
     277          64 :                 poFeature->SetStyleString(osNewStyle);
     278             :             }
     279          64 :         }
     280             :     }
     281             :     else
     282             :     {
     283        2172 :         PrepareLineStyle(poFeature, poBlockFeature);
     284             :     }
     285        2287 : }
     286             : 
     287             : /************************************************************************/
     288             : /*                         PrepareBrushStyle()                          */
     289             : /************************************************************************/
     290             : 
     291         163 : void OGRDXFLayer::PrepareBrushStyle(
     292             :     OGRDXFFeature *const poFeature,
     293             :     OGRDXFFeature *const poBlockFeature /* = NULL */)
     294             : 
     295             : {
     296         326 :     CPLString osStyle = "BRUSH(fc:";
     297             :     const std::string osForegroundColor =
     298         326 :         poFeature->GetColor(poDS, poBlockFeature);
     299         163 :     osStyle += osForegroundColor;
     300             : 
     301         370 :     if (poFeature->oStyleProperties.count("FillFlag") > 0 &&
     302         207 :         poFeature->oStyleProperties["FillFlag"] == "Pattern")
     303             :     {
     304          22 :         if (poFeature->oStyleProperties.count("HatchBackgroundColor") > 0)
     305             :         {
     306             :             unsigned nColor = static_cast<unsigned>(
     307          14 :                 atoi(poFeature->oStyleProperties["HatchBackgroundColor"]));
     308          14 :             if ((nColor >> 24) == 0xC3)
     309             :             {
     310             :                 // Indexed color
     311           4 :                 nColor &= 0xFFFFFF;
     312           4 :                 if (nColor < 256)
     313             :                 {
     314           4 :                     const unsigned char *pabyDXFColors = ACGetColorTable();
     315             : 
     316             :                     osStyle += CPLSPrintf(",bc:#%02x%02x%02x",
     317           4 :                                           pabyDXFColors[nColor * 3 + 0],
     318           4 :                                           pabyDXFColors[nColor * 3 + 1],
     319           4 :                                           pabyDXFColors[nColor * 3 + 2]);
     320             :                 }
     321             :             }
     322          10 :             else if ((nColor >> 24) == 0xC2)
     323             :             {
     324             :                 // True color
     325          10 :                 nColor &= 0xFFFFFF;
     326             : 
     327          10 :                 osStyle += CPLSPrintf(",bc:#%06x", nColor);
     328             :             }
     329             :         }
     330             : 
     331          22 :         double dfRotation = 0.0;
     332          22 :         if (poFeature->oStyleProperties.count("HatchPatternRotation") > 0)
     333             :         {
     334             :             dfRotation =
     335          22 :                 CPLAtof(poFeature->oStyleProperties["HatchPatternRotation"]);
     336             :         }
     337             : 
     338          22 :         const char *pszPatternName = poFeature->GetFieldAsString("Text");
     339          22 :         if (EQUAL(pszPatternName, "ANSI31"))
     340             :         {
     341           8 :             if (std::fabs(dfRotation - -45) < 1e-12 ||
     342           6 :                 std::fabs(dfRotation - 315) < 1e-12)
     343             :             {
     344           2 :                 osStyle += ",id:\"ogr-brush-2\"";
     345             :             }
     346           6 :             else if (std::fabs(dfRotation - 45) < 1e-12 ||
     347           4 :                      std::fabs(dfRotation - 225) < 1e-12)
     348             :             {
     349           2 :                 osStyle += ",id:\"ogr-brush-3\"";
     350             :             }
     351           4 :             else if (std::fabs(dfRotation - 90) < 1e-12 ||
     352           2 :                      std::fabs(dfRotation - -90) < 1e-12 ||
     353           2 :                      std::fabs(dfRotation - 270) < 1e-12)
     354             :             {
     355           2 :                 osStyle += ",id:\"ogr-brush-4\"";
     356             :             }
     357           2 :             else if (std::fabs(dfRotation) < 1e-12)
     358             :             {
     359           2 :                 osStyle += ",id:\"ogr-brush-5\"";
     360             :             }
     361             :             else
     362             :             {
     363           0 :                 osStyle += ",id:\"ogr-brush-5\"";
     364           0 :                 osStyle += CPLSPrintf(",a:%f", dfRotation);
     365             :             }
     366             :         }
     367          14 :         else if (EQUAL(pszPatternName, "ANSI37"))
     368             :         {
     369           6 :             if (std::fabs(dfRotation - 45) < 1e-12 ||
     370           4 :                 std::fabs(dfRotation - 225) < 1e-12)
     371             :             {
     372           2 :                 osStyle += ",id:\"ogr-brush-6\"";
     373             :             }
     374           4 :             else if (std::fabs(dfRotation) < 1e-12)
     375             :             {
     376           3 :                 osStyle += ",id:\"ogr-brush-7\"";
     377             :             }
     378             :             else
     379             :             {
     380           1 :                 osStyle += ",id:\"ogr-brush-7\"";
     381           1 :                 osStyle += CPLSPrintf(",a:%f", dfRotation);
     382             :             }
     383             :         }
     384           8 :         else if (EQUAL(pszPatternName, "null"))
     385             :         {
     386             :             // NOTE: null is a totally made up name to express the intent
     387           0 :             osStyle += ",id:\"ogr-brush-1\"";
     388             :         }
     389             : 
     390          22 :         if (poFeature->oStyleProperties.count("HatchPatternScale") > 0)
     391             :         {
     392             :             const double dfScale =
     393          22 :                 CPLAtof(poFeature->oStyleProperties["HatchPatternScale"]);
     394          22 :             if (std::fabs(dfScale - 1) > 1e-12)
     395             :             {
     396           2 :                 osStyle += CPLSPrintf(",s:%f", dfScale);
     397             :             }
     398             :         }
     399             :     }
     400         141 :     else if (osForegroundColor == "#00000000")
     401             :     {
     402           1 :         osStyle += ",id:\"ogr-brush-1\"";
     403             :     }
     404             : 
     405         163 :     osStyle += ")";
     406             : 
     407         163 :     poFeature->SetStyleString(osStyle);
     408         163 : }
     409             : 
     410             : /************************************************************************/
     411             : /*                          PrepareLineStyle()                          */
     412             : /************************************************************************/
     413             : 
     414        2726 : void OGRDXFLayer::PrepareLineStyle(
     415             :     OGRDXFFeature *const poFeature,
     416             :     OGRDXFFeature *const poBlockFeature /* = NULL */)
     417             : 
     418             : {
     419        5452 :     const CPLString osLayer = poFeature->GetFieldAsString("Layer");
     420             : 
     421             :     /* -------------------------------------------------------------------- */
     422             :     /*      Get line weight if available.                                   */
     423             :     /* -------------------------------------------------------------------- */
     424        2726 :     double dfWeight = 0.0;
     425        5452 :     CPLString osWeight = "-1";
     426             : 
     427        2726 :     if (poFeature->oStyleProperties.count("LineWeight") > 0)
     428         278 :         osWeight = poFeature->oStyleProperties["LineWeight"];
     429             : 
     430             :     // Use ByBlock lineweight?
     431        2726 :     if (CPLAtof(osWeight) == -2 && poBlockFeature)
     432             :     {
     433          97 :         if (poBlockFeature->oStyleProperties.count("LineWeight") > 0)
     434             :         {
     435             :             // Inherit lineweight from the owning block
     436          14 :             osWeight = poBlockFeature->oStyleProperties["LineWeight"];
     437             : 
     438             :             // Use the inherited lineweight if we regenerate the style
     439             :             // string again during block insertion
     440          14 :             poFeature->oStyleProperties["LineWeight"] = osWeight;
     441             :         }
     442             :         else
     443             :         {
     444             :             // If the owning block has no explicit lineweight,
     445             :             // assume ByLayer
     446          83 :             osWeight = "-1";
     447             :         }
     448             :     }
     449             : 
     450             :     // Use layer lineweight?
     451        2726 :     if (CPLAtof(osWeight) == -1)
     452             :     {
     453             :         auto osLayerLineWeight =
     454        2667 :             poDS->LookupLayerProperty(osLayer, "LineWeight");
     455        2667 :         osWeight = osLayerLineWeight ? *osLayerLineWeight : CPLString();
     456             :     }
     457             : 
     458             :     // Will be zero in the case of an invalid value
     459        2726 :     dfWeight = CPLAtof(osWeight) / 100.0;
     460             : 
     461             :     /* -------------------------------------------------------------------- */
     462             :     /*      Do we have a dash/dot line style?                               */
     463             :     /* -------------------------------------------------------------------- */
     464        5452 :     CPLString osLinetype = poFeature->GetFieldAsString("Linetype");
     465             : 
     466             :     // Use ByBlock line style?
     467        2726 :     if (!osLinetype.empty() && EQUAL(osLinetype, "ByBlock") && poBlockFeature)
     468             :     {
     469          41 :         osLinetype = poBlockFeature->GetFieldAsString("Linetype");
     470             : 
     471             :         // Use the inherited line style if we regenerate the style string
     472             :         // again during block insertion
     473          41 :         if (!osLinetype.empty())
     474           3 :             poFeature->SetField("Linetype", osLinetype);
     475             :     }
     476             : 
     477             :     // Use layer line style?
     478        2726 :     if (osLinetype.empty())
     479             :     {
     480        1750 :         auto osLayerLineType = poDS->LookupLayerProperty(osLayer, "Linetype");
     481         875 :         if (osLayerLineType)
     482         790 :             osLinetype = *osLayerLineType;
     483             :     }
     484             : 
     485        5452 :     const std::vector<double> oLineType = poDS->LookupLineType(osLinetype);
     486             : 
     487             :     // Linetype scale is not inherited from the block feature
     488        2726 :     double dfLineTypeScale = CPLAtof(poDS->GetVariable("$LTSCALE", "1.0"));
     489        2726 :     if (poFeature->oStyleProperties.count("LinetypeScale") > 0)
     490          16 :         dfLineTypeScale *=
     491          16 :             CPLAtof(poFeature->oStyleProperties["LinetypeScale"]);
     492             : 
     493        5452 :     CPLString osPattern;
     494        2816 :     for (std::vector<double>::const_iterator oIt = oLineType.begin();
     495        2816 :          oIt != oLineType.end(); ++oIt)
     496             :     {
     497             :         // this is the format specifier %g followed by a literal 'g'
     498             :         osPattern +=
     499          90 :             CPLString().Printf("%.11gg ", fabs(*oIt) * dfLineTypeScale);
     500             :     }
     501             : 
     502        2726 :     if (osPattern.length() > 0)
     503          45 :         osPattern.erase(osPattern.end() - 1);
     504             : 
     505             :     /* -------------------------------------------------------------------- */
     506             :     /*      Format the style string.                                        */
     507             :     /* -------------------------------------------------------------------- */
     508             : 
     509        5452 :     CPLString osStyle = "PEN(c:";
     510        2726 :     osStyle += poFeature->GetColor(poDS, poBlockFeature);
     511             : 
     512        2726 :     if (dfWeight > 0.0)
     513             :     {
     514             :         char szBuffer[64];
     515          19 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight);
     516          19 :         osStyle += CPLString().Printf(",w:%sg", szBuffer);
     517             :     }
     518             : 
     519        2726 :     if (osPattern != "")
     520             :     {
     521          45 :         osStyle += ",p:\"";
     522          45 :         osStyle += osPattern;
     523          45 :         osStyle += "\"";
     524             :     }
     525             : 
     526        2726 :     osStyle += ")";
     527             : 
     528        2726 :     poFeature->SetStyleString(osStyle);
     529        2726 : }
     530             : 
     531             : /************************************************************************/
     532             : /*                             TextRecode()                             */
     533             : /************************************************************************/
     534             : 
     535        1626 : CPLString OGRDXFLayer::TextRecode(const char *pszInput)
     536             : 
     537             : {
     538        3252 :     return CPLString(pszInput).Recode(poDS->GetEncoding(), CPL_ENC_UTF8);
     539             : }
     540             : 
     541             : /************************************************************************/
     542             : /*                            TextUnescape()                            */
     543             : /*                                                                      */
     544             : /*      Unexcape DXF style escape sequences such as \P for newline      */
     545             : /*      and \~ for space, and do the recoding to UTF8.                  */
     546             : /************************************************************************/
     547             : 
     548         150 : CPLString OGRDXFLayer::TextUnescape(const char *pszInput, bool bIsMText)
     549             : 
     550             : {
     551         150 :     if (poDS->ShouldTranslateEscapes())
     552         149 :         return ACTextUnescape(pszInput, poDS->GetEncoding(), bIsMText);
     553             : 
     554           1 :     return TextRecode(pszInput);
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                           TranslateMTEXT()                           */
     559             : /************************************************************************/
     560             : 
     561          40 : OGRDXFFeature *OGRDXFLayer::TranslateMTEXT()
     562             : 
     563             : {
     564             :     char szLineBuf[512];
     565          40 :     int nCode = 0;
     566          80 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
     567          40 :     double dfX = 0.0;
     568          40 :     double dfY = 0.0;
     569          40 :     double dfZ = 0.0;
     570          40 :     double dfAngle = 0.0;
     571          40 :     double dfHeight = 0.0;
     572          40 :     double dfXDirection = 0.0;
     573          40 :     double dfYDirection = 0.0;
     574          40 :     bool bHaveZ = false;
     575          40 :     int nAttachmentPoint = -1;
     576          80 :     CPLString osText;
     577          80 :     CPLString osStyleName = "STANDARD";
     578             : 
     579         802 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     580             :     {
     581         762 :         switch (nCode)
     582             :         {
     583          40 :             case 10:
     584          40 :                 dfX = CPLAtof(szLineBuf);
     585          40 :                 break;
     586             : 
     587          40 :             case 20:
     588          40 :                 dfY = CPLAtof(szLineBuf);
     589          40 :                 break;
     590             : 
     591          34 :             case 30:
     592          34 :                 dfZ = CPLAtof(szLineBuf);
     593          34 :                 bHaveZ = true;
     594          34 :                 break;
     595             : 
     596          40 :             case 40:
     597          40 :                 dfHeight = CPLAtof(szLineBuf);
     598          40 :                 break;
     599             : 
     600          37 :             case 71:
     601          37 :                 nAttachmentPoint = atoi(szLineBuf);
     602          37 :                 break;
     603             : 
     604           0 :             case 11:
     605           0 :                 dfXDirection = CPLAtof(szLineBuf);
     606           0 :                 break;
     607             : 
     608           0 :             case 21:
     609           0 :                 dfYDirection = CPLAtof(szLineBuf);
     610           0 :                 dfAngle = atan2(dfYDirection, dfXDirection) * 180.0 / M_PI;
     611           0 :                 break;
     612             : 
     613          42 :             case 1:
     614             :             case 3:
     615          42 :                 osText += TextUnescape(szLineBuf, true);
     616          42 :                 break;
     617             : 
     618          29 :             case 50:
     619          29 :                 dfAngle = CPLAtof(szLineBuf);
     620          29 :                 break;
     621             : 
     622          29 :             case 7:
     623          29 :                 osStyleName = TextRecode(szLineBuf);
     624          29 :                 break;
     625             : 
     626         471 :             default:
     627         471 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
     628         471 :                 break;
     629             :         }
     630             :     }
     631          40 :     if (nCode < 0)
     632             :     {
     633           0 :         DXF_LAYER_READER_ERROR();
     634           0 :         return nullptr;
     635             :     }
     636             : 
     637          40 :     if (nCode == 0)
     638          40 :         poDS->UnreadValue();
     639             : 
     640          40 :     OGRPoint *poGeom = nullptr;
     641          40 :     if (bHaveZ)
     642          34 :         poGeom = new OGRPoint(dfX, dfY, dfZ);
     643             :     else
     644           6 :         poGeom = new OGRPoint(dfX, dfY);
     645             : 
     646             :     /* We do NOT apply the OCS for MTEXT. See
     647             :      * https://trac.osgeo.org/gdal/ticket/7049 */
     648             :     /* ApplyOCSTransformer( poGeom ); */
     649             : 
     650          40 :     poFeature->SetGeometryDirectly(poGeom);
     651             : 
     652             :     /* -------------------------------------------------------------------- */
     653             :     /*      Apply text after stripping off any extra terminating newline.   */
     654             :     /* -------------------------------------------------------------------- */
     655          40 :     if (!osText.empty() && osText.back() == '\n')
     656           0 :         osText.pop_back();
     657             : 
     658          40 :     poFeature->SetField("Text", osText);
     659             : 
     660             :     /* -------------------------------------------------------------------- */
     661             :     /*      We need to escape double quotes with backslashes before they    */
     662             :     /*      can be inserted in the style string.                            */
     663             :     /* -------------------------------------------------------------------- */
     664          40 :     if (strchr(osText, '"') != nullptr)
     665             :     {
     666          10 :         std::string osEscaped;
     667             : 
     668         230 :         for (size_t iC = 0; iC < osText.size(); iC++)
     669             :         {
     670         220 :             if (osText[iC] == '"')
     671          20 :                 osEscaped += "\\\"";
     672             :             else
     673         200 :                 osEscaped += osText[iC];
     674             :         }
     675          10 :         osText = std::move(osEscaped);
     676             :     }
     677             : 
     678             :     /* -------------------------------------------------------------------- */
     679             :     /*      Prepare style string.                                           */
     680             :     /* -------------------------------------------------------------------- */
     681          80 :     CPLString osStyle;
     682             :     char szBuffer[64];
     683             : 
     684             :     // Font name
     685          40 :     osStyle.Printf("LABEL(f:\"");
     686             : 
     687             :     // Preserve legacy behavior of specifying "Arial" as a default font name.
     688          40 :     osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
     689             : 
     690          40 :     osStyle += "\"";
     691             : 
     692             :     // Bold, italic
     693          40 :     if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
     694             :     {
     695           4 :         osStyle += ",bo:1";
     696             :     }
     697          40 :     if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
     698             :     {
     699           1 :         osStyle += ",it:1";
     700             :     }
     701             : 
     702             :     // Text string itself
     703          40 :     osStyle += ",t:\"";
     704          40 :     osStyle += osText;
     705          40 :     osStyle += "\"";
     706             : 
     707          40 :     if (dfAngle != 0.0)
     708             :     {
     709          19 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
     710          19 :         osStyle += CPLString().Printf(",a:%s", szBuffer);
     711             :     }
     712             : 
     713          40 :     if (dfHeight != 0.0)
     714             :     {
     715          40 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
     716          40 :         osStyle += CPLString().Printf(",s:%sg", szBuffer);
     717             :     }
     718             : 
     719             :     const char *pszWidthFactor =
     720          40 :         poDS->LookupTextStyleProperty(osStyleName, "Width", "1");
     721          40 :     if (pszWidthFactor && CPLAtof(pszWidthFactor) != 1.0)
     722             :     {
     723           4 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g",
     724           4 :                     CPLAtof(pszWidthFactor) * 100.0);
     725           4 :         osStyle += CPLString().Printf(",w:%s", szBuffer);
     726             :     }
     727             : 
     728          40 :     if (nAttachmentPoint >= 0 && nAttachmentPoint <= 9)
     729             :     {
     730             :         const static int anAttachmentMap[10] = {-1, 7, 8, 9, 4, 5, 6, 1, 2, 3};
     731             : 
     732             :         osStyle +=
     733          37 :             CPLString().Printf(",p:%d", anAttachmentMap[nAttachmentPoint]);
     734             :     }
     735             : 
     736             :     // Color
     737          40 :     osStyle += ",c:";
     738          40 :     osStyle += poFeature->GetColor(poDS);
     739             : 
     740          40 :     osStyle += ")";
     741             : 
     742          40 :     poFeature->SetStyleString(osStyle);
     743             : 
     744          40 :     return poFeature.release();
     745             : }
     746             : 
     747             : /************************************************************************/
     748             : /*                           TranslateTEXT()                            */
     749             : /*                                                                      */
     750             : /*      This function translates TEXT and ATTRIB entities, as well as   */
     751             : /*      ATTDEF entities when we are not inlining blocks.                */
     752             : /************************************************************************/
     753             : 
     754          65 : OGRDXFFeature *OGRDXFLayer::TranslateTEXT(const bool bIsAttribOrAttdef)
     755             : 
     756             : {
     757             :     char szLineBuf[257];
     758          65 :     int nCode = 0;
     759         130 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
     760             : 
     761          65 :     double dfX = 0.0;
     762          65 :     double dfY = 0.0;
     763          65 :     double dfZ = 0.0;
     764          65 :     bool bHaveZ = false;
     765             : 
     766          65 :     double dfAngle = 0.0;
     767          65 :     double dfHeight = 0.0;
     768          65 :     double dfWidthFactor = 1.0;
     769          65 :     bool bHasAlignmentPoint = false;
     770          65 :     double dfAlignmentPointX = 0.0;
     771          65 :     double dfAlignmentPointY = 0.0;
     772             : 
     773         130 :     CPLString osText;
     774         130 :     CPLString osStyleName = "STANDARD";
     775             : 
     776          65 :     int nAnchorPosition = 1;
     777          65 :     int nHorizontalAlignment = 0;
     778          65 :     int nVerticalAlignment = 0;
     779             : 
     780         986 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     781             :     {
     782         921 :         switch (nCode)
     783             :         {
     784          64 :             case 10:
     785          64 :                 dfX = CPLAtof(szLineBuf);
     786          64 :                 break;
     787             : 
     788          64 :             case 20:
     789          64 :                 dfY = CPLAtof(szLineBuf);
     790          64 :                 break;
     791             : 
     792          25 :             case 11:
     793          25 :                 dfAlignmentPointX = CPLAtof(szLineBuf);
     794          25 :                 break;
     795             : 
     796          25 :             case 21:
     797          25 :                 dfAlignmentPointY = CPLAtof(szLineBuf);
     798          25 :                 bHasAlignmentPoint = true;
     799          25 :                 break;
     800             : 
     801          64 :             case 30:
     802          64 :                 dfZ = CPLAtof(szLineBuf);
     803          64 :                 bHaveZ = true;
     804          64 :                 break;
     805             : 
     806          64 :             case 40:
     807          64 :                 dfHeight = CPLAtof(szLineBuf);
     808          64 :                 break;
     809             : 
     810          12 :             case 41:
     811          12 :                 dfWidthFactor = CPLAtof(szLineBuf);
     812          12 :                 break;
     813             : 
     814          64 :             case 1:
     815          64 :                 osText += TextUnescape(szLineBuf, false);
     816          64 :                 break;
     817             : 
     818          11 :             case 50:
     819          11 :                 dfAngle = CPLAtof(szLineBuf);
     820          11 :                 break;
     821             : 
     822          25 :             case 72:
     823          25 :                 nHorizontalAlignment = atoi(szLineBuf);
     824          25 :                 break;
     825             : 
     826           0 :             case 73:
     827           0 :                 if (!bIsAttribOrAttdef)
     828           0 :                     nVerticalAlignment = atoi(szLineBuf);
     829           0 :                 break;
     830             : 
     831           8 :             case 74:
     832           8 :                 if (bIsAttribOrAttdef)
     833           8 :                     nVerticalAlignment = atoi(szLineBuf);
     834           8 :                 break;
     835             : 
     836           0 :             case 7:
     837           0 :                 osStyleName = TextRecode(szLineBuf);
     838           0 :                 break;
     839             : 
     840             :             // 2 and 70 are for ATTRIB and ATTDEF entities only
     841          37 :             case 2:
     842          37 :                 if (bIsAttribOrAttdef)
     843             :                 {
     844             :                     // Attribute tags are not supposed to contain spaces (but
     845             :                     // sometimes they do)
     846          37 :                     while (char *pchSpace = strchr(szLineBuf, ' '))
     847           0 :                         *pchSpace = '_';
     848             : 
     849          37 :                     poFeature->osAttributeTag = szLineBuf;
     850             :                 }
     851          37 :                 break;
     852             : 
     853          37 :             case 70:
     854          37 :                 if (bIsAttribOrAttdef)
     855             :                 {
     856             :                     // When the LSB is set, this ATTRIB is "invisible"
     857          37 :                     if (atoi(szLineBuf) & 1)
     858           0 :                         poFeature->oStyleProperties["Hidden"] = "1";
     859             :                     // If the next bit is set, this ATTDEF is to be preserved
     860             :                     // and treated as constant TEXT
     861          37 :                     else if (atoi(szLineBuf) & 2)
     862           2 :                         poFeature->osAttributeTag.Clear();
     863             :                 }
     864          37 :                 break;
     865             : 
     866         421 :             default:
     867         421 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
     868         421 :                 break;
     869             :         }
     870             :     }
     871          65 :     if (nCode < 0)
     872             :     {
     873           0 :         DXF_LAYER_READER_ERROR();
     874           0 :         return nullptr;
     875             :     }
     876             : 
     877          65 :     if (nCode == 0)
     878          65 :         poDS->UnreadValue();
     879             : 
     880          65 :     OGRPoint *poGeom = nullptr;
     881          65 :     if (bHaveZ)
     882          64 :         poGeom = new OGRPoint(dfX, dfY, dfZ);
     883             :     else
     884           1 :         poGeom = new OGRPoint(dfX, dfY);
     885          65 :     poFeature->ApplyOCSTransformer(poGeom);
     886          65 :     poFeature->SetGeometryDirectly(poGeom);
     887             : 
     888             :     /* -------------------------------------------------------------------- */
     889             :     /*      Determine anchor position.                                      */
     890             :     /* -------------------------------------------------------------------- */
     891          65 :     if (nHorizontalAlignment > 0 || nVerticalAlignment > 0)
     892             :     {
     893          25 :         switch (nVerticalAlignment)
     894             :         {
     895           0 :             case 1:  // bottom
     896           0 :                 nAnchorPosition = 10;
     897           0 :                 break;
     898             : 
     899           2 :             case 2:  // middle
     900           2 :                 nAnchorPosition = 4;
     901           2 :                 break;
     902             : 
     903           6 :             case 3:  // top
     904           6 :                 nAnchorPosition = 7;
     905           6 :                 break;
     906             : 
     907          17 :             default:
     908             :                 // Handle "Middle" alignment approximately (this is rather like
     909             :                 // MTEXT alignment in that it uses the actual height of the text
     910             :                 // string to position the text, and thus requires knowledge of
     911             :                 // text metrics)
     912          17 :                 if (nHorizontalAlignment == 4)
     913           1 :                     nAnchorPosition = 5;
     914          17 :                 break;
     915             :         }
     916          25 :         if (nHorizontalAlignment < 3)
     917          24 :             nAnchorPosition += nHorizontalAlignment;
     918             :         // TODO other alignment options
     919             :     }
     920             : 
     921          65 :     poFeature->SetField("Text", osText);
     922             : 
     923             :     /* -------------------------------------------------------------------- */
     924             :     /*      We need to escape double quotes with backslashes before they    */
     925             :     /*      can be inserted in the style string.                            */
     926             :     /* -------------------------------------------------------------------- */
     927          65 :     if (strchr(osText, '"') != nullptr)
     928             :     {
     929           0 :         CPLString osEscaped;
     930             : 
     931           0 :         for (size_t iC = 0; iC < osText.size(); iC++)
     932             :         {
     933           0 :             if (osText[iC] == '"')
     934           0 :                 osEscaped += "\\\"";
     935             :             else
     936           0 :                 osEscaped += osText[iC];
     937             :         }
     938           0 :         osText = std::move(osEscaped);
     939             :     }
     940             : 
     941             :     /* -------------------------------------------------------------------- */
     942             :     /*      Prepare style string.                                           */
     943             :     /* -------------------------------------------------------------------- */
     944         130 :     CPLString osStyle;
     945             :     char szBuffer[64];
     946             : 
     947             :     // Font name
     948          65 :     osStyle.Printf("LABEL(f:\"");
     949             : 
     950             :     // Preserve legacy behavior of specifying "Arial" as a default font name.
     951          65 :     osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
     952             : 
     953          65 :     osStyle += "\"";
     954             : 
     955             :     // Bold, italic
     956          65 :     if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
     957             :     {
     958           6 :         osStyle += ",bo:1";
     959             :     }
     960          65 :     if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
     961             :     {
     962           6 :         osStyle += ",it:1";
     963             :     }
     964             : 
     965             :     // Text string itself
     966          65 :     osStyle += ",t:\"";
     967          65 :     osStyle += osText;
     968          65 :     osStyle += "\"";
     969             : 
     970             :     // Other attributes
     971          65 :     osStyle += CPLString().Printf(",p:%d", nAnchorPosition);
     972             : 
     973          65 :     if (dfAngle != 0.0)
     974             :     {
     975          11 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
     976          11 :         osStyle += CPLString().Printf(",a:%s", szBuffer);
     977             :     }
     978             : 
     979          65 :     if (dfHeight != 0.0)
     980             :     {
     981          64 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
     982          64 :         osStyle += CPLString().Printf(",s:%sg", szBuffer);
     983             :     }
     984             : 
     985          65 :     if (dfWidthFactor != 1.0)
     986             :     {
     987          12 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g", dfWidthFactor * 100.0);
     988          12 :         osStyle += CPLString().Printf(",w:%s", szBuffer);
     989             :     }
     990             : 
     991          65 :     if (bHasAlignmentPoint && dfAlignmentPointX != dfX)
     992             :     {
     993          23 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
     994             :                     dfAlignmentPointX - dfX);
     995          23 :         osStyle += CPLString().Printf(",dx:%sg", szBuffer);
     996             :     }
     997             : 
     998          65 :     if (bHasAlignmentPoint && dfAlignmentPointY != dfY)
     999             :     {
    1000          14 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
    1001             :                     dfAlignmentPointY - dfY);
    1002          14 :         osStyle += CPLString().Printf(",dy:%sg", szBuffer);
    1003             :     }
    1004             : 
    1005             :     // Color
    1006          65 :     osStyle += ",c:";
    1007          65 :     osStyle += poFeature->GetColor(poDS);
    1008             : 
    1009          65 :     osStyle += ")";
    1010             : 
    1011          65 :     poFeature->SetStyleString(osStyle);
    1012             : 
    1013          65 :     return poFeature.release();
    1014             : }
    1015             : 
    1016             : /************************************************************************/
    1017             : /*                           TranslatePOINT()                           */
    1018             : /************************************************************************/
    1019             : 
    1020          35 : OGRDXFFeature *OGRDXFLayer::TranslatePOINT()
    1021             : 
    1022             : {
    1023             :     char szLineBuf[257];
    1024          35 :     int nCode = 0;
    1025          70 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1026          35 :     double dfX = 0.0;
    1027          35 :     double dfY = 0.0;
    1028          35 :     double dfZ = 0.0;
    1029          35 :     bool bHaveZ = false;
    1030             : 
    1031         363 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1032             :     {
    1033         328 :         switch (nCode)
    1034             :         {
    1035          35 :             case 10:
    1036          35 :                 dfX = CPLAtof(szLineBuf);
    1037          35 :                 break;
    1038             : 
    1039          35 :             case 20:
    1040          35 :                 dfY = CPLAtof(szLineBuf);
    1041          35 :                 break;
    1042             : 
    1043          31 :             case 30:
    1044          31 :                 dfZ = CPLAtof(szLineBuf);
    1045          31 :                 bHaveZ = true;
    1046          31 :                 break;
    1047             : 
    1048         227 :             default:
    1049         227 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1050         227 :                 break;
    1051             :         }
    1052             :     }
    1053          35 :     if (nCode < 0)
    1054             :     {
    1055           0 :         DXF_LAYER_READER_ERROR();
    1056           0 :         return nullptr;
    1057             :     }
    1058             : 
    1059          35 :     if (nCode == 0)
    1060          35 :         poDS->UnreadValue();
    1061             : 
    1062          35 :     OGRPoint *poGeom = nullptr;
    1063          35 :     if (bHaveZ)
    1064          31 :         poGeom = new OGRPoint(dfX, dfY, dfZ);
    1065             :     else
    1066           4 :         poGeom = new OGRPoint(dfX, dfY);
    1067             : 
    1068          35 :     poFeature->SetGeometryDirectly(poGeom);
    1069             : 
    1070             :     // Set style pen color
    1071          35 :     PrepareLineStyle(poFeature.get());
    1072             : 
    1073          35 :     return poFeature.release();
    1074             : }
    1075             : 
    1076             : /************************************************************************/
    1077             : /*                           TranslateLINE()                            */
    1078             : /************************************************************************/
    1079             : 
    1080         223 : OGRDXFFeature *OGRDXFLayer::TranslateLINE()
    1081             : 
    1082             : {
    1083             :     char szLineBuf[257];
    1084         223 :     int nCode = 0;
    1085         446 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1086         223 :     double dfX1 = 0.0;
    1087         223 :     double dfY1 = 0.0;
    1088         223 :     double dfZ1 = 0.0;
    1089         223 :     double dfX2 = 0.0;
    1090         223 :     double dfY2 = 0.0;
    1091         223 :     double dfZ2 = 0.0;
    1092         223 :     bool bHaveZ = false;
    1093             : 
    1094             :     /* -------------------------------------------------------------------- */
    1095             :     /*      Process values.                                                 */
    1096             :     /* -------------------------------------------------------------------- */
    1097        2862 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1098             :     {
    1099        2639 :         switch (nCode)
    1100             :         {
    1101         223 :             case 10:
    1102         223 :                 dfX1 = CPLAtof(szLineBuf);
    1103         223 :                 break;
    1104             : 
    1105         223 :             case 11:
    1106         223 :                 dfX2 = CPLAtof(szLineBuf);
    1107         223 :                 break;
    1108             : 
    1109         223 :             case 20:
    1110         223 :                 dfY1 = CPLAtof(szLineBuf);
    1111         223 :                 break;
    1112             : 
    1113         223 :             case 21:
    1114         223 :                 dfY2 = CPLAtof(szLineBuf);
    1115         223 :                 break;
    1116             : 
    1117         223 :             case 30:
    1118         223 :                 dfZ1 = CPLAtof(szLineBuf);
    1119         223 :                 bHaveZ = true;
    1120         223 :                 break;
    1121             : 
    1122         223 :             case 31:
    1123         223 :                 dfZ2 = CPLAtof(szLineBuf);
    1124         223 :                 bHaveZ = true;
    1125         223 :                 break;
    1126             : 
    1127        1301 :             default:
    1128        1301 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1129        1301 :                 break;
    1130             :         }
    1131             :     }
    1132         223 :     if (nCode < 0)
    1133             :     {
    1134           0 :         DXF_LAYER_READER_ERROR();
    1135           0 :         return nullptr;
    1136             :     }
    1137             : 
    1138         223 :     if (nCode == 0)
    1139         223 :         poDS->UnreadValue();
    1140             : 
    1141             :     /* -------------------------------------------------------------------- */
    1142             :     /*      Create geometry                                                 */
    1143             :     /* -------------------------------------------------------------------- */
    1144         446 :     auto poLS = std::make_unique<OGRLineString>();
    1145         223 :     if (bHaveZ)
    1146             :     {
    1147         223 :         poLS->addPoint(dfX1, dfY1, dfZ1);
    1148         223 :         poLS->addPoint(dfX2, dfY2, dfZ2);
    1149             :     }
    1150             :     else
    1151             :     {
    1152           0 :         poLS->addPoint(dfX1, dfY1);
    1153           0 :         poLS->addPoint(dfX2, dfY2);
    1154             :     }
    1155             : 
    1156         223 :     poFeature->SetGeometryDirectly(poLS.release());
    1157             : 
    1158         223 :     PrepareLineStyle(poFeature.get());
    1159             : 
    1160         223 :     return poFeature.release();
    1161             : }
    1162             : 
    1163             : /************************************************************************/
    1164             : /*                         TranslateLWPOLYLINE()                        */
    1165             : /************************************************************************/
    1166          39 : OGRDXFFeature *OGRDXFLayer::TranslateLWPOLYLINE()
    1167             : 
    1168             : {
    1169             :     // Collect vertices and attributes into a smooth polyline.
    1170             :     // If there are no bulges, then we are a straight-line polyline.
    1171             :     // Single-vertex polylines become points.
    1172             :     // Group code 30 (vertex Z) is not part of this entity.
    1173             :     char szLineBuf[257];
    1174          39 :     int nCode = 0;
    1175          39 :     int nPolylineFlag = 0;
    1176             : 
    1177          78 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1178          39 :     double dfX = 0.0;
    1179          39 :     double dfY = 0.0;
    1180          39 :     double dfZ = 0.0;
    1181          39 :     bool bHaveX = false;
    1182          39 :     bool bHaveY = false;
    1183             : 
    1184          39 :     int nNumVertices = 1;  // use 1 based index
    1185          39 :     int npolyarcVertexCount = 1;
    1186          39 :     double dfBulge = 0.0;
    1187          78 :     DXFSmoothPolyline smoothPolyline;
    1188             : 
    1189          39 :     smoothPolyline.setCoordinateDimension(2);
    1190             : 
    1191             :     /* -------------------------------------------------------------------- */
    1192             :     /*      Collect information from the LWPOLYLINE object itself.          */
    1193             :     /* -------------------------------------------------------------------- */
    1194         705 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1195             :     {
    1196         666 :         if (npolyarcVertexCount > nNumVertices)
    1197             :         {
    1198           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1199             :                      "Too many vertices found in LWPOLYLINE.");
    1200           0 :             return nullptr;
    1201             :         }
    1202             : 
    1203         666 :         switch (nCode)
    1204             :         {
    1205          10 :             case 38:
    1206             :                 // Constant elevation.
    1207          10 :                 dfZ = CPLAtof(szLineBuf);
    1208          10 :                 smoothPolyline.setCoordinateDimension(3);
    1209          10 :                 break;
    1210             : 
    1211          39 :             case 90:
    1212          39 :                 nNumVertices = atoi(szLineBuf);
    1213          39 :                 break;
    1214             : 
    1215          39 :             case 70:
    1216          39 :                 nPolylineFlag = atoi(szLineBuf);
    1217          39 :                 break;
    1218             : 
    1219         132 :             case 10:
    1220         132 :                 if (bHaveX && bHaveY)
    1221             :                 {
    1222          93 :                     smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
    1223          93 :                     npolyarcVertexCount++;
    1224          93 :                     dfBulge = 0.0;
    1225          93 :                     bHaveY = false;
    1226             :                 }
    1227         132 :                 dfX = CPLAtof(szLineBuf);
    1228         132 :                 bHaveX = true;
    1229         132 :                 break;
    1230             : 
    1231         132 :             case 20:
    1232         132 :                 if (bHaveX && bHaveY)
    1233             :                 {
    1234           0 :                     smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
    1235           0 :                     npolyarcVertexCount++;
    1236           0 :                     dfBulge = 0.0;
    1237           0 :                     bHaveX = false;
    1238             :                 }
    1239         132 :                 dfY = CPLAtof(szLineBuf);
    1240         132 :                 bHaveY = true;
    1241         132 :                 break;
    1242             : 
    1243          14 :             case 42:
    1244          14 :                 dfBulge = CPLAtof(szLineBuf);
    1245          14 :                 break;
    1246             : 
    1247         300 :             default:
    1248         300 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1249         300 :                 break;
    1250             :         }
    1251             :     }
    1252          39 :     if (nCode < 0)
    1253             :     {
    1254           0 :         DXF_LAYER_READER_ERROR();
    1255           0 :         return nullptr;
    1256             :     }
    1257             : 
    1258          39 :     if (nCode == 0)
    1259          39 :         poDS->UnreadValue();
    1260             : 
    1261          39 :     if (bHaveX && bHaveY)
    1262          39 :         smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
    1263             : 
    1264          39 :     if (smoothPolyline.IsEmpty())
    1265             :     {
    1266           0 :         return nullptr;
    1267             :     }
    1268             : 
    1269             :     /* -------------------------------------------------------------------- */
    1270             :     /*      Close polyline if necessary.                                    */
    1271             :     /* -------------------------------------------------------------------- */
    1272          39 :     const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
    1273          39 :     if (bIsClosed)
    1274          15 :         smoothPolyline.Close();
    1275             : 
    1276          39 :     const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
    1277             : 
    1278          39 :     smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
    1279             :     auto poGeom =
    1280          78 :         std::unique_ptr<OGRGeometry>(smoothPolyline.Tessellate(bAsPolygon));
    1281          39 :     poFeature->ApplyOCSTransformer(poGeom.get());
    1282          39 :     poFeature->SetGeometryDirectly(poGeom.release());
    1283             : 
    1284          39 :     PrepareLineStyle(poFeature.get());
    1285             : 
    1286          39 :     return poFeature.release();
    1287             : }
    1288             : 
    1289             : /************************************************************************/
    1290             : /*                             SafeAbs()                                */
    1291             : /************************************************************************/
    1292             : 
    1293          24 : static inline int SafeAbs(int x)
    1294             : {
    1295          24 :     if (x == std::numeric_limits<int>::min())
    1296           0 :         return std::numeric_limits<int>::max();
    1297          24 :     return abs(x);
    1298             : }
    1299             : 
    1300             : /************************************************************************/
    1301             : /*                         TranslatePOLYLINE()                          */
    1302             : /*                                                                      */
    1303             : /*      We also capture the following vertices.                         */
    1304             : /************************************************************************/
    1305             : 
    1306          20 : OGRDXFFeature *OGRDXFLayer::TranslatePOLYLINE()
    1307             : 
    1308             : {
    1309             :     char szLineBuf[257];
    1310          20 :     int nCode = 0;
    1311          20 :     int nPolylineFlag = 0;
    1312          40 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1313             : 
    1314             :     /* -------------------------------------------------------------------- */
    1315             :     /*      Collect information from the POLYLINE object itself.            */
    1316             :     /* -------------------------------------------------------------------- */
    1317         227 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1318             :     {
    1319         207 :         switch (nCode)
    1320             :         {
    1321          20 :             case 70:
    1322          20 :                 nPolylineFlag = atoi(szLineBuf);
    1323          20 :                 break;
    1324             : 
    1325         187 :             default:
    1326         187 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1327         187 :                 break;
    1328             :         }
    1329             :     }
    1330          20 :     if (nCode < 0)
    1331             :     {
    1332           0 :         DXF_LAYER_READER_ERROR();
    1333           0 :         return nullptr;
    1334             :     }
    1335             : 
    1336          20 :     if ((nPolylineFlag & 16) != 0)
    1337             :     {
    1338           0 :         CPLDebug("DXF", "Polygon mesh not supported.");
    1339           0 :         return nullptr;
    1340             :     }
    1341             : 
    1342             :     /* -------------------------------------------------------------------- */
    1343             :     /*      Collect vertices as a smooth polyline.                          */
    1344             :     /* -------------------------------------------------------------------- */
    1345          20 :     double dfX = 0.0;
    1346          20 :     double dfY = 0.0;
    1347          20 :     double dfZ = 0.0;
    1348          20 :     double dfBulge = 0.0;
    1349          20 :     int nVertexFlag = 0;
    1350          40 :     DXFSmoothPolyline smoothPolyline;
    1351          20 :     unsigned int vertexIndex71 = 0;
    1352          20 :     unsigned int vertexIndex72 = 0;
    1353          20 :     unsigned int vertexIndex73 = 0;
    1354          20 :     unsigned int vertexIndex74 = 0;
    1355          40 :     std::vector<OGRPoint> aoPoints;
    1356          40 :     auto poPS = std::make_unique<OGRPolyhedralSurface>();
    1357             : 
    1358          20 :     smoothPolyline.setCoordinateDimension(2);
    1359             : 
    1360         106 :     while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
    1361             :     {
    1362             :         // Eat non-vertex objects.
    1363          86 :         if (!EQUAL(szLineBuf, "VERTEX"))
    1364             :         {
    1365           0 :             while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1366             :             {
    1367             :             }
    1368           0 :             if (nCode < 0)
    1369             :             {
    1370           0 :                 DXF_LAYER_READER_ERROR();
    1371           0 :                 return nullptr;
    1372             :             }
    1373             : 
    1374           0 :             continue;
    1375             :         }
    1376             : 
    1377             :         // process a Vertex
    1378         956 :         while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1379             :         {
    1380         870 :             switch (nCode)
    1381             :             {
    1382          86 :                 case 10:
    1383          86 :                     dfX = CPLAtof(szLineBuf);
    1384          86 :                     break;
    1385             : 
    1386          86 :                 case 20:
    1387          86 :                     dfY = CPLAtof(szLineBuf);
    1388          86 :                     break;
    1389             : 
    1390          86 :                 case 30:
    1391          86 :                     dfZ = CPLAtof(szLineBuf);
    1392          86 :                     smoothPolyline.setCoordinateDimension(3);
    1393          86 :                     break;
    1394             : 
    1395           4 :                 case 42:
    1396           4 :                     dfBulge = CPLAtof(szLineBuf);
    1397           4 :                     break;
    1398             : 
    1399          64 :                 case 70:
    1400          64 :                     nVertexFlag = atoi(szLineBuf);
    1401          64 :                     break;
    1402             : 
    1403           6 :                 case 71:
    1404             :                     // See comment below about negative values for 71, 72, 73,
    1405             :                     // 74
    1406           6 :                     vertexIndex71 = SafeAbs(atoi(szLineBuf));
    1407           6 :                     break;
    1408             : 
    1409           6 :                 case 72:
    1410           6 :                     vertexIndex72 = SafeAbs(atoi(szLineBuf));
    1411           6 :                     break;
    1412             : 
    1413           6 :                 case 73:
    1414           6 :                     vertexIndex73 = SafeAbs(atoi(szLineBuf));
    1415           6 :                     break;
    1416             : 
    1417           6 :                 case 74:
    1418           6 :                     vertexIndex74 = SafeAbs(atoi(szLineBuf));
    1419           6 :                     break;
    1420             : 
    1421         520 :                 default:
    1422         520 :                     break;
    1423             :             }
    1424             :         }
    1425             : 
    1426          86 :         if (((nVertexFlag & 64) != 0) && ((nVertexFlag & 128) != 0))
    1427             :         {
    1428             :             // add the point to the list of points
    1429             :             try
    1430             :             {
    1431           8 :                 aoPoints.emplace_back(dfX, dfY, dfZ);
    1432             :             }
    1433           0 :             catch (const std::exception &e)
    1434             :             {
    1435           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1436           0 :                 return nullptr;
    1437             :             }
    1438             :         }
    1439             : 
    1440             :         // Note - If any index out of vertexIndex71, vertexIndex72,
    1441             :         // vertexIndex73 or vertexIndex74 is negative, it means that the line
    1442             :         // starting from that vertex is invisible. However, it still needs to be
    1443             :         // constructed as part of the resultant polyhedral surface; there is no
    1444             :         // way to specify the visibility of individual edges in a polyhedral
    1445             :         // surface at present
    1446             : 
    1447          86 :         if (nVertexFlag == 128)
    1448             :         {
    1449             :             // create a polygon and add it to the Polyhedral Surface
    1450          12 :             auto poLR = std::make_unique<OGRLinearRing>();
    1451           6 :             int iPoint = 0;
    1452           6 :             int startPoint = -1;
    1453           6 :             poLR->set3D(TRUE);
    1454           6 :             if (vertexIndex71 != 0 && vertexIndex71 <= aoPoints.size())
    1455             :             {
    1456             :                 // if (startPoint == -1)
    1457           6 :                 startPoint = vertexIndex71 - 1;
    1458           6 :                 poLR->setPoint(iPoint, &aoPoints[vertexIndex71 - 1]);
    1459           6 :                 iPoint++;
    1460           6 :                 vertexIndex71 = 0;
    1461             :             }
    1462           6 :             if (vertexIndex72 != 0 && vertexIndex72 <= aoPoints.size())
    1463             :             {
    1464           6 :                 if (startPoint == -1)
    1465           0 :                     startPoint = vertexIndex72 - 1;
    1466           6 :                 poLR->setPoint(iPoint, &aoPoints[vertexIndex72 - 1]);
    1467           6 :                 iPoint++;
    1468           6 :                 vertexIndex72 = 0;
    1469             :             }
    1470           6 :             if (vertexIndex73 != 0 && vertexIndex73 <= aoPoints.size())
    1471             :             {
    1472           6 :                 if (startPoint == -1)
    1473           0 :                     startPoint = vertexIndex73 - 1;
    1474           6 :                 poLR->setPoint(iPoint, &aoPoints[vertexIndex73 - 1]);
    1475           6 :                 iPoint++;
    1476           6 :                 vertexIndex73 = 0;
    1477             :             }
    1478           6 :             if (vertexIndex74 != 0 && vertexIndex74 <= aoPoints.size())
    1479             :             {
    1480           6 :                 if (startPoint == -1)
    1481           0 :                     startPoint = vertexIndex74 - 1;
    1482           6 :                 poLR->setPoint(iPoint, &aoPoints[vertexIndex74 - 1]);
    1483           6 :                 iPoint++;
    1484           6 :                 vertexIndex74 = 0;
    1485             :             }
    1486           6 :             if (startPoint >= 0)
    1487             :             {
    1488             :                 // complete the ring
    1489           6 :                 poLR->setPoint(iPoint, &aoPoints[startPoint]);
    1490             : 
    1491           6 :                 OGRPolygon *poPolygon = new OGRPolygon();
    1492           6 :                 poPolygon->addRingDirectly(poLR.release());
    1493             : 
    1494           6 :                 poPS->addGeometryDirectly(poPolygon);
    1495             :             }
    1496             :         }
    1497             : 
    1498          86 :         if (nCode < 0)
    1499             :         {
    1500           0 :             DXF_LAYER_READER_ERROR();
    1501           0 :             return nullptr;
    1502             :         }
    1503             : 
    1504             :         // Ignore Spline frame control points ( see #4683 )
    1505          86 :         if ((nVertexFlag & 16) == 0)
    1506          86 :             smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
    1507          86 :         dfBulge = 0.0;
    1508             :     }
    1509             : 
    1510          20 :     if (smoothPolyline.IsEmpty())
    1511             :     {
    1512           0 :         return nullptr;
    1513             :     }
    1514             : 
    1515          20 :     if (poPS->getNumGeometries() > 0)
    1516             :     {
    1517           1 :         poFeature->SetGeometryDirectly(poPS.release());
    1518           1 :         PrepareBrushStyle(poFeature.get());
    1519           1 :         return poFeature.release();
    1520             :     }
    1521             : 
    1522             :     /* -------------------------------------------------------------------- */
    1523             :     /*      Close polyline if necessary.                                    */
    1524             :     /* -------------------------------------------------------------------- */
    1525          19 :     const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
    1526          19 :     if (bIsClosed)
    1527          12 :         smoothPolyline.Close();
    1528             : 
    1529          19 :     const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
    1530             : 
    1531          19 :     smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
    1532          19 :     OGRGeometry *poGeom = smoothPolyline.Tessellate(bAsPolygon);
    1533             : 
    1534          19 :     if ((nPolylineFlag & 8) == 0)
    1535           4 :         poFeature->ApplyOCSTransformer(poGeom);
    1536          19 :     poFeature->SetGeometryDirectly(poGeom);
    1537             : 
    1538          19 :     PrepareLineStyle(poFeature.get());
    1539             : 
    1540          19 :     return poFeature.release();
    1541             : }
    1542             : 
    1543             : /************************************************************************/
    1544             : /*                           TranslateMLINE()                           */
    1545             : /************************************************************************/
    1546             : 
    1547           3 : OGRDXFFeature *OGRDXFLayer::TranslateMLINE()
    1548             : 
    1549             : {
    1550             :     char szLineBuf[257];
    1551           3 :     int nCode = 0;
    1552             : 
    1553           6 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1554             : 
    1555           3 :     bool bIsClosed = false;
    1556           3 :     int nNumVertices = 0;
    1557           3 :     int nNumElements = 0;
    1558             : 
    1559          57 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0 &&
    1560             :            nCode != 11)
    1561             :     {
    1562          54 :         switch (nCode)
    1563             :         {
    1564           3 :             case 71:
    1565           3 :                 bIsClosed = (atoi(szLineBuf) & 2) == 2;
    1566           3 :                 break;
    1567             : 
    1568           3 :             case 72:
    1569           3 :                 nNumVertices = atoi(szLineBuf);
    1570           3 :                 break;
    1571             : 
    1572           3 :             case 73:
    1573           3 :                 nNumElements = atoi(szLineBuf);
    1574             :                 // No-one should ever need more than 1000 elements!
    1575           3 :                 if (nNumElements <= 0 || nNumElements > 1000)
    1576             :                 {
    1577           0 :                     CPLDebug("DXF", "Invalid number of MLINE elements (73): %s",
    1578             :                              szLineBuf);
    1579           0 :                     DXF_LAYER_READER_ERROR();
    1580           0 :                     return nullptr;
    1581             :                 }
    1582           3 :                 break;
    1583             : 
    1584          45 :             default:
    1585          45 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1586          45 :                 break;
    1587             :         }
    1588             :     }
    1589           3 :     if (nCode < 0)
    1590             :     {
    1591           0 :         DXF_LAYER_READER_ERROR();
    1592           0 :         return nullptr;
    1593             :     }
    1594             : 
    1595           3 :     if (nCode == 0 || nCode == 11)
    1596           3 :         poDS->UnreadValue();
    1597             : 
    1598             :     /* -------------------------------------------------------------------- */
    1599             :     /*      Read in the position and parameters for each vertex, and        */
    1600             :     /*      translate these values into line geometries.                    */
    1601             :     /* -------------------------------------------------------------------- */
    1602             : 
    1603           6 :     auto poMLS = std::make_unique<OGRMultiLineString>();
    1604           6 :     std::vector<std::unique_ptr<OGRLineString>> apoCurrentLines(nNumElements);
    1605             : 
    1606             :     // For use when bIsClosed is true
    1607           6 :     std::vector<DXFTriple> aoInitialVertices(nNumElements);
    1608             : 
    1609             : #define EXPECT_CODE(code)                                                      \
    1610             :     if (poDS->ReadValue(szLineBuf, sizeof(szLineBuf)) != (code))               \
    1611             :     {                                                                          \
    1612             :         DXF_LAYER_READER_ERROR();                                              \
    1613             :         return nullptr;                                                        \
    1614             :     }
    1615             : 
    1616          13 :     for (int iVertex = 0; iVertex < nNumVertices; iVertex++)
    1617             :     {
    1618          10 :         EXPECT_CODE(11);
    1619          10 :         const double dfVertexX = CPLAtof(szLineBuf);
    1620          10 :         EXPECT_CODE(21);
    1621          10 :         const double dfVertexY = CPLAtof(szLineBuf);
    1622          10 :         EXPECT_CODE(31);
    1623          10 :         const double dfVertexZ = CPLAtof(szLineBuf);
    1624             : 
    1625          10 :         EXPECT_CODE(12);
    1626          10 :         const double dfSegmentDirectionX = CPLAtof(szLineBuf);
    1627          10 :         EXPECT_CODE(22);
    1628          10 :         const double dfSegmentDirectionY = CPLAtof(szLineBuf);
    1629          10 :         EXPECT_CODE(32);
    1630          10 :         const double dfSegmentDirectionZ = CPLAtof(szLineBuf);
    1631             : 
    1632          10 :         EXPECT_CODE(13);
    1633          10 :         const double dfMiterDirectionX = CPLAtof(szLineBuf);
    1634          10 :         EXPECT_CODE(23);
    1635          10 :         const double dfMiterDirectionY = CPLAtof(szLineBuf);
    1636          10 :         EXPECT_CODE(33);
    1637          10 :         const double dfMiterDirectionZ = CPLAtof(szLineBuf);
    1638             : 
    1639          34 :         for (int iElement = 0; iElement < nNumElements; iElement++)
    1640             :         {
    1641          24 :             double dfStartSegmentX = 0.0;
    1642          24 :             double dfStartSegmentY = 0.0;
    1643          24 :             double dfStartSegmentZ = 0.0;
    1644             : 
    1645          24 :             EXPECT_CODE(74);
    1646          24 :             const int nNumParameters = atoi(szLineBuf);
    1647             : 
    1648             :             // The first parameter is special: it is a distance along the
    1649             :             // miter vector from the initial vertex to the start of the
    1650             :             // element line.
    1651          24 :             if (nNumParameters > 0)
    1652             :             {
    1653          24 :                 EXPECT_CODE(41);
    1654          24 :                 const double dfDistance = CPLAtof(szLineBuf);
    1655             : 
    1656          24 :                 dfStartSegmentX = dfVertexX + dfMiterDirectionX * dfDistance;
    1657          24 :                 dfStartSegmentY = dfVertexY + dfMiterDirectionY * dfDistance;
    1658          24 :                 dfStartSegmentZ = dfVertexZ + dfMiterDirectionZ * dfDistance;
    1659             : 
    1660          24 :                 if (bIsClosed && iVertex == 0)
    1661             :                 {
    1662           3 :                     aoInitialVertices[iElement] = DXFTriple(
    1663             :                         dfStartSegmentX, dfStartSegmentY, dfStartSegmentZ);
    1664             :                 }
    1665             : 
    1666             :                 // If we have an unfinished line for this element, we need
    1667             :                 // to close it off.
    1668          24 :                 if (apoCurrentLines[iElement])
    1669             :                 {
    1670          16 :                     apoCurrentLines[iElement]->addPoint(
    1671             :                         dfStartSegmentX, dfStartSegmentY, dfStartSegmentZ);
    1672          32 :                     poMLS->addGeometryDirectly(
    1673          16 :                         apoCurrentLines[iElement].release());
    1674             :                 }
    1675             :             }
    1676             : 
    1677             :             // Parameters with an odd index give pen-up distances (breaks),
    1678             :             // while even indexes are pen-down distances (line segments).
    1679          61 :             for (int iParameter = 1; iParameter < nNumParameters; iParameter++)
    1680             :             {
    1681          37 :                 EXPECT_CODE(41);
    1682          37 :                 const double dfDistance = CPLAtof(szLineBuf);
    1683             : 
    1684          37 :                 const double dfCurrentX =
    1685          37 :                     dfStartSegmentX + dfSegmentDirectionX * dfDistance;
    1686          37 :                 const double dfCurrentY =
    1687          37 :                     dfStartSegmentY + dfSegmentDirectionY * dfDistance;
    1688          37 :                 const double dfCurrentZ =
    1689          37 :                     dfStartSegmentZ + dfSegmentDirectionZ * dfDistance;
    1690             : 
    1691          37 :                 if (iParameter % 2 == 0)
    1692             :                 {
    1693             :                     // The dfCurrent(X,Y,Z) point is the end of a line segment
    1694           7 :                     CPLAssert(apoCurrentLines[iElement]);
    1695           7 :                     apoCurrentLines[iElement]->addPoint(dfCurrentX, dfCurrentY,
    1696             :                                                         dfCurrentZ);
    1697          14 :                     poMLS->addGeometryDirectly(
    1698           7 :                         apoCurrentLines[iElement].release());
    1699             :                 }
    1700             :                 else
    1701             :                 {
    1702             :                     // The dfCurrent(X,Y,Z) point is the end of a break
    1703          30 :                     apoCurrentLines[iElement] =
    1704          60 :                         std::make_unique<OGRLineString>();
    1705          30 :                     apoCurrentLines[iElement]->addPoint(dfCurrentX, dfCurrentY,
    1706             :                                                         dfCurrentZ);
    1707             :                 }
    1708             :             }
    1709             : 
    1710          24 :             EXPECT_CODE(75);
    1711          24 :             const int nNumAreaFillParams = atoi(szLineBuf);
    1712             : 
    1713          24 :             for (int iParameter = 0; iParameter < nNumAreaFillParams;
    1714             :                  iParameter++)
    1715             :             {
    1716           0 :                 EXPECT_CODE(42);
    1717             :             }
    1718             :         }
    1719             :     }
    1720             : 
    1721             : #undef EXPECT_CODE
    1722             : 
    1723             :     // Close the MLINE if required.
    1724           3 :     if (bIsClosed)
    1725             :     {
    1726           4 :         for (int iElement = 0; iElement < nNumElements; iElement++)
    1727             :         {
    1728           3 :             if (apoCurrentLines[iElement])
    1729             :             {
    1730           6 :                 apoCurrentLines[iElement]->addPoint(
    1731           3 :                     aoInitialVertices[iElement].dfX,
    1732           3 :                     aoInitialVertices[iElement].dfY,
    1733           3 :                     aoInitialVertices[iElement].dfZ);
    1734           3 :                 poMLS->addGeometryDirectly(apoCurrentLines[iElement].release());
    1735             :             }
    1736             :         }
    1737             :     }
    1738             : 
    1739             :     // Apparently extrusions are ignored for MLINE entities.
    1740             :     // poFeature->ApplyOCSTransformer( poMLS );
    1741           3 :     poFeature->SetGeometryDirectly(poMLS.release());
    1742             : 
    1743           3 :     PrepareLineStyle(poFeature.get());
    1744             : 
    1745           3 :     return poFeature.release();
    1746             : }
    1747             : 
    1748             : /************************************************************************/
    1749             : /*                          TranslateCIRCLE()                           */
    1750             : /************************************************************************/
    1751             : 
    1752          24 : OGRDXFFeature *OGRDXFLayer::TranslateCIRCLE()
    1753             : 
    1754             : {
    1755             :     char szLineBuf[257];
    1756          24 :     int nCode = 0;
    1757          48 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1758          24 :     double dfX1 = 0.0;
    1759          24 :     double dfY1 = 0.0;
    1760          24 :     double dfZ1 = 0.0;
    1761          24 :     double dfRadius = 0.0;
    1762          24 :     double dfThickness = 0.0;
    1763          24 :     bool bHaveZ = false;
    1764             : 
    1765             :     /* -------------------------------------------------------------------- */
    1766             :     /*      Process values.                                                 */
    1767             :     /* -------------------------------------------------------------------- */
    1768         243 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1769             :     {
    1770         219 :         switch (nCode)
    1771             :         {
    1772          24 :             case 10:
    1773          24 :                 dfX1 = CPLAtof(szLineBuf);
    1774          24 :                 break;
    1775             : 
    1776          24 :             case 20:
    1777          24 :                 dfY1 = CPLAtof(szLineBuf);
    1778          24 :                 break;
    1779             : 
    1780          24 :             case 30:
    1781          24 :                 dfZ1 = CPLAtof(szLineBuf);
    1782          24 :                 bHaveZ = true;
    1783          24 :                 break;
    1784             : 
    1785           1 :             case 39:
    1786           1 :                 dfThickness = CPLAtof(szLineBuf);
    1787           1 :                 break;
    1788             : 
    1789          24 :             case 40:
    1790          24 :                 dfRadius = CPLAtof(szLineBuf);
    1791          24 :                 break;
    1792             : 
    1793         122 :             default:
    1794         122 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1795         122 :                 break;
    1796             :         }
    1797             :     }
    1798          24 :     if (nCode < 0)
    1799             :     {
    1800           0 :         DXF_LAYER_READER_ERROR();
    1801           0 :         return nullptr;
    1802             :     }
    1803             : 
    1804          24 :     if (nCode == 0)
    1805          24 :         poDS->UnreadValue();
    1806             : 
    1807             :     /* -------------------------------------------------------------------- */
    1808             :     /*      Create geometry                                                 */
    1809             :     /* -------------------------------------------------------------------- */
    1810             :     auto poCircle = std::unique_ptr<OGRLineString>(
    1811             :         OGRGeometryFactory::approximateArcAngles(dfX1, dfY1, dfZ1, dfRadius,
    1812             :                                                  dfRadius, 0.0, 0.0, 360.0, 0.0,
    1813          24 :                                                  poDS->InlineBlocks())
    1814          48 :             ->toLineString());
    1815             : 
    1816          24 :     const int nPoints = poCircle->getNumPoints();
    1817             : 
    1818             :     // If dfThickness is nonzero, we need to extrude a cylinder of height
    1819             :     // dfThickness in the Z axis.
    1820          24 :     if (dfThickness != 0.0 && nPoints > 1)
    1821             :     {
    1822           1 :         OGRPolyhedralSurface *poSurface = new OGRPolyhedralSurface();
    1823             : 
    1824             :         // Add the bottom base as a polygon
    1825           1 :         OGRLinearRing *poRing1 = new OGRLinearRing();
    1826           1 :         poRing1->addSubLineString(poCircle.get());
    1827             : 
    1828           1 :         OGRPolygon *poBase1 = new OGRPolygon();
    1829           1 :         poBase1->addRingDirectly(poRing1);
    1830           1 :         poSurface->addGeometryDirectly(poBase1);
    1831             : 
    1832             :         // Create and add the top base
    1833           1 :         OGRLinearRing *poRing2 = poRing1->clone();
    1834             : 
    1835           1 :         OGRDXFInsertTransformer oTransformer;
    1836           1 :         oTransformer.dfZOffset = dfThickness;
    1837           1 :         poRing2->transform(&oTransformer);
    1838             : 
    1839           1 :         OGRPolygon *poBase2 = new OGRPolygon();
    1840           1 :         poBase2->addRingDirectly(poRing2);
    1841           1 :         poSurface->addGeometryDirectly(poBase2);
    1842             : 
    1843             :         // Add the side of the cylinder as two "semicylindrical" polygons
    1844           2 :         auto poRect = std::make_unique<OGRLinearRing>();
    1845           2 :         OGRPoint oPoint;
    1846             : 
    1847          47 :         for (int iPoint = nPoints / 2; iPoint >= 0; iPoint--)
    1848             :         {
    1849          46 :             poRing1->getPoint(iPoint, &oPoint);
    1850          46 :             poRect->addPoint(&oPoint);
    1851             :         }
    1852          47 :         for (int iPoint = 0; iPoint <= nPoints / 2; iPoint++)
    1853             :         {
    1854          46 :             poRing2->getPoint(iPoint, &oPoint);
    1855          46 :             poRect->addPoint(&oPoint);
    1856             :         }
    1857             : 
    1858           1 :         poRect->closeRings();
    1859             : 
    1860           1 :         OGRPolygon *poRectPolygon = new OGRPolygon();
    1861           1 :         poRectPolygon->addRingDirectly(poRect.release());
    1862           1 :         poSurface->addGeometryDirectly(poRectPolygon);
    1863             : 
    1864           1 :         poRect = std::make_unique<OGRLinearRing>();
    1865             : 
    1866          47 :         for (int iPoint = nPoints - 1; iPoint >= nPoints / 2; iPoint--)
    1867             :         {
    1868          46 :             poRing1->getPoint(iPoint, &oPoint);
    1869          46 :             poRect->addPoint(&oPoint);
    1870             :         }
    1871          47 :         for (int iPoint = nPoints / 2; iPoint < nPoints; iPoint++)
    1872             :         {
    1873          46 :             poRing2->getPoint(iPoint, &oPoint);
    1874          46 :             poRect->addPoint(&oPoint);
    1875             :         }
    1876             : 
    1877           1 :         poRect->closeRings();
    1878             : 
    1879           1 :         poRectPolygon = new OGRPolygon();
    1880           1 :         poRectPolygon->addRingDirectly(poRect.release());
    1881           1 :         poSurface->addGeometryDirectly(poRectPolygon);
    1882             : 
    1883             :         // That's your cylinder, folks
    1884           1 :         poFeature->ApplyOCSTransformer(poSurface);
    1885           2 :         poFeature->SetGeometryDirectly(poSurface);
    1886             :     }
    1887             :     else
    1888             :     {
    1889          23 :         if (!bHaveZ)
    1890           0 :             poCircle->flattenTo2D();
    1891             : 
    1892          23 :         poFeature->ApplyOCSTransformer(poCircle.get());
    1893          23 :         poFeature->SetGeometryDirectly(poCircle.release());
    1894             :     }
    1895             : 
    1896          24 :     PrepareLineStyle(poFeature.get());
    1897             : 
    1898          24 :     return poFeature.release();
    1899             : }
    1900             : 
    1901             : /************************************************************************/
    1902             : /*                          TranslateELLIPSE()                          */
    1903             : /************************************************************************/
    1904             : 
    1905          29 : OGRDXFFeature *OGRDXFLayer::TranslateELLIPSE()
    1906             : 
    1907             : {
    1908             :     char szLineBuf[257];
    1909          29 :     int nCode = 0;
    1910          58 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1911          29 :     double dfX1 = 0.0;
    1912          29 :     double dfY1 = 0.0;
    1913          29 :     double dfZ1 = 0.0;
    1914          29 :     double dfRatio = 0.0;
    1915          29 :     double dfStartAngle = 0.0;
    1916          29 :     double dfEndAngle = 360.0;
    1917          29 :     double dfAxisX = 0.0;
    1918          29 :     double dfAxisY = 0.0;
    1919          29 :     double dfAxisZ = 0.0;
    1920          29 :     bool bHaveZ = false;
    1921          29 :     bool bApplyOCSTransform = false;
    1922             : 
    1923             :     /* -------------------------------------------------------------------- */
    1924             :     /*      Process values.                                                 */
    1925             :     /* -------------------------------------------------------------------- */
    1926         555 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1927             :     {
    1928         526 :         switch (nCode)
    1929             :         {
    1930          29 :             case 10:
    1931          29 :                 dfX1 = CPLAtof(szLineBuf);
    1932          29 :                 break;
    1933             : 
    1934          29 :             case 20:
    1935          29 :                 dfY1 = CPLAtof(szLineBuf);
    1936          29 :                 break;
    1937             : 
    1938          29 :             case 30:
    1939          29 :                 dfZ1 = CPLAtof(szLineBuf);
    1940          29 :                 bHaveZ = true;
    1941          29 :                 break;
    1942             : 
    1943          29 :             case 11:
    1944          29 :                 dfAxisX = CPLAtof(szLineBuf);
    1945          29 :                 break;
    1946             : 
    1947          29 :             case 21:
    1948          29 :                 dfAxisY = CPLAtof(szLineBuf);
    1949          29 :                 break;
    1950             : 
    1951          29 :             case 31:
    1952          29 :                 dfAxisZ = CPLAtof(szLineBuf);
    1953          29 :                 break;
    1954             : 
    1955          29 :             case 40:
    1956          29 :                 dfRatio = CPLAtof(szLineBuf);
    1957          29 :                 break;
    1958             : 
    1959          29 :             case 41:
    1960             :                 // These *seem* to always be in radians regardless of $AUNITS
    1961          29 :                 dfEndAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
    1962          29 :                 break;
    1963             : 
    1964          29 :             case 42:
    1965             :                 // These *seem* to always be in radians regardless of $AUNITS
    1966          29 :                 dfStartAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
    1967          29 :                 break;
    1968             : 
    1969         265 :             default:
    1970         265 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1971         265 :                 break;
    1972             :         }
    1973             :     }
    1974          29 :     if (nCode < 0)
    1975             :     {
    1976           0 :         DXF_LAYER_READER_ERROR();
    1977           0 :         return nullptr;
    1978             :     }
    1979             : 
    1980          29 :     if (nCode == 0)
    1981          29 :         poDS->UnreadValue();
    1982             : 
    1983             :     /* -------------------------------------------------------------------- */
    1984             :     /*      Setup coordinate system                                         */
    1985             :     /* -------------------------------------------------------------------- */
    1986             :     double adfN[3];
    1987          29 :     poFeature->oOCS.ToArray(adfN);
    1988             : 
    1989          29 :     if ((adfN[0] == 0.0 && adfN[1] == 0.0 && adfN[2] == 1.0) == false)
    1990             :     {
    1991          14 :         OGRDXFOCSTransformer oTransformer(adfN, true);
    1992             : 
    1993           7 :         bApplyOCSTransform = true;
    1994             : 
    1995           7 :         double *x = &dfX1;
    1996           7 :         double *y = &dfY1;
    1997           7 :         double *z = &dfZ1;
    1998           7 :         oTransformer.InverseTransform(1, x, y, z);
    1999             : 
    2000           7 :         x = &dfAxisX;
    2001           7 :         y = &dfAxisY;
    2002           7 :         z = &dfAxisZ;
    2003           7 :         oTransformer.InverseTransform(1, x, y, z);
    2004             :     }
    2005             : 
    2006             :     /* -------------------------------------------------------------------- */
    2007             :     /*      Compute primary and secondary axis lengths, and the angle of    */
    2008             :     /*      rotation for the ellipse.                                       */
    2009             :     /* -------------------------------------------------------------------- */
    2010             :     double dfPrimaryRadius =
    2011          29 :         sqrt(dfAxisX * dfAxisX + dfAxisY * dfAxisY + dfAxisZ * dfAxisZ);
    2012             : 
    2013          29 :     double dfSecondaryRadius = dfRatio * dfPrimaryRadius;
    2014             : 
    2015          29 :     double dfRotation = -1 * atan2(dfAxisY, dfAxisX) * 180 / M_PI;
    2016             : 
    2017             :     /* -------------------------------------------------------------------- */
    2018             :     /*      Create geometry                                                 */
    2019             :     /* -------------------------------------------------------------------- */
    2020          29 :     if (dfStartAngle > dfEndAngle)
    2021           0 :         dfEndAngle += 360.0;
    2022             : 
    2023          29 :     if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
    2024             :     {
    2025             :         // Only honor OGR_DXF_MAX_GAP if this geometry isn't at risk of
    2026             :         // being enlarged or shrunk as part of a block insertion.
    2027             :         auto poEllipse = std::unique_ptr<OGRGeometry>(
    2028             :             OGRGeometryFactory::approximateArcAngles(
    2029             :                 dfX1, dfY1, dfZ1, dfPrimaryRadius, dfSecondaryRadius,
    2030             :                 dfRotation, dfStartAngle, dfEndAngle, 0.0,
    2031          58 :                 poDS->InlineBlocks()));
    2032             : 
    2033          29 :         if (!bHaveZ)
    2034           0 :             poEllipse->flattenTo2D();
    2035             : 
    2036          29 :         if (bApplyOCSTransform == true)
    2037           7 :             poFeature->ApplyOCSTransformer(poEllipse.get());
    2038          29 :         poFeature->SetGeometryDirectly(poEllipse.release());
    2039             :     }
    2040             :     else
    2041             :     {
    2042             :         // TODO: emit error ?
    2043             :     }
    2044             : 
    2045          29 :     PrepareLineStyle(poFeature.get());
    2046             : 
    2047          29 :     return poFeature.release();
    2048             : }
    2049             : 
    2050             : /************************************************************************/
    2051             : /*                            TranslateARC()                            */
    2052             : /************************************************************************/
    2053             : 
    2054          14 : OGRDXFFeature *OGRDXFLayer::TranslateARC()
    2055             : 
    2056             : {
    2057             :     char szLineBuf[257];
    2058          14 :     int nCode = 0;
    2059          28 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2060          14 :     double dfX1 = 0.0;
    2061          14 :     double dfY1 = 0.0;
    2062          14 :     double dfZ1 = 0.0;
    2063          14 :     double dfRadius = 0.0;
    2064          14 :     double dfStartAngle = 0.0;
    2065          14 :     double dfEndAngle = 360.0;
    2066          14 :     bool bHaveZ = false;
    2067             : 
    2068             :     /* -------------------------------------------------------------------- */
    2069             :     /*      Process values.                                                 */
    2070             :     /* -------------------------------------------------------------------- */
    2071         202 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2072             :     {
    2073         188 :         switch (nCode)
    2074             :         {
    2075          14 :             case 10:
    2076          14 :                 dfX1 = CPLAtof(szLineBuf);
    2077          14 :                 break;
    2078             : 
    2079          14 :             case 20:
    2080          14 :                 dfY1 = CPLAtof(szLineBuf);
    2081          14 :                 break;
    2082             : 
    2083          14 :             case 30:
    2084          14 :                 dfZ1 = CPLAtof(szLineBuf);
    2085          14 :                 bHaveZ = true;
    2086          14 :                 break;
    2087             : 
    2088          14 :             case 40:
    2089          14 :                 dfRadius = CPLAtof(szLineBuf);
    2090          14 :                 break;
    2091             : 
    2092          14 :             case 50:
    2093             :                 // This is apparently always degrees regardless of AUNITS
    2094          14 :                 dfEndAngle = -1 * CPLAtof(szLineBuf);
    2095          14 :                 break;
    2096             : 
    2097          14 :             case 51:
    2098             :                 // This is apparently always degrees regardless of AUNITS
    2099          14 :                 dfStartAngle = -1 * CPLAtof(szLineBuf);
    2100          14 :                 break;
    2101             : 
    2102         104 :             default:
    2103         104 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2104         104 :                 break;
    2105             :         }
    2106             :     }
    2107          14 :     if (nCode < 0)
    2108             :     {
    2109           0 :         DXF_LAYER_READER_ERROR();
    2110           0 :         return nullptr;
    2111             :     }
    2112             : 
    2113          14 :     if (nCode == 0)
    2114          14 :         poDS->UnreadValue();
    2115             : 
    2116             :     /* -------------------------------------------------------------------- */
    2117             :     /*      Create geometry                                                 */
    2118             :     /* -------------------------------------------------------------------- */
    2119          14 :     if (dfStartAngle > dfEndAngle)
    2120          13 :         dfEndAngle += 360.0;
    2121             : 
    2122          14 :     if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
    2123             :     {
    2124             :         auto poArc = std::unique_ptr<OGRGeometry>(
    2125             :             OGRGeometryFactory::approximateArcAngles(
    2126             :                 dfX1, dfY1, dfZ1, dfRadius, dfRadius, 0.0, dfStartAngle,
    2127          28 :                 dfEndAngle, 0.0, poDS->InlineBlocks()));
    2128          14 :         if (!bHaveZ)
    2129           0 :             poArc->flattenTo2D();
    2130             : 
    2131          14 :         poFeature->ApplyOCSTransformer(poArc.get());
    2132          14 :         poFeature->SetGeometryDirectly(poArc.release());
    2133             :     }
    2134             :     else
    2135             :     {
    2136             :         // TODO: emit error ?
    2137             :     }
    2138             : 
    2139          14 :     PrepareLineStyle(poFeature.get());
    2140             : 
    2141          14 :     return poFeature.release();
    2142             : }
    2143             : 
    2144             : /************************************************************************/
    2145             : /*                          TranslateSPLINE()                           */
    2146             : /************************************************************************/
    2147             : 
    2148             : void rbspline2(int npts, int k, int p1, double b[], double h[],
    2149             :                bool bCalculateKnots, double knots[], double p[]);
    2150             : 
    2151          25 : OGRDXFFeature *OGRDXFLayer::TranslateSPLINE()
    2152             : 
    2153             : {
    2154             :     char szLineBuf[257];
    2155             :     int nCode;
    2156          50 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2157             : 
    2158          50 :     std::vector<double> adfControlPoints(FORTRAN_INDEXING, 0.0);
    2159          50 :     std::vector<double> adfKnots(FORTRAN_INDEXING, 0.0);
    2160          50 :     std::vector<double> adfWeights(FORTRAN_INDEXING, 0.0);
    2161          25 :     int nDegree = -1;
    2162          25 :     int nControlPoints = -1;
    2163          25 :     int nKnots = -1;
    2164          25 :     bool bInsertNullZ = false;
    2165          25 :     bool bHasZ = false;
    2166             : 
    2167             :     /* -------------------------------------------------------------------- */
    2168             :     /*      Process values.                                                 */
    2169             :     /* -------------------------------------------------------------------- */
    2170        1363 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2171             :     {
    2172        1339 :         bool bStop = false;
    2173        1339 :         switch (nCode)
    2174             :         {
    2175         138 :             case 10:
    2176         138 :                 if (bInsertNullZ)
    2177             :                 {
    2178           0 :                     adfControlPoints.push_back(0.0);
    2179           0 :                     bInsertNullZ = false;
    2180             :                 }
    2181         138 :                 adfControlPoints.push_back(CPLAtof(szLineBuf));
    2182         138 :                 break;
    2183             : 
    2184         138 :             case 20:
    2185         138 :                 adfControlPoints.push_back(CPLAtof(szLineBuf));
    2186         138 :                 bInsertNullZ = true;
    2187         138 :                 break;
    2188             : 
    2189         138 :             case 30:
    2190         138 :                 adfControlPoints.push_back(CPLAtof(szLineBuf));
    2191         138 :                 bHasZ = true;
    2192         138 :                 bInsertNullZ = false;
    2193         138 :                 break;
    2194             : 
    2195         227 :             case 40:
    2196             :             {
    2197         227 :                 double dfVal = CPLAtof(szLineBuf);
    2198             :                 // Ad-hoc fix for https://github.com/OSGeo/gdal/issues/1969
    2199             :                 // where the first knot is at a very very close to zero negative
    2200             :                 // value and following knots are at 0.
    2201         227 :                 if (dfVal < 0 && dfVal > -1.0e-10)
    2202           1 :                     dfVal = 0;
    2203         227 :                 adfKnots.push_back(dfVal);
    2204         227 :                 break;
    2205             :             }
    2206             : 
    2207          10 :             case 41:
    2208          10 :                 adfWeights.push_back(CPLAtof(szLineBuf));
    2209          10 :                 break;
    2210             : 
    2211          25 :             case 70:
    2212          25 :                 break;
    2213             : 
    2214          25 :             case 71:
    2215          25 :                 nDegree = atoi(szLineBuf);
    2216             :                 // Arbitrary threshold
    2217          25 :                 if (nDegree < 0 || nDegree > 100)
    2218             :                 {
    2219           0 :                     DXF_LAYER_READER_ERROR();
    2220           0 :                     return nullptr;
    2221             :                 }
    2222          25 :                 break;
    2223             : 
    2224          25 :             case 72:
    2225          25 :                 nKnots = atoi(szLineBuf);
    2226             :                 // Arbitrary threshold
    2227          25 :                 if (nKnots < 0 || nKnots > 10000000)
    2228             :                 {
    2229           0 :                     DXF_LAYER_READER_ERROR();
    2230           0 :                     return nullptr;
    2231             :                 }
    2232          25 :                 break;
    2233             : 
    2234          25 :             case 73:
    2235          25 :                 nControlPoints = atoi(szLineBuf);
    2236             :                 // Arbitrary threshold
    2237          25 :                 if (nControlPoints < 0 || nControlPoints > 10000000)
    2238             :                 {
    2239           0 :                     DXF_LAYER_READER_ERROR();
    2240           0 :                     return nullptr;
    2241             :                 }
    2242          25 :                 break;
    2243             : 
    2244          51 :             case 100:
    2245          51 :                 if (EQUAL(szLineBuf, "AcDbHelix"))
    2246           1 :                     bStop = true;
    2247          51 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2248          51 :                 break;
    2249             : 
    2250         537 :             default:
    2251         537 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2252         537 :                 break;
    2253             :         }
    2254             : 
    2255        1339 :         if (bStop)
    2256           1 :             break;
    2257             :     }
    2258          25 :     if (nCode < 0)
    2259             :     {
    2260           0 :         DXF_LAYER_READER_ERROR();
    2261           0 :         return nullptr;
    2262             :     }
    2263             : 
    2264          25 :     if (nCode == 0)
    2265          24 :         poDS->UnreadValue();
    2266             : 
    2267          25 :     if (bInsertNullZ)
    2268             :     {
    2269           0 :         adfControlPoints.push_back(0.0);
    2270             :     }
    2271             : 
    2272          25 :     if (static_cast<int>(adfControlPoints.size() % 3) != FORTRAN_INDEXING)
    2273             :     {
    2274           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2275             :                  "Invalid number of values for spline control points");
    2276           0 :         DXF_LAYER_READER_ERROR();
    2277           0 :         return nullptr;
    2278             :     }
    2279             : 
    2280             :     /* -------------------------------------------------------------------- */
    2281             :     /*      Use the helper function to check the input data and insert      */
    2282             :     /*      the spline.                                                     */
    2283             :     /* -------------------------------------------------------------------- */
    2284             :     auto poLS =
    2285             :         InsertSplineWithChecks(nDegree, adfControlPoints, bHasZ, nControlPoints,
    2286          50 :                                adfKnots, nKnots, adfWeights);
    2287             : 
    2288          25 :     if (!poLS)
    2289             :     {
    2290           0 :         DXF_LAYER_READER_ERROR();
    2291           0 :         return nullptr;
    2292             :     }
    2293             : 
    2294          25 :     poFeature->SetGeometryDirectly(poLS.release());
    2295             : 
    2296          25 :     PrepareLineStyle(poFeature.get());
    2297             : 
    2298          25 :     return poFeature.release();
    2299             : }
    2300             : 
    2301             : /************************************************************************/
    2302             : /*                       InsertSplineWithChecks()                       */
    2303             : /*                                                                      */
    2304             : /*     Inserts a spline based on unchecked DXF input.  The arrays are   */
    2305             : /*     one-based.                                                       */
    2306             : /************************************************************************/
    2307             : 
    2308          27 : std::unique_ptr<OGRLineString> OGRDXFLayer::InsertSplineWithChecks(
    2309             :     const int nDegree, std::vector<double> &adfControlPoints, bool bHasZ,
    2310             :     int nControlPoints, std::vector<double> &adfKnots, int nKnots,
    2311             :     std::vector<double> &adfWeights)
    2312             : {
    2313             :     /* -------------------------------------------------------------------- */
    2314             :     /*      Sanity checks                                                   */
    2315             :     /* -------------------------------------------------------------------- */
    2316          27 :     const int nOrder = nDegree + 1;
    2317             : 
    2318          27 :     bool bResult = (nOrder >= 2);
    2319          27 :     if (bResult == true)
    2320             :     {
    2321             :         // Check whether nctrlpts value matches number of vertices read
    2322             :         int nCheck =
    2323          27 :             (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) / 3;
    2324             : 
    2325          27 :         if (nControlPoints == -1)
    2326           0 :             nControlPoints =
    2327           0 :                 (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) /
    2328             :                 3;
    2329             : 
    2330             :         // min( num(ctrlpts) ) = order
    2331          27 :         bResult = (nControlPoints >= nOrder && nControlPoints == nCheck);
    2332             :     }
    2333             : 
    2334          27 :     bool bCalculateKnots = false;
    2335          27 :     if (bResult == true)
    2336             :     {
    2337          27 :         int nCheck = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
    2338             : 
    2339             :         // Recalculate knots when:
    2340             :         // - no knots data present, nknots is -1 and ncheck is 0
    2341             :         // - nknots value present, no knot vertices
    2342             :         //   nknots is (nctrlpts + order), ncheck is 0
    2343          27 :         if (nCheck == 0)
    2344             :         {
    2345           1 :             bCalculateKnots = true;
    2346          12 :             for (int i = 0; i < (nControlPoints + nOrder); i++)
    2347          11 :                 adfKnots.push_back(0.0);
    2348             : 
    2349           1 :             nCheck = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
    2350             :         }
    2351             :         // Adjust nknots value when:
    2352             :         // - nknots value not present, knot vertices present
    2353             :         //   nknots is -1, ncheck is (nctrlpts + order)
    2354          27 :         if (nKnots == -1)
    2355           0 :             nKnots = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
    2356             : 
    2357             :         // num(knots) = num(ctrlpts) + order
    2358          27 :         bResult = (nKnots == (nControlPoints + nOrder) && nKnots == nCheck);
    2359             :     }
    2360             : 
    2361          27 :     if (bResult == true)
    2362             :     {
    2363          27 :         int nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
    2364             : 
    2365          27 :         if (nWeights == 0)
    2366             :         {
    2367         158 :             for (int i = 0; i < nControlPoints; i++)
    2368         134 :                 adfWeights.push_back(1.0);
    2369             : 
    2370          24 :             nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
    2371             :         }
    2372             : 
    2373             :         // num(weights) = num(ctrlpts)
    2374          27 :         bResult = (nWeights == nControlPoints);
    2375             :     }
    2376             : 
    2377          27 :     if (bResult == false)
    2378           0 :         return nullptr;
    2379             : 
    2380             :     /* -------------------------------------------------------------------- */
    2381             :     /*      Interpolate spline                                              */
    2382             :     /* -------------------------------------------------------------------- */
    2383          27 :     int p1 = nControlPoints * 8;
    2384          54 :     std::vector<double> p(3 * p1 + FORTRAN_INDEXING);
    2385             : 
    2386          27 :     rbspline2(nControlPoints, nOrder, p1, &(adfControlPoints[0]),
    2387          27 :               &(adfWeights[0]), bCalculateKnots, &(adfKnots[0]), &(p[0]));
    2388             : 
    2389             :     /* -------------------------------------------------------------------- */
    2390             :     /*      Turn into OGR geometry.                                         */
    2391             :     /* -------------------------------------------------------------------- */
    2392          54 :     auto poLS = std::make_unique<OGRLineString>();
    2393             : 
    2394          27 :     poLS->setNumPoints(p1);
    2395          27 :     if (bHasZ)
    2396             :     {
    2397        1129 :         for (int i = 0; i < p1; i++)
    2398        2208 :             poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
    2399        1104 :                            p[i * 3 + FORTRAN_INDEXING + 1],
    2400        1104 :                            p[i * 3 + FORTRAN_INDEXING + 2]);
    2401             :     }
    2402             :     else
    2403             :     {
    2404         106 :         for (int i = 0; i < p1; i++)
    2405         208 :             poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
    2406         104 :                            p[i * 3 + FORTRAN_INDEXING + 1]);
    2407             :     }
    2408             : 
    2409          27 :     return poLS;
    2410             : }
    2411             : 
    2412             : /************************************************************************/
    2413             : /*                          Translate3DFACE()                           */
    2414             : /************************************************************************/
    2415             : 
    2416          10 : OGRDXFFeature *OGRDXFLayer::Translate3DFACE()
    2417             : 
    2418             : {
    2419             :     char szLineBuf[257];
    2420          10 :     int nCode = 0;
    2421          20 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2422          10 :     double dfX1 = 0.0;
    2423          10 :     double dfY1 = 0.0;
    2424          10 :     double dfZ1 = 0.0;
    2425          10 :     double dfX2 = 0.0;
    2426          10 :     double dfY2 = 0.0;
    2427          10 :     double dfZ2 = 0.0;
    2428          10 :     double dfX3 = 0.0;
    2429          10 :     double dfY3 = 0.0;
    2430          10 :     double dfZ3 = 0.0;
    2431          10 :     double dfX4 = 0.0;
    2432          10 :     double dfY4 = 0.0;
    2433          10 :     double dfZ4 = 0.0;
    2434             : 
    2435             :     /* -------------------------------------------------------------------- */
    2436             :     /*      Process values.                                                 */
    2437             :     /* -------------------------------------------------------------------- */
    2438         172 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2439             :     {
    2440         162 :         switch (nCode)
    2441             :         {
    2442          10 :             case 10:
    2443          10 :                 dfX1 = CPLAtof(szLineBuf);
    2444          10 :                 break;
    2445             : 
    2446          10 :             case 11:
    2447          10 :                 dfX2 = CPLAtof(szLineBuf);
    2448          10 :                 break;
    2449             : 
    2450          10 :             case 12:
    2451          10 :                 dfX3 = CPLAtof(szLineBuf);
    2452          10 :                 break;
    2453             : 
    2454          10 :             case 13:
    2455          10 :                 dfX4 = CPLAtof(szLineBuf);
    2456          10 :                 break;
    2457             : 
    2458          10 :             case 20:
    2459          10 :                 dfY1 = CPLAtof(szLineBuf);
    2460          10 :                 break;
    2461             : 
    2462          10 :             case 21:
    2463          10 :                 dfY2 = CPLAtof(szLineBuf);
    2464          10 :                 break;
    2465             : 
    2466          10 :             case 22:
    2467          10 :                 dfY3 = CPLAtof(szLineBuf);
    2468          10 :                 break;
    2469             : 
    2470          10 :             case 23:
    2471          10 :                 dfY4 = CPLAtof(szLineBuf);
    2472          10 :                 break;
    2473             : 
    2474          10 :             case 30:
    2475          10 :                 dfZ1 = CPLAtof(szLineBuf);
    2476          10 :                 break;
    2477             : 
    2478          10 :             case 31:
    2479          10 :                 dfZ2 = CPLAtof(szLineBuf);
    2480          10 :                 break;
    2481             : 
    2482          10 :             case 32:
    2483          10 :                 dfZ3 = CPLAtof(szLineBuf);
    2484          10 :                 break;
    2485             : 
    2486          10 :             case 33:
    2487          10 :                 dfZ4 = CPLAtof(szLineBuf);
    2488          10 :                 break;
    2489             : 
    2490          42 :             default:
    2491          42 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2492          42 :                 break;
    2493             :         }
    2494             :     }
    2495          10 :     if (nCode < 0)
    2496             :     {
    2497           0 :         DXF_LAYER_READER_ERROR();
    2498           0 :         return nullptr;
    2499             :     }
    2500             : 
    2501          10 :     if (nCode == 0)
    2502          10 :         poDS->UnreadValue();
    2503             : 
    2504             :     /* -------------------------------------------------------------------- */
    2505             :     /*      Create geometry                                                 */
    2506             :     /* -------------------------------------------------------------------- */
    2507          20 :     auto poPoly = std::make_unique<OGRPolygon>();
    2508          10 :     OGRLinearRing *poLR = new OGRLinearRing();
    2509          10 :     poLR->addPoint(dfX1, dfY1, dfZ1);
    2510          10 :     poLR->addPoint(dfX2, dfY2, dfZ2);
    2511          10 :     poLR->addPoint(dfX3, dfY3, dfZ3);
    2512          10 :     if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
    2513           9 :         poLR->addPoint(dfX4, dfY4, dfZ4);
    2514          10 :     poPoly->addRingDirectly(poLR);
    2515          10 :     poPoly->closeRings();
    2516             : 
    2517          10 :     poFeature->ApplyOCSTransformer(poLR);
    2518          10 :     poFeature->SetGeometryDirectly(poPoly.release());
    2519             : 
    2520          10 :     PrepareLineStyle(poFeature.get());
    2521             : 
    2522          10 :     return poFeature.release();
    2523             : }
    2524             : 
    2525             : /* -------------------------------------------------------------------- */
    2526             : /*      PointXAxisComparer                                              */
    2527             : /*                                                                      */
    2528             : /*      Returns true if oP1 is to the left of oP2, or they have the     */
    2529             : /*      same x-coordinate and oP1 is below oP2.                         */
    2530             : /* -------------------------------------------------------------------- */
    2531             : 
    2532         150 : static bool PointXAxisComparer(const OGRPoint &oP1, const OGRPoint &oP2)
    2533             : {
    2534         256 :     return oP1.getX() == oP2.getX() ? oP1.getY() < oP2.getY()
    2535         256 :                                     : oP1.getX() < oP2.getX();
    2536             : }
    2537             : 
    2538             : /* -------------------------------------------------------------------- */
    2539             : /*      PointXYZEqualityComparer                                        */
    2540             : /*                                                                      */
    2541             : /*      Returns true if oP1 is equal to oP2 in the X, Y and Z axes.     */
    2542             : /* -------------------------------------------------------------------- */
    2543             : 
    2544          75 : static bool PointXYZEqualityComparer(const OGRPoint &oP1, const OGRPoint &oP2)
    2545             : {
    2546          86 :     return oP1.getX() == oP2.getX() && oP1.getY() == oP2.getY() &&
    2547          86 :            oP1.getZ() == oP2.getZ();
    2548             : }
    2549             : 
    2550             : /************************************************************************/
    2551             : /*                           TranslateSOLID()                           */
    2552             : /************************************************************************/
    2553             : 
    2554          25 : OGRDXFFeature *OGRDXFLayer::TranslateSOLID()
    2555             : 
    2556             : {
    2557             :     char szLineBuf[257];
    2558          25 :     int nCode = 0;
    2559          50 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2560          25 :     double dfX1 = 0.0;
    2561          25 :     double dfY1 = 0.0;
    2562          25 :     double dfZ1 = 0.0;
    2563          25 :     double dfX2 = 0.0;
    2564          25 :     double dfY2 = 0.0;
    2565          25 :     double dfZ2 = 0.0;
    2566          25 :     double dfX3 = 0.0;
    2567          25 :     double dfY3 = 0.0;
    2568          25 :     double dfZ3 = 0.0;
    2569          25 :     double dfX4 = 0.0;
    2570          25 :     double dfY4 = 0.0;
    2571          25 :     double dfZ4 = 0.0;
    2572             : 
    2573         472 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2574             :     {
    2575         447 :         switch (nCode)
    2576             :         {
    2577          25 :             case 10:
    2578          25 :                 dfX1 = CPLAtof(szLineBuf);
    2579          25 :                 break;
    2580             : 
    2581          25 :             case 20:
    2582          25 :                 dfY1 = CPLAtof(szLineBuf);
    2583          25 :                 break;
    2584             : 
    2585          25 :             case 30:
    2586          25 :                 dfZ1 = CPLAtof(szLineBuf);
    2587          25 :                 break;
    2588             : 
    2589          25 :             case 11:
    2590          25 :                 dfX2 = CPLAtof(szLineBuf);
    2591          25 :                 break;
    2592             : 
    2593          25 :             case 21:
    2594          25 :                 dfY2 = CPLAtof(szLineBuf);
    2595          25 :                 break;
    2596             : 
    2597          25 :             case 31:
    2598          25 :                 dfZ2 = CPLAtof(szLineBuf);
    2599          25 :                 break;
    2600             : 
    2601          25 :             case 12:
    2602          25 :                 dfX3 = CPLAtof(szLineBuf);
    2603          25 :                 break;
    2604             : 
    2605          25 :             case 22:
    2606          25 :                 dfY3 = CPLAtof(szLineBuf);
    2607          25 :                 break;
    2608             : 
    2609          25 :             case 32:
    2610          25 :                 dfZ3 = CPLAtof(szLineBuf);
    2611          25 :                 break;
    2612             : 
    2613          25 :             case 13:
    2614          25 :                 dfX4 = CPLAtof(szLineBuf);
    2615          25 :                 break;
    2616             : 
    2617          25 :             case 23:
    2618          25 :                 dfY4 = CPLAtof(szLineBuf);
    2619          25 :                 break;
    2620             : 
    2621          25 :             case 33:
    2622          25 :                 dfZ4 = CPLAtof(szLineBuf);
    2623          25 :                 break;
    2624             : 
    2625         147 :             default:
    2626         147 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2627         147 :                 break;
    2628             :         }
    2629             :     }
    2630          25 :     if (nCode < 0)
    2631             :     {
    2632           0 :         DXF_LAYER_READER_ERROR();
    2633           0 :         return nullptr;
    2634             :     }
    2635          25 :     if (nCode == 0)
    2636          25 :         poDS->UnreadValue();
    2637             : 
    2638             :     // do we want Z-coordinates?
    2639          25 :     const bool bWantZ =
    2640          25 :         dfZ1 != 0.0 || dfZ2 != 0.0 || dfZ3 != 0.0 || dfZ4 != 0.0;
    2641             : 
    2642             :     // check how many unique corners we have
    2643         250 :     OGRPoint oCorners[4];
    2644          25 :     oCorners[0].setX(dfX1);
    2645          25 :     oCorners[0].setY(dfY1);
    2646          25 :     if (bWantZ)
    2647           3 :         oCorners[0].setZ(dfZ1);
    2648          25 :     oCorners[1].setX(dfX2);
    2649          25 :     oCorners[1].setY(dfY2);
    2650          25 :     if (bWantZ)
    2651           3 :         oCorners[1].setZ(dfZ2);
    2652          25 :     oCorners[2].setX(dfX3);
    2653          25 :     oCorners[2].setY(dfY3);
    2654          25 :     if (bWantZ)
    2655           3 :         oCorners[2].setZ(dfZ3);
    2656          25 :     oCorners[3].setX(dfX4);
    2657          25 :     oCorners[3].setY(dfY4);
    2658          25 :     if (bWantZ)
    2659           3 :         oCorners[3].setZ(dfZ4);
    2660             : 
    2661          25 :     std::sort(&oCorners[0], &oCorners[4], PointXAxisComparer);
    2662             :     int nCornerCount = static_cast<int>(
    2663          25 :         std::unique(&oCorners[0], &oCorners[4], PointXYZEqualityComparer) -
    2664          25 :         &oCorners[0]);
    2665          25 :     if (nCornerCount < 1)
    2666             :     {
    2667           0 :         DXF_LAYER_READER_ERROR();
    2668           0 :         return nullptr;
    2669             :     }
    2670             : 
    2671          25 :     std::unique_ptr<OGRGeometry> poFinalGeom;
    2672             : 
    2673             :     // what kind of object do we need?
    2674          25 :     if (nCornerCount == 1)
    2675             :     {
    2676           1 :         poFinalGeom.reset(oCorners[0].clone());
    2677             : 
    2678           1 :         PrepareLineStyle(poFeature.get());
    2679             :     }
    2680          24 :     else if (nCornerCount == 2)
    2681             :     {
    2682           2 :         auto poLS = std::make_unique<OGRLineString>();
    2683           1 :         poLS->setPoint(0, &oCorners[0]);
    2684           1 :         poLS->setPoint(1, &oCorners[1]);
    2685           1 :         poFinalGeom.reset(poLS.release());
    2686             : 
    2687           1 :         PrepareLineStyle(poFeature.get());
    2688             :     }
    2689             :     else
    2690             :     {
    2691             :         // SOLID vertices seem to be joined in the order 1-2-4-3-1.
    2692             :         // See trac ticket #7089
    2693          23 :         OGRLinearRing *poLinearRing = new OGRLinearRing();
    2694          23 :         int iIndex = 0;
    2695          23 :         poLinearRing->setPoint(iIndex++, dfX1, dfY1, dfZ1);
    2696          23 :         if (dfX1 != dfX2 || dfY1 != dfY2 || dfZ1 != dfZ2)
    2697          23 :             poLinearRing->setPoint(iIndex++, dfX2, dfY2, dfZ2);
    2698          23 :         if (dfX2 != dfX4 || dfY2 != dfY4 || dfZ2 != dfZ4)
    2699          23 :             poLinearRing->setPoint(iIndex++, dfX4, dfY4, dfZ4);
    2700          23 :         if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
    2701          17 :             poLinearRing->setPoint(iIndex++, dfX3, dfY3, dfZ3);
    2702          23 :         poLinearRing->closeRings();
    2703             : 
    2704          23 :         if (!bWantZ)
    2705          20 :             poLinearRing->flattenTo2D();
    2706             : 
    2707          46 :         auto poPoly = std::make_unique<OGRPolygon>();
    2708          23 :         poPoly->addRingDirectly(poLinearRing);
    2709          23 :         poFinalGeom.reset(poPoly.release());
    2710             : 
    2711          23 :         PrepareBrushStyle(poFeature.get());
    2712             :     }
    2713             : 
    2714          25 :     poFeature->ApplyOCSTransformer(poFinalGeom.get());
    2715          25 :     poFeature->SetGeometryDirectly(poFinalGeom.release());
    2716             : 
    2717          25 :     return poFeature.release();
    2718             : }
    2719             : 
    2720             : /************************************************************************/
    2721             : /*                         TranslateASMEntity()                         */
    2722             : /*                                                                      */
    2723             : /*     Translate Autodesk ShapeManager entities (3DSOLID, REGION,       */
    2724             : /*     SURFACE), also known as ACIS entities.                           */
    2725             : /************************************************************************/
    2726             : 
    2727           2 : OGRDXFFeature *OGRDXFLayer::TranslateASMEntity()
    2728             : 
    2729             : {
    2730             :     char szLineBuf[257];
    2731           2 :     int nCode = 0;
    2732           4 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2733             : 
    2734          23 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2735             :     {
    2736          21 :         TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2737             :     }
    2738             : 
    2739           2 :     if (nCode < 0)
    2740             :     {
    2741           0 :         DXF_LAYER_READER_ERROR();
    2742           0 :         return nullptr;
    2743             :     }
    2744             : 
    2745           2 :     poDS->UnreadValue();
    2746             : 
    2747           2 :     const char *pszEntityHandle = poFeature->GetFieldAsString("EntityHandle");
    2748             : 
    2749             :     // The actual data is located at the end of the DXF file (sigh).
    2750             :     const GByte *pabyBinaryData;
    2751             :     size_t nDataLength =
    2752           2 :         poDS->GetEntryFromAcDsDataSection(pszEntityHandle, &pabyBinaryData);
    2753           2 :     if (!pabyBinaryData)
    2754             :     {
    2755           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2756             :                  "ACDSRECORD data for entity %s was not found.",
    2757             :                  pszEntityHandle);
    2758           0 :         return poFeature.release();
    2759             :     }
    2760             : 
    2761             :     // Return a feature with no geometry but with one very interesting field.
    2762           2 :     poFeature->SetField(poFeatureDefn->GetFieldIndex("ASMData"),
    2763             :                         static_cast<int>(nDataLength), pabyBinaryData);
    2764             : 
    2765             :     // Set up an affine transformation matrix so the user will be able to
    2766             :     // transform the resulting 3D geometry
    2767           2 :     poFeature->poASMTransform = std::make_unique<OGRDXFAffineTransform>();
    2768             : 
    2769           2 :     poFeature->poASMTransform->SetField(poFeature.get(), "ASMTransform");
    2770             : 
    2771             : #ifdef notdef
    2772             :     FILE *fp;
    2773             :     fopen_s(&fp,
    2774             :             CPLString().Printf("C:\\Projects\\output.sab", pszEntityHandle),
    2775             :             "wb");
    2776             : 
    2777             :     if (fp != nullptr)
    2778             :     {
    2779             :         fprintf(fp, "Entity handle:  %s\r\n\r\n", pszEntityHandle);
    2780             :         fwrite(pabyBinaryData, sizeof(GByte), nDataLength, fp);
    2781             :         if (ferror(fp) != 0)
    2782             :         {
    2783             :             fputs("Error writing .sab file", stderr);
    2784             :         }
    2785             :         fclose(fp);
    2786             :     }
    2787             : #endif
    2788             : 
    2789           2 :     PrepareBrushStyle(poFeature.get());
    2790             : 
    2791           2 :     return poFeature.release();
    2792             : }
    2793             : 
    2794             : /************************************************************************/
    2795             : /*                       SimplifyBlockGeometry()                        */
    2796             : /************************************************************************/
    2797             : 
    2798             : OGRGeometry *
    2799          81 : OGRDXFLayer::SimplifyBlockGeometry(OGRGeometryCollection *poCollection)
    2800             : {
    2801             :     /* -------------------------------------------------------------------- */
    2802             :     /*      If there is only one geometry in the collection, just return    */
    2803             :     /*      it.                                                             */
    2804             :     /* -------------------------------------------------------------------- */
    2805          81 :     if (poCollection->getNumGeometries() == 1)
    2806             :     {
    2807          68 :         OGRGeometry *poReturn = poCollection->getGeometryRef(0);
    2808          68 :         poCollection->removeGeometry(0, FALSE);
    2809          68 :         delete poCollection;
    2810          68 :         return poReturn;
    2811             :     }
    2812             : 
    2813             :     /* -------------------------------------------------------------------- */
    2814             :     /*      Convert to polygon, multipolygon, multilinestring or multipoint */
    2815             :     /* -------------------------------------------------------------------- */
    2816             : 
    2817             :     OGRwkbGeometryType eType =
    2818          13 :         wkbFlatten(poCollection->getGeometryRef(0)->getGeometryType());
    2819             :     int i;
    2820          47 :     for (i = 1; i < poCollection->getNumGeometries(); i++)
    2821             :     {
    2822          35 :         if (wkbFlatten(poCollection->getGeometryRef(i)->getGeometryType()) !=
    2823             :             eType)
    2824             :         {
    2825           1 :             eType = wkbUnknown;
    2826           1 :             break;
    2827             :         }
    2828             :     }
    2829          13 :     if (eType == wkbPoint || eType == wkbLineString)
    2830             :     {
    2831             :         OGRGeometryCollection *poNewColl;
    2832          11 :         if (eType == wkbPoint)
    2833           0 :             poNewColl = new OGRMultiPoint();
    2834             :         else
    2835          11 :             poNewColl = new OGRMultiLineString();
    2836          55 :         while (poCollection->getNumGeometries() > 0)
    2837             :         {
    2838          44 :             OGRGeometry *poGeom = poCollection->getGeometryRef(0);
    2839          44 :             poCollection->removeGeometry(0, FALSE);
    2840          44 :             poNewColl->addGeometryDirectly(poGeom);
    2841             :         }
    2842          11 :         delete poCollection;
    2843          11 :         return poNewColl;
    2844             :     }
    2845           2 :     else if (eType == wkbPolygon)
    2846             :     {
    2847           2 :         std::vector<OGRGeometry *> aosPolygons;
    2848           3 :         while (poCollection->getNumGeometries() > 0)
    2849             :         {
    2850           2 :             OGRGeometry *poGeom = poCollection->getGeometryRef(0);
    2851           2 :             poCollection->removeGeometry(0, FALSE);
    2852           2 :             if (!aosPolygons.empty() && aosPolygons[0]->Equals(poGeom))
    2853             :             {
    2854             :                 // Avoids a performance issue as in
    2855             :                 // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=8067
    2856           0 :                 delete poGeom;
    2857             :             }
    2858             :             else
    2859             :             {
    2860           2 :                 aosPolygons.push_back(poGeom);
    2861             :             }
    2862             :         }
    2863           1 :         delete poCollection;
    2864             :         int bIsValidGeometry;
    2865           1 :         return OGRGeometryFactory::organizePolygons(&aosPolygons[0],
    2866           1 :                                                     (int)aosPolygons.size(),
    2867           1 :                                                     &bIsValidGeometry, nullptr);
    2868             :     }
    2869             : 
    2870           1 :     return poCollection;
    2871             : }
    2872             : 
    2873             : /************************************************************************/
    2874             : /*                        TranslateWIPEOUT()                            */
    2875             : /*                                                                      */
    2876             : /*     Translate Autodesk Wipeout entities                              */
    2877             : /*     This function reads only the geometry of the image outline and   */
    2878             : /*     doesn't output the embedded image                                */
    2879             : /************************************************************************/
    2880             : 
    2881           4 : OGRDXFFeature *OGRDXFLayer::TranslateWIPEOUT()
    2882             : 
    2883             : {
    2884             :     char szLineBuf[257];
    2885             :     int nCode;
    2886           8 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    2887           4 :     double dfX = 0.0, dfY = 0.0, dfXOffset = 0.0, dfYOffset = 0.0;
    2888           4 :     double dfXscale = 1.0, dfYscale = 1.0;
    2889             : 
    2890           4 :     int nNumVertices = 0;
    2891           4 :     int nBoundaryVertexCount = 0;
    2892           4 :     int nFormat = 0;
    2893             : 
    2894           8 :     DXFSmoothPolyline smoothPolyline;
    2895             : 
    2896           4 :     smoothPolyline.setCoordinateDimension(2);
    2897             : 
    2898             :     /* Read main feature properties as class, insertion point (in WCS) */
    2899         182 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    2900             :     {
    2901         178 :         if (nBoundaryVertexCount > nNumVertices)
    2902             :         {
    2903           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2904             :                      "Too many vertices found in WIPEOUT.");
    2905           0 :             return nullptr;
    2906             :         }
    2907             : 
    2908         178 :         switch (nCode)
    2909             :         {
    2910             :                 /* Group codes 10, 20 control the insertion point of the lower
    2911             :                    left corner of your image. */
    2912           4 :             case 10:
    2913           4 :                 dfXOffset = CPLAtof(szLineBuf);
    2914           4 :                 break;
    2915             : 
    2916           4 :             case 20:
    2917           4 :                 dfYOffset = CPLAtof(szLineBuf);
    2918           4 :                 smoothPolyline.AddPoint(dfXOffset, dfYOffset, 0.0, 0.0);
    2919           4 :                 break;
    2920             : 
    2921             :                 /* --------------------------------------------------------------------- */
    2922             :                 /* The group codes 11, 21 and 31 are used to define a vector in 3D space */
    2923             :                 /* that is the endpoint of a line whose start point is assumed to be     */
    2924             :                 /* 0,0,0, regardless of the origin point of the image.                   */
    2925             :                 /* These group codes describe a relative vector.                         */
    2926             :                 /* --------------------------------------------------------------------- */
    2927             : 
    2928           4 :             case 11:
    2929           4 :                 dfXscale = CPLAtof(szLineBuf);
    2930           4 :                 break;
    2931             : 
    2932           4 :             case 22:
    2933           4 :                 dfYscale = CPLAtof(szLineBuf);
    2934           4 :                 break;
    2935             : 
    2936           4 :             case 31:
    2937           4 :                 break;
    2938             : 
    2939             :             /* Read image properties and set them in feature style (contrast...) */
    2940           4 :             case 281:
    2941           4 :                 break;
    2942             : 
    2943           4 :             case 282:
    2944           4 :                 break;
    2945             : 
    2946           0 :             case 293:
    2947           0 :                 break;
    2948             : 
    2949           4 :             case 71:
    2950           4 :                 nFormat = atoi(szLineBuf);
    2951           4 :                 if (nFormat == 1)
    2952             :                 {
    2953             :                     // Here ignore feature because point format set to 1 is not supported
    2954           0 :                     CPLError(
    2955             :                         CE_Warning, CPLE_AppDefined,
    2956             :                         "Format of points in WIPEOUT entity not supported.");
    2957           0 :                     return nullptr;
    2958             :                 }
    2959           4 :                 break;
    2960             : 
    2961           4 :             case 91:
    2962           4 :                 nNumVertices = atoi(szLineBuf);
    2963           4 :                 break;
    2964             : 
    2965             :             /* -------------------------------------------------------------------- */
    2966             :             /*      Read clipping boundary properties and set them feature geometry */
    2967             :             /*      Collect vertices as a smooth polyline.                          */
    2968             :             /* -------------------------------------------------------------------- */
    2969          20 :             case 14:
    2970          20 :                 dfX = CPLAtof(szLineBuf);
    2971          20 :                 break;
    2972             : 
    2973          20 :             case 24:
    2974          20 :                 dfY = CPLAtof(szLineBuf);
    2975          20 :                 smoothPolyline.AddPoint(dfXOffset + (0.5 + dfX) * dfXscale,
    2976          20 :                                         dfYOffset + (0.5 - dfY) * dfYscale, 0.0,
    2977             :                                         0.0);
    2978          20 :                 nBoundaryVertexCount++;
    2979          20 :                 break;
    2980             : 
    2981         102 :             default:
    2982         102 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    2983         102 :                 break;
    2984             :         }
    2985             :     }
    2986           4 :     if (nCode < 0)
    2987             :     {
    2988           0 :         DXF_LAYER_READER_ERROR();
    2989           0 :         return nullptr;
    2990             :     }
    2991             : 
    2992           4 :     if (nCode == 0)
    2993           4 :         poDS->UnreadValue();
    2994             : 
    2995           4 :     if (smoothPolyline.IsEmpty())
    2996             :     {
    2997           0 :         return nullptr;
    2998             :     }
    2999             : 
    3000             :     /* -------------------------------------------------------------------- */
    3001             :     /*      Close polyline to output polygon geometry.                      */
    3002             :     /* -------------------------------------------------------------------- */
    3003           4 :     smoothPolyline.Close();
    3004             : 
    3005           4 :     OGRGeometry *poGeom = smoothPolyline.Tessellate(TRUE);
    3006             : 
    3007           4 :     poFeature->SetGeometryDirectly(poGeom);
    3008             : 
    3009             :     // Set style pen color
    3010           4 :     PrepareLineStyle(poFeature.get());
    3011             : 
    3012           4 :     return poFeature.release();
    3013             : }
    3014             : 
    3015             : /************************************************************************/
    3016             : 
    3017             : /************************************************************************/
    3018             : /*                       InsertBlockReference()                         */
    3019             : /*                                                                      */
    3020             : /*     Returns a point geometry located at the block's insertion        */
    3021             : /*     point.                                                           */
    3022             : /************************************************************************/
    3023             : OGRDXFFeature *
    3024         814 : OGRDXFLayer::InsertBlockReference(const CPLString &osBlockName,
    3025             :                                   const OGRDXFInsertTransformer &oTransformer,
    3026             :                                   OGRDXFFeature *const poFeature)
    3027             : {
    3028             :     // Store the block's properties in the special DXF-specific members
    3029             :     // on the feature object
    3030         814 :     poFeature->bIsBlockReference = true;
    3031         814 :     poFeature->osBlockName = osBlockName;
    3032         814 :     poFeature->dfBlockAngle = oTransformer.dfAngle * 180 / M_PI;
    3033        1628 :     poFeature->oBlockScale = DXFTriple(
    3034         814 :         oTransformer.dfXScale, oTransformer.dfYScale, oTransformer.dfZScale);
    3035        1628 :     poFeature->oOriginalCoords = DXFTriple(
    3036         814 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    3037             : 
    3038             :     // Only if DXF_INLINE_BLOCKS is false should we ever need to expose these
    3039             :     // to the end user as fields.
    3040         814 :     if (poFeature->GetFieldIndex("BlockName") != -1)
    3041             :     {
    3042          13 :         poFeature->SetField("BlockName", poFeature->osBlockName);
    3043          13 :         poFeature->SetField("BlockAngle", poFeature->dfBlockAngle);
    3044          13 :         poFeature->SetField("BlockScale", 3, &(poFeature->oBlockScale.dfX));
    3045          13 :         poFeature->SetField("BlockOCSNormal", 3, &(poFeature->oOCS.dfX));
    3046          13 :         poFeature->SetField("BlockOCSCoords", 3,
    3047          13 :                             &(poFeature->oOriginalCoords.dfX));
    3048             :     }
    3049             : 
    3050             :     // For convenience to the end user, the point geometry will be located
    3051             :     // at the WCS coordinates of the insertion point.
    3052             :     OGRPoint *poInsertionPoint = new OGRPoint(
    3053         814 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    3054             : 
    3055         814 :     poFeature->ApplyOCSTransformer(poInsertionPoint);
    3056         814 :     poFeature->SetGeometryDirectly(poInsertionPoint);
    3057             : 
    3058         814 :     return poFeature;
    3059             : }
    3060             : 
    3061             : /************************************************************************/
    3062             : /*                         InsertBlockInline()                          */
    3063             : /*                                                                      */
    3064             : /*     Inserts the given block at the location specified by the given   */
    3065             : /*     transformer.  Returns poFeature, or NULL if all features on      */
    3066             : /*     the block have been pushed to the extra feature queue.           */
    3067             : /*     If poFeature is not returned, it is deleted.                     */
    3068             : /*     Throws std::invalid_argument if the requested block              */
    3069             : /*     doesn't exist.                                                   */
    3070             : /*                                                                      */
    3071             : /*     - poFeature: The feature to use as a template. This feature's    */
    3072             : /*       OCS will be applied to the block.                              */
    3073             : /*     - bInlineRecursively: If true, INSERTs within this block         */
    3074             : /*       will be recursively inserted.  Otherwise, they will be         */
    3075             : /*       represented as a point geometry using InsertBlockReference.    */
    3076             : /*     - bMergeGeometry: If true, all features in the block,            */
    3077             : /*       apart from text features, are merged into a                    */
    3078             : /*       GeometryCollection which is returned by the function.          */
    3079             : /************************************************************************/
    3080             : 
    3081        1340 : OGRDXFFeature *OGRDXFLayer::InsertBlockInline(
    3082             :     GUInt32 nInitialErrorCounter, const CPLString &osBlockName,
    3083             :     OGRDXFInsertTransformer oTransformer, OGRDXFFeature *const poFeature,
    3084             :     OGRDXFFeatureQueue &apoExtraFeatures, const bool bInlineRecursively,
    3085             :     const bool bMergeGeometry)
    3086             : {
    3087             :     /* -------------------------------------------------------------------- */
    3088             :     /*      Set up protection against excessive recursion on this layer.    */
    3089             :     /* -------------------------------------------------------------------- */
    3090        1340 :     if (!poDS->PushBlockInsertion(osBlockName))
    3091             :     {
    3092        1003 :         delete poFeature;
    3093        1003 :         return nullptr;
    3094             :     }
    3095             : 
    3096             :     /* -------------------------------------------------------------------- */
    3097             :     /*      Transform the insertion point from OCS into                     */
    3098             :     /*      world coordinates.                                              */
    3099             :     /* -------------------------------------------------------------------- */
    3100             :     OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
    3101         674 :                              oTransformer.dfZOffset);
    3102             : 
    3103         337 :     poFeature->ApplyOCSTransformer(&oInsertionPoint);
    3104             : 
    3105         337 :     oTransformer.dfXOffset = oInsertionPoint.getX();
    3106         337 :     oTransformer.dfYOffset = oInsertionPoint.getY();
    3107         337 :     oTransformer.dfZOffset = oInsertionPoint.getZ();
    3108             : 
    3109             :     /* -------------------------------------------------------------------- */
    3110             :     /*      Lookup the block.                                               */
    3111             :     /* -------------------------------------------------------------------- */
    3112         337 :     DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
    3113             : 
    3114         337 :     if (poBlock == nullptr)
    3115             :     {
    3116             :         // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
    3117           9 :         poDS->PopBlockInsertion();
    3118           9 :         throw std::invalid_argument("osBlockName");
    3119             :     }
    3120             : 
    3121             :     /* -------------------------------------------------------------------- */
    3122             :     /*      If we have complete features associated with the block, push    */
    3123             :     /*      them on the pending feature stack copying over key override     */
    3124             :     /*      information.                                                    */
    3125             :     /*                                                                      */
    3126             :     /*      If bMergeGeometry is true, we merge the features                */
    3127             :     /*      (except text) into a single GeometryCollection.                 */
    3128             :     /* -------------------------------------------------------------------- */
    3129         328 :     OGRGeometryCollection *poMergedGeometry = nullptr;
    3130         328 :     if (bMergeGeometry)
    3131          95 :         poMergedGeometry = new OGRGeometryCollection();
    3132             : 
    3133         656 :     OGRDXFFeatureQueue apoInnerExtraFeatures;
    3134             : 
    3135        2960 :     for (unsigned int iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size();
    3136             :          iSubFeat++)
    3137             :     {
    3138             :         OGRDXFFeature *poSubFeature =
    3139        2634 :             poBlock->apoFeatures[iSubFeat]->CloneDXFFeature();
    3140             : 
    3141             :         // If the template feature is in PaperSpace, set this on the
    3142             :         // subfeature too
    3143        2634 :         if (poFeature->GetFieldAsInteger("PaperSpace"))
    3144          48 :             poSubFeature->SetField("PaperSpace", 1);
    3145             : 
    3146             :         // Does this feature represent a block reference? If so,
    3147             :         // insert that block
    3148        2634 :         if (bInlineRecursively && poSubFeature->IsBlockReference())
    3149             :         {
    3150             :             // Unpack the transformation data stored in fields of this
    3151             :             // feature
    3152           0 :             OGRDXFInsertTransformer oInnerTransformer;
    3153        1170 :             oInnerTransformer.dfXOffset = poSubFeature->oOriginalCoords.dfX;
    3154        1170 :             oInnerTransformer.dfYOffset = poSubFeature->oOriginalCoords.dfY;
    3155        1170 :             oInnerTransformer.dfZOffset = poSubFeature->oOriginalCoords.dfZ;
    3156        1170 :             oInnerTransformer.dfAngle = poSubFeature->dfBlockAngle * M_PI / 180;
    3157        1170 :             oInnerTransformer.dfXScale = poSubFeature->oBlockScale.dfX;
    3158        1170 :             oInnerTransformer.dfYScale = poSubFeature->oBlockScale.dfY;
    3159        1170 :             oInnerTransformer.dfZScale = poSubFeature->oBlockScale.dfZ;
    3160             : 
    3161        1170 :             poSubFeature->bIsBlockReference = false;
    3162             : 
    3163             :             // Keep a reference to the attributes that need to be inserted
    3164             :             std::vector<std::unique_ptr<OGRDXFFeature>> apoInnerAttribFeatures =
    3165        1170 :                 std::move(poSubFeature->apoAttribFeatures);
    3166             : 
    3167             :             // Insert this block recursively
    3168             :             try
    3169             :             {
    3170        2340 :                 poSubFeature = InsertBlockInline(
    3171        1170 :                     nInitialErrorCounter, poSubFeature->osBlockName,
    3172        1170 :                     std::move(oInnerTransformer), poSubFeature,
    3173             :                     apoInnerExtraFeatures, true, bMergeGeometry);
    3174             :             }
    3175           0 :             catch (const std::invalid_argument &)
    3176             :             {
    3177             :                 // Block doesn't exist. Skip it and keep going
    3178           0 :                 delete poSubFeature;
    3179           0 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    3180             :                 {
    3181           0 :                     break;
    3182             :                 }
    3183           0 :                 continue;
    3184             :             }
    3185             : 
    3186        1170 :             if (!poSubFeature)
    3187             :             {
    3188        1164 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    3189             :                 {
    3190           2 :                     break;
    3191             :                 }
    3192             : 
    3193             :                 // Append the attribute features to the pending feature stack
    3194        1164 :                 for (auto &poAttribFeature : apoInnerAttribFeatures)
    3195             :                 {
    3196             :                     // Clear the attribute tag so the feature doesn't get mistaken
    3197             :                     // for an ATTDEF and skipped
    3198           2 :                     poAttribFeature->osAttributeTag = "";
    3199             : 
    3200           2 :                     apoInnerExtraFeatures.push(poAttribFeature.release());
    3201             :                 }
    3202             : 
    3203        1162 :                 if (apoInnerExtraFeatures.empty())
    3204             :                 {
    3205             :                     // Block is empty and has no attributes. Skip it and keep going
    3206        1002 :                     continue;
    3207             :                 }
    3208             :                 else
    3209             :                 {
    3210             :                     // Load up the first extra feature ready for
    3211             :                     // transformation
    3212         160 :                     poSubFeature = apoInnerExtraFeatures.front();
    3213         160 :                     apoInnerExtraFeatures.pop();
    3214             :                 }
    3215             :             }
    3216             :         }
    3217             : 
    3218             :         // Go through the current feature and any extra features generated
    3219             :         // by the recursive insert, and apply transformations
    3220             :         while (true)
    3221             :         {
    3222        2429 :             OGRGeometry *poSubFeatGeom = poSubFeature->GetGeometryRef();
    3223        2429 :             if (poSubFeatGeom != nullptr)
    3224             :             {
    3225             :                 // Rotation and scaling first
    3226             :                 OGRDXFInsertTransformer oInnerTrans =
    3227        4852 :                     oTransformer.GetRotateScaleTransformer();
    3228        2426 :                 poSubFeatGeom->transform(&oInnerTrans);
    3229             : 
    3230             :                 // Then the OCS to WCS transformation
    3231        2426 :                 poFeature->ApplyOCSTransformer(poSubFeatGeom);
    3232             : 
    3233             :                 // Offset translation last
    3234        2426 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    3235        2426 :                 poSubFeatGeom->transform(&oInnerTrans);
    3236             :             }
    3237             :             // Transform the specially-stored data for ASM entities
    3238           3 :             else if (poSubFeature->poASMTransform)
    3239             :             {
    3240             :                 // Rotation and scaling first
    3241             :                 OGRDXFInsertTransformer oInnerTrans =
    3242           6 :                     oTransformer.GetRotateScaleTransformer();
    3243           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    3244             : 
    3245             :                 // Then the OCS to WCS transformation
    3246           3 :                 poFeature->ApplyOCSTransformer(
    3247             :                     poSubFeature->poASMTransform.get());
    3248             : 
    3249             :                 // Offset translation last
    3250           3 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    3251           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    3252             : 
    3253           3 :                 poSubFeature->poASMTransform->SetField(poSubFeature,
    3254             :                                                        "ASMTransform");
    3255             :             }
    3256             : 
    3257             :             // If we are merging features, and this is not text or a block
    3258             :             // reference, merge it into the GeometryCollection
    3259        2623 :             if (bMergeGeometry &&
    3260         194 :                 (poSubFeature->GetStyleString() == nullptr ||
    3261         177 :                  strstr(poSubFeature->GetStyleString(), "LABEL") == nullptr) &&
    3262        2742 :                 !poSubFeature->IsBlockReference() &&
    3263         119 :                 poSubFeature->GetGeometryRef())
    3264             :             {
    3265         116 :                 poMergedGeometry->addGeometryDirectly(
    3266         116 :                     poSubFeature->StealGeometry());
    3267         116 :                 delete poSubFeature;
    3268             :             }
    3269             :             // Import all other features, except ATTDEFs when inlining
    3270             :             // recursively
    3271        2313 :             else if (!bInlineRecursively || poSubFeature->osAttributeTag == "")
    3272             :             {
    3273             :                 // If the subfeature is on layer 0, this is a special case: the
    3274             :                 // subfeature should take on the style properties of the layer
    3275             :                 // the block is being inserted onto.
    3276             :                 // But don't do this if we are inserting onto a Blocks layer
    3277             :                 // (that is, the owning feature has no layer).
    3278        2863 :                 if (EQUAL(poSubFeature->GetFieldAsString("Layer"), "0") &&
    3279         582 :                     !EQUAL(poFeature->GetFieldAsString("Layer"), ""))
    3280             :                 {
    3281         551 :                     poSubFeature->SetField(
    3282             :                         "Layer", poFeature->GetFieldAsString("Layer"));
    3283             :                 }
    3284             : 
    3285             :                 // Update the style string to replace ByBlock and ByLayer
    3286             :                 // values.
    3287        2281 :                 PrepareFeatureStyle(poSubFeature, poFeature);
    3288             : 
    3289        2281 :                 ACAdjustText(oTransformer.dfAngle * 180 / M_PI,
    3290             :                              oTransformer.dfXScale, oTransformer.dfYScale,
    3291             :                              poSubFeature);
    3292             : 
    3293        2281 :                 if (!EQUAL(poFeature->GetFieldAsString("EntityHandle"), ""))
    3294             :                 {
    3295        2244 :                     poSubFeature->SetField(
    3296             :                         "EntityHandle",
    3297             :                         poFeature->GetFieldAsString("EntityHandle"));
    3298             :                 }
    3299             : 
    3300        2281 :                 apoExtraFeatures.push(poSubFeature);
    3301             :             }
    3302             :             else
    3303             :             {
    3304          32 :                 delete poSubFeature;
    3305             :             }
    3306             : 
    3307        2429 :             if (apoInnerExtraFeatures.empty())
    3308             :             {
    3309        1630 :                 break;
    3310             :             }
    3311             :             else
    3312             :             {
    3313         799 :                 poSubFeature = apoInnerExtraFeatures.front();
    3314         799 :                 apoInnerExtraFeatures.pop();
    3315             :             }
    3316         799 :         }
    3317             :     }
    3318             : 
    3319         330 :     while (!apoInnerExtraFeatures.empty())
    3320             :     {
    3321           2 :         auto poFeatureToDelete = apoInnerExtraFeatures.front();
    3322           2 :         apoInnerExtraFeatures.pop();
    3323           2 :         delete poFeatureToDelete;
    3324             :     }
    3325             : 
    3326         328 :     poDS->PopBlockInsertion();
    3327             : 
    3328             :     /* -------------------------------------------------------------------- */
    3329             :     /*      Return the merged geometry if applicable.  Otherwise            */
    3330             :     /*      return NULL and let the machinery find the rest of the          */
    3331             :     /*      features in the pending feature stack.                          */
    3332             :     /* -------------------------------------------------------------------- */
    3333         328 :     if (bMergeGeometry)
    3334             :     {
    3335          95 :         if (poMergedGeometry->getNumGeometries() == 0)
    3336             :         {
    3337          14 :             delete poMergedGeometry;
    3338             :         }
    3339             :         else
    3340             :         {
    3341          81 :             poFeature->SetGeometryDirectly(
    3342             :                 SimplifyBlockGeometry(poMergedGeometry));
    3343             : 
    3344          81 :             PrepareLineStyle(poFeature);
    3345          81 :             return poFeature;
    3346             :         }
    3347             :     }
    3348             : 
    3349         247 :     delete poFeature;
    3350         247 :     return nullptr;
    3351             : }
    3352             : 
    3353             : /************************************************************************/
    3354             : /*                          TranslateINSERT()                           */
    3355             : /************************************************************************/
    3356             : 
    3357         161 : bool OGRDXFLayer::TranslateINSERT()
    3358             : 
    3359             : {
    3360             :     char szLineBuf[257];
    3361         161 :     int nCode = 0;
    3362             : 
    3363         161 :     m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
    3364         161 :     m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
    3365         161 :     m_oInsertState.m_osBlockName.clear();
    3366         161 :     m_oInsertState.m_nColumnCount = 1;
    3367         161 :     m_oInsertState.m_nRowCount = 1;
    3368         161 :     m_oInsertState.m_iCurCol = 0;
    3369         161 :     m_oInsertState.m_iCurRow = 0;
    3370         161 :     m_oInsertState.m_dfColumnSpacing = 0.0;
    3371         161 :     m_oInsertState.m_dfRowSpacing = 0.0;
    3372             : 
    3373         161 :     bool bHasAttribs = false;
    3374         161 :     m_oInsertState.m_apoAttribs.clear();
    3375         161 :     m_oInsertState.m_aosAttribs.Clear();
    3376             : 
    3377             :     /* -------------------------------------------------------------------- */
    3378             :     /*      Process values.                                                 */
    3379             :     /* -------------------------------------------------------------------- */
    3380        1657 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3381             :     {
    3382        1496 :         switch (nCode)
    3383             :         {
    3384         159 :             case 10:
    3385         159 :                 m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
    3386         159 :                 break;
    3387             : 
    3388         159 :             case 20:
    3389         159 :                 m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
    3390         159 :                 break;
    3391             : 
    3392         152 :             case 30:
    3393         152 :                 m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
    3394         152 :                 break;
    3395             : 
    3396          30 :             case 41:
    3397          30 :                 m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
    3398          30 :                 break;
    3399             : 
    3400          33 :             case 42:
    3401          33 :                 m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
    3402          33 :                 break;
    3403             : 
    3404          18 :             case 43:
    3405          18 :                 m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
    3406          18 :                 break;
    3407             : 
    3408           6 :             case 44:
    3409           6 :                 m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
    3410           6 :                 break;
    3411             : 
    3412           6 :             case 45:
    3413           6 :                 m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
    3414           6 :                 break;
    3415             : 
    3416          25 :             case 50:
    3417             :                 // We want to transform this to radians.
    3418             :                 // It is apparently always in degrees regardless of $AUNITS
    3419          25 :                 m_oInsertState.m_oTransformer.dfAngle =
    3420          25 :                     CPLAtof(szLineBuf) * M_PI / 180.0;
    3421          25 :                 break;
    3422             : 
    3423          15 :             case 66:
    3424          15 :                 bHasAttribs = atoi(szLineBuf) == 1;
    3425          15 :                 break;
    3426             : 
    3427           2 :             case 70:
    3428           2 :                 m_oInsertState.m_nColumnCount = atoi(szLineBuf);
    3429           2 :                 if (m_oInsertState.m_nColumnCount < 0)
    3430             :                 {
    3431           0 :                     DXF_LAYER_READER_ERROR();
    3432           0 :                     m_oInsertState.m_nRowCount = 0;
    3433           0 :                     m_oInsertState.m_nColumnCount = 0;
    3434           0 :                     return false;
    3435             :                 }
    3436           2 :                 break;
    3437             : 
    3438           3 :             case 71:
    3439           3 :                 m_oInsertState.m_nRowCount = atoi(szLineBuf);
    3440           3 :                 if (m_oInsertState.m_nRowCount < 0)
    3441             :                 {
    3442           0 :                     DXF_LAYER_READER_ERROR();
    3443           0 :                     m_oInsertState.m_nRowCount = 0;
    3444           0 :                     m_oInsertState.m_nColumnCount = 0;
    3445           0 :                     return false;
    3446             :                 }
    3447           3 :                 break;
    3448             : 
    3449         159 :             case 2:
    3450         159 :                 m_oInsertState.m_osBlockName = szLineBuf;
    3451         159 :                 break;
    3452             : 
    3453         729 :             default:
    3454         729 :                 TranslateGenericProperty(
    3455             :                     m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
    3456         729 :                 break;
    3457             :         }
    3458             :     }
    3459         161 :     if (nCode < 0)
    3460             :     {
    3461           0 :         DXF_LAYER_READER_ERROR();
    3462           0 :         m_oInsertState.m_nRowCount = 0;
    3463           0 :         m_oInsertState.m_nColumnCount = 0;
    3464           0 :         return false;
    3465             :     }
    3466             : 
    3467         161 :     if (m_oInsertState.m_nRowCount == 0 || m_oInsertState.m_nColumnCount == 0)
    3468             :     {
    3469             :         // AutoCad doesn't allow setting to 0 in its UI, but interprets 0
    3470             :         // as 1 (but other software such as LibreCAD interpret 0 as 0)
    3471           1 :         m_oInsertState.m_nRowCount = 1;
    3472           1 :         m_oInsertState.m_nColumnCount = 1;
    3473             :     }
    3474             : 
    3475             :     /* -------------------------------------------------------------------- */
    3476             :     /*      Process any attribute entities.                                 */
    3477             :     /* -------------------------------------------------------------------- */
    3478             : 
    3479         161 :     if (bHasAttribs)
    3480             :     {
    3481          42 :         while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
    3482             :         {
    3483          27 :             if (!EQUAL(szLineBuf, "ATTRIB"))
    3484             :             {
    3485           0 :                 DXF_LAYER_READER_ERROR();
    3486           0 :                 m_oInsertState.m_nRowCount = 0;
    3487           0 :                 m_oInsertState.m_nColumnCount = 0;
    3488           0 :                 return false;
    3489             :             }
    3490             : 
    3491             :             auto poAttribFeature =
    3492          27 :                 std::unique_ptr<OGRDXFFeature>(TranslateTEXT(true));
    3493             : 
    3494          27 :             if (poAttribFeature && poAttribFeature->osAttributeTag != "")
    3495             :             {
    3496             :                 m_oInsertState.m_apoAttribs.emplace_back(
    3497          27 :                     std::move(poAttribFeature));
    3498             :             }
    3499             : 
    3500          27 :             nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
    3501             :         }
    3502             :     }
    3503         146 :     else if (nCode == 0)
    3504             :     {
    3505         146 :         poDS->UnreadValue();
    3506             :     }
    3507             : 
    3508             :     /* -------------------------------------------------------------------- */
    3509             :     /*      Prepare a string list of the attributes and their text values   */
    3510             :     /*      as space-separated entries, to be stored in the                 */
    3511             :     /*      BlockAttributes field if we are not inlining blocks.            */
    3512             :     /* -------------------------------------------------------------------- */
    3513             : 
    3514         165 :     if (!poDS->InlineBlocks() && bHasAttribs &&
    3515           4 :         poFeatureDefn->GetFieldIndex("BlockAttributes") != -1)
    3516             :     {
    3517          10 :         for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3518             :         {
    3519          14 :             CPLString osAttribString = poAttr->osAttributeTag;
    3520           7 :             osAttribString += " ";
    3521           7 :             osAttribString += poAttr->GetFieldAsString("Text");
    3522             : 
    3523           7 :             m_oInsertState.m_aosAttribs.AddString(osAttribString);
    3524             :         }
    3525             :     }
    3526             : 
    3527         161 :     return true;
    3528             : }
    3529             : 
    3530             : /************************************************************************/
    3531             : /*                       GenerateINSERTFeatures()                       */
    3532             : /************************************************************************/
    3533             : 
    3534         928 : bool OGRDXFLayer::GenerateINSERTFeatures()
    3535             : {
    3536             :     OGRDXFFeature *poFeature =
    3537         928 :         m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
    3538             : 
    3539         928 :     const double dfExtraXOffset =
    3540         928 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3541         928 :             cos(m_oInsertState.m_oTransformer.dfAngle) +
    3542         928 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3543         928 :             -sin(m_oInsertState.m_oTransformer.dfAngle);
    3544         928 :     const double dfExtraYOffset =
    3545         928 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3546         928 :             sin(m_oInsertState.m_oTransformer.dfAngle) +
    3547         928 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3548         928 :             cos(m_oInsertState.m_oTransformer.dfAngle);
    3549             : 
    3550        1856 :     OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
    3551         928 :     oTransformer.dfXOffset += dfExtraXOffset;
    3552         928 :     oTransformer.dfYOffset += dfExtraYOffset;
    3553             : 
    3554             :     // If we are not inlining blocks, just insert a point that refers
    3555             :     // to this block
    3556         928 :     if (!poDS->InlineBlocks())
    3557             :     {
    3558         814 :         poFeature = InsertBlockReference(m_oInsertState.m_osBlockName,
    3559             :                                          oTransformer, poFeature);
    3560             : 
    3561         814 :         auto papszAttribs = m_oInsertState.m_aosAttribs.List();
    3562         814 :         if (papszAttribs)
    3563           3 :             poFeature->SetField("BlockAttributes", papszAttribs);
    3564             : 
    3565         814 :         poFeature->apoAttribFeatures = std::move(m_oInsertState.m_apoAttribs);
    3566             : 
    3567         814 :         apoPendingFeatures.push(poFeature);
    3568             :     }
    3569             :     // Otherwise, try inlining the contents of this block
    3570             :     else
    3571             :     {
    3572         114 :         OGRDXFFeatureQueue apoExtraFeatures;
    3573             :         try
    3574             :         {
    3575         228 :             poFeature = InsertBlockInline(
    3576         114 :                 CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
    3577         114 :                 std::move(oTransformer), poFeature, apoExtraFeatures, true,
    3578         114 :                 poDS->ShouldMergeBlockGeometries());
    3579             :         }
    3580           0 :         catch (const std::invalid_argument &)
    3581             :         {
    3582             :             // Block doesn't exist
    3583           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
    3584             :                      m_oInsertState.m_osBlockName.c_str());
    3585           0 :             delete poFeature;
    3586           0 :             return false;
    3587             :         }
    3588             : 
    3589         114 :         if (poFeature)
    3590          57 :             apoPendingFeatures.push(poFeature);
    3591             : 
    3592        1231 :         while (!apoExtraFeatures.empty())
    3593             :         {
    3594        1117 :             apoPendingFeatures.push(apoExtraFeatures.front());
    3595        1117 :             apoExtraFeatures.pop();
    3596             :         }
    3597             : 
    3598             :         // Append the attribute features to the pending feature stack
    3599         114 :         if (!m_oInsertState.m_apoAttribs.empty())
    3600             :         {
    3601          16 :             OGRDXFInsertTransformer oAttribTransformer;
    3602          16 :             oAttribTransformer.dfXOffset = dfExtraXOffset;
    3603          16 :             oAttribTransformer.dfYOffset = dfExtraYOffset;
    3604             : 
    3605          42 :             for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3606             :             {
    3607          26 :                 OGRDXFFeature *poAttribFeature = poAttr->CloneDXFFeature();
    3608             : 
    3609          26 :                 if (poAttribFeature->GetGeometryRef())
    3610             :                 {
    3611          26 :                     poAttribFeature->GetGeometryRef()->transform(
    3612          26 :                         &oAttribTransformer);
    3613             :                 }
    3614             : 
    3615          26 :                 apoPendingFeatures.push(poAttribFeature);
    3616             :             }
    3617             :         }
    3618             :     }
    3619         928 :     return true;
    3620             : }
    3621             : 
    3622             : /************************************************************************/
    3623             : /*                      GetNextUnfilteredFeature()                      */
    3624             : /************************************************************************/
    3625             : 
    3626        2941 : OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
    3627             : 
    3628             : {
    3629        2941 :     OGRDXFFeature *poFeature = nullptr;
    3630        4867 :     while (poFeature == nullptr)
    3631             :     {
    3632             :         /* --------------------------------------------------------------------
    3633             :          */
    3634             :         /*      If we have pending features, return one of them. */
    3635             :         /* --------------------------------------------------------------------
    3636             :          */
    3637        4246 :         if (!apoPendingFeatures.empty())
    3638             :         {
    3639        2211 :             poFeature = apoPendingFeatures.front();
    3640        2211 :             apoPendingFeatures.pop();
    3641             : 
    3642        2211 :             poFeature->SetFID(iNextFID++);
    3643        2320 :             return poFeature;
    3644             :         }
    3645             : 
    3646             :         /* --------------------------------------------------------------------
    3647             :          */
    3648             :         /*      Emit INSERT features. */
    3649             :         /* --------------------------------------------------------------------
    3650             :          */
    3651        2035 :         if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
    3652             :         {
    3653        1056 :             if (m_oInsertState.m_iCurCol == m_oInsertState.m_nColumnCount)
    3654             :             {
    3655         889 :                 m_oInsertState.m_iCurRow++;
    3656         889 :                 m_oInsertState.m_iCurCol = 0;
    3657         889 :                 if (m_oInsertState.m_iCurRow == m_oInsertState.m_nRowCount)
    3658             :                 {
    3659         128 :                     m_oInsertState.m_nRowCount = 0;
    3660         128 :                     m_oInsertState.m_nColumnCount = 0;
    3661        1056 :                     continue;
    3662             :                 }
    3663             :             }
    3664         928 :             if (GenerateINSERTFeatures())
    3665             :             {
    3666         928 :                 m_oInsertState.m_iCurCol++;
    3667             :             }
    3668             :             else
    3669             :             {
    3670           0 :                 m_oInsertState.m_nRowCount = 0;
    3671           0 :                 m_oInsertState.m_nColumnCount = 0;
    3672             :             }
    3673         928 :             continue;
    3674             :         }
    3675             : 
    3676             :         // read ahead to an entity.
    3677             :         char szLineBuf[257];
    3678         979 :         int nCode = 0;
    3679        1109 :         while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3680             :         {
    3681             :         }
    3682         979 :         if (nCode < 0)
    3683             :         {
    3684           5 :             DXF_LAYER_READER_ERROR();
    3685           5 :             return nullptr;
    3686             :         }
    3687             : 
    3688         974 :         if (EQUAL(szLineBuf, "ENDSEC"))
    3689             :         {
    3690             :             // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
    3691          20 :             poDS->UnreadValue();
    3692          20 :             return nullptr;
    3693             :         }
    3694             : 
    3695         954 :         if (EQUAL(szLineBuf, "ENDBLK"))
    3696             :         {
    3697             :             // CPLDebug( "DXF", "Clean end of block at ENDBLK." );
    3698          84 :             poDS->UnreadValue();
    3699          84 :             return nullptr;
    3700             :         }
    3701             : 
    3702             :         /* --------------------------------------------------------------------
    3703             :          */
    3704             :         /*      Handle the entity. */
    3705             :         /* --------------------------------------------------------------------
    3706             :          */
    3707         870 :         if (EQUAL(szLineBuf, "POINT"))
    3708             :         {
    3709          35 :             poFeature = TranslatePOINT();
    3710             :         }
    3711         835 :         else if (EQUAL(szLineBuf, "MTEXT"))
    3712             :         {
    3713          40 :             poFeature = TranslateMTEXT();
    3714             :         }
    3715         795 :         else if (EQUAL(szLineBuf, "TEXT"))
    3716             :         {
    3717          28 :             poFeature = TranslateTEXT(false);
    3718             :         }
    3719         767 :         else if (EQUAL(szLineBuf, "ATTDEF"))
    3720             :         {
    3721          10 :             poFeature = TranslateTEXT(true);
    3722             :         }
    3723         757 :         else if (EQUAL(szLineBuf, "LINE"))
    3724             :         {
    3725         223 :             poFeature = TranslateLINE();
    3726             :         }
    3727         534 :         else if (EQUAL(szLineBuf, "POLYLINE"))
    3728             :         {
    3729          20 :             poFeature = TranslatePOLYLINE();
    3730             :         }
    3731         514 :         else if (EQUAL(szLineBuf, "LWPOLYLINE"))
    3732             :         {
    3733          39 :             poFeature = TranslateLWPOLYLINE();
    3734             :         }
    3735         475 :         else if (EQUAL(szLineBuf, "MLINE"))
    3736             :         {
    3737           3 :             poFeature = TranslateMLINE();
    3738             :         }
    3739         472 :         else if (EQUAL(szLineBuf, "CIRCLE"))
    3740             :         {
    3741          24 :             poFeature = TranslateCIRCLE();
    3742             :         }
    3743         448 :         else if (EQUAL(szLineBuf, "ELLIPSE"))
    3744             :         {
    3745          29 :             poFeature = TranslateELLIPSE();
    3746             :         }
    3747         419 :         else if (EQUAL(szLineBuf, "ARC"))
    3748             :         {
    3749          14 :             poFeature = TranslateARC();
    3750             :         }
    3751         405 :         else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
    3752             :         {
    3753          25 :             poFeature = TranslateSPLINE();
    3754             :         }
    3755         380 :         else if (EQUAL(szLineBuf, "3DFACE"))
    3756             :         {
    3757          10 :             poFeature = Translate3DFACE();
    3758             :         }
    3759         370 :         else if (EQUAL(szLineBuf, "INSERT"))
    3760             :         {
    3761         161 :             if (!TranslateINSERT())
    3762           0 :                 return nullptr;
    3763             :         }
    3764         209 :         else if (EQUAL(szLineBuf, "DIMENSION"))
    3765             :         {
    3766          30 :             poFeature = TranslateDIMENSION();
    3767             :         }
    3768         179 :         else if (EQUAL(szLineBuf, "HATCH"))
    3769             :         {
    3770          44 :             poFeature = TranslateHATCH();
    3771             :         }
    3772         135 :         else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
    3773             :         {
    3774          25 :             poFeature = TranslateSOLID();
    3775             :         }
    3776         110 :         else if (EQUAL(szLineBuf, "LEADER"))
    3777             :         {
    3778          14 :             poFeature = TranslateLEADER();
    3779             :         }
    3780          96 :         else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
    3781             :         {
    3782          18 :             poFeature = TranslateMLEADER();
    3783             :         }
    3784          78 :         else if (EQUAL(szLineBuf, "WIPEOUT"))
    3785             :         {
    3786           4 :             poFeature = TranslateWIPEOUT();
    3787             :         }
    3788          74 :         else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
    3789          72 :                  EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
    3790             :         {
    3791           2 :             if (poDS->In3DExtensibleMode())
    3792             :             {
    3793           2 :                 poFeature = TranslateASMEntity();
    3794             :             }
    3795           0 :             else if (oIgnoredEntities.count(szLineBuf) == 0)
    3796             :             {
    3797           0 :                 oIgnoredEntities.insert(szLineBuf);
    3798           0 :                 CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
    3799             :                          szLineBuf);
    3800             :             }
    3801             :         }
    3802             :         else
    3803             :         {
    3804          72 :             if (oIgnoredEntities.count(szLineBuf) == 0)
    3805             :             {
    3806          13 :                 oIgnoredEntities.insert(szLineBuf);
    3807          13 :                 CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
    3808             :                          szLineBuf);
    3809             :             }
    3810             :         }
    3811             :     }
    3812             : 
    3813             :     /* -------------------------------------------------------------------- */
    3814             :     /*      Set FID.                                                        */
    3815             :     /* -------------------------------------------------------------------- */
    3816         621 :     poFeature->SetFID(iNextFID++);
    3817         621 :     m_nFeaturesRead++;
    3818             : 
    3819         621 :     return poFeature;
    3820             : }
    3821             : 
    3822             : /************************************************************************/
    3823             : /*                           GetNextFeature()                           */
    3824             : /************************************************************************/
    3825             : 
    3826        1787 : OGRFeature *OGRDXFLayer::GetNextFeature()
    3827             : 
    3828             : {
    3829             :     while (true)
    3830             :     {
    3831        1787 :         OGRFeature *poFeature = GetNextUnfilteredFeature();
    3832             : 
    3833        1787 :         if (poFeature == nullptr)
    3834          22 :             return nullptr;
    3835             : 
    3836        3530 :         if ((m_poFilterGeom == nullptr ||
    3837        3530 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    3838        1765 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    3839             :         {
    3840        1765 :             return poFeature;
    3841             :         }
    3842             : 
    3843           0 :         delete poFeature;
    3844           0 :     }
    3845             : }
    3846             : 
    3847             : /************************************************************************/
    3848             : /*                           TestCapability()                           */
    3849             : /************************************************************************/
    3850             : 
    3851           1 : int OGRDXFLayer::TestCapability(const char *pszCap)
    3852             : 
    3853             : {
    3854           1 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    3855           0 :         return true;
    3856           1 :     else if (EQUAL(pszCap, OLCZGeometries))
    3857           0 :         return true;
    3858           1 :     return false;
    3859             : }
    3860             : 
    3861             : /************************************************************************/
    3862             : /*                             GetDataset()                             */
    3863             : /************************************************************************/
    3864             : 
    3865           1 : GDALDataset *OGRDXFLayer::GetDataset()
    3866             : {
    3867           1 :     return poDS;
    3868             : }

Generated by: LCOV version 1.14