LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1676 1837 91.2 %
Date: 2025-03-25 20:12:57 Functions: 40 41 97.6 %

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

Generated by: LCOV version 1.14