LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1677 1839 91.2 %
Date: 2025-04-16 00:42:22 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           2 :     if (smoothPolyline.IsEmpty())
    2881             :     {
    2882           0 :         return nullptr;
    2883             :     }
    2884             : 
    2885             :     /* -------------------------------------------------------------------- */
    2886             :     /*      Close polyline to output polygon geometry.                      */
    2887             :     /* -------------------------------------------------------------------- */
    2888           2 :     smoothPolyline.Close();
    2889             : 
    2890           2 :     OGRGeometry *poGeom = smoothPolyline.Tessellate(TRUE);
    2891             : 
    2892           2 :     poFeature->SetGeometryDirectly(poGeom);
    2893             : 
    2894             :     // Set style pen color
    2895           2 :     PrepareLineStyle(poFeature.get());
    2896             : 
    2897           2 :     return poFeature.release();
    2898             : }
    2899             : 
    2900             : /************************************************************************/
    2901             : 
    2902             : /************************************************************************/
    2903             : /*                       InsertBlockReference()                         */
    2904             : /*                                                                      */
    2905             : /*     Returns a point geometry located at the block's insertion        */
    2906             : /*     point.                                                           */
    2907             : /************************************************************************/
    2908             : OGRDXFFeature *
    2909         805 : OGRDXFLayer::InsertBlockReference(const CPLString &osBlockName,
    2910             :                                   const OGRDXFInsertTransformer &oTransformer,
    2911             :                                   OGRDXFFeature *const poFeature)
    2912             : {
    2913             :     // Store the block's properties in the special DXF-specific members
    2914             :     // on the feature object
    2915         805 :     poFeature->bIsBlockReference = true;
    2916         805 :     poFeature->osBlockName = osBlockName;
    2917         805 :     poFeature->dfBlockAngle = oTransformer.dfAngle * 180 / M_PI;
    2918        1610 :     poFeature->oBlockScale = DXFTriple(
    2919         805 :         oTransformer.dfXScale, oTransformer.dfYScale, oTransformer.dfZScale);
    2920        1610 :     poFeature->oOriginalCoords = DXFTriple(
    2921         805 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    2922             : 
    2923             :     // Only if DXF_INLINE_BLOCKS is false should we ever need to expose these
    2924             :     // to the end user as fields.
    2925         805 :     if (poFeature->GetFieldIndex("BlockName") != -1)
    2926             :     {
    2927          13 :         poFeature->SetField("BlockName", poFeature->osBlockName);
    2928          13 :         poFeature->SetField("BlockAngle", poFeature->dfBlockAngle);
    2929          13 :         poFeature->SetField("BlockScale", 3, &(poFeature->oBlockScale.dfX));
    2930          13 :         poFeature->SetField("BlockOCSNormal", 3, &(poFeature->oOCS.dfX));
    2931          13 :         poFeature->SetField("BlockOCSCoords", 3,
    2932          13 :                             &(poFeature->oOriginalCoords.dfX));
    2933             :     }
    2934             : 
    2935             :     // For convenience to the end user, the point geometry will be located
    2936             :     // at the WCS coordinates of the insertion point.
    2937             :     OGRPoint *poInsertionPoint = new OGRPoint(
    2938         805 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    2939             : 
    2940         805 :     poFeature->ApplyOCSTransformer(poInsertionPoint);
    2941         805 :     poFeature->SetGeometryDirectly(poInsertionPoint);
    2942             : 
    2943         805 :     return poFeature;
    2944             : }
    2945             : 
    2946             : /************************************************************************/
    2947             : /*                         InsertBlockInline()                          */
    2948             : /*                                                                      */
    2949             : /*     Inserts the given block at the location specified by the given   */
    2950             : /*     transformer.  Returns poFeature, or NULL if all features on      */
    2951             : /*     the block have been pushed to the extra feature queue.           */
    2952             : /*     If poFeature is not returned, it is deleted.                     */
    2953             : /*     Throws std::invalid_argument if the requested block              */
    2954             : /*     doesn't exist.                                                   */
    2955             : /*                                                                      */
    2956             : /*     - poFeature: The feature to use as a template. This feature's    */
    2957             : /*       OCS will be applied to the block.                              */
    2958             : /*     - bInlineRecursively: If true, INSERTs within this block         */
    2959             : /*       will be recursively inserted.  Otherwise, they will be         */
    2960             : /*       represented as a point geometry using InsertBlockReference.    */
    2961             : /*     - bMergeGeometry: If true, all features in the block,            */
    2962             : /*       apart from text features, are merged into a                    */
    2963             : /*       GeometryCollection which is returned by the function.          */
    2964             : /************************************************************************/
    2965             : 
    2966        1248 : OGRDXFFeature *OGRDXFLayer::InsertBlockInline(
    2967             :     GUInt32 nInitialErrorCounter, const CPLString &osBlockName,
    2968             :     OGRDXFInsertTransformer oTransformer, OGRDXFFeature *const poFeature,
    2969             :     OGRDXFFeatureQueue &apoExtraFeatures, const bool bInlineRecursively,
    2970             :     const bool bMergeGeometry)
    2971             : {
    2972             :     /* -------------------------------------------------------------------- */
    2973             :     /*      Set up protection against excessive recursion on this layer.    */
    2974             :     /* -------------------------------------------------------------------- */
    2975        1248 :     if (!poDS->PushBlockInsertion(osBlockName))
    2976             :     {
    2977        1003 :         delete poFeature;
    2978        1003 :         return nullptr;
    2979             :     }
    2980             : 
    2981             :     /* -------------------------------------------------------------------- */
    2982             :     /*      Transform the insertion point from OCS into                     */
    2983             :     /*      world coordinates.                                              */
    2984             :     /* -------------------------------------------------------------------- */
    2985             :     OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
    2986         490 :                              oTransformer.dfZOffset);
    2987             : 
    2988         245 :     poFeature->ApplyOCSTransformer(&oInsertionPoint);
    2989             : 
    2990         245 :     oTransformer.dfXOffset = oInsertionPoint.getX();
    2991         245 :     oTransformer.dfYOffset = oInsertionPoint.getY();
    2992         245 :     oTransformer.dfZOffset = oInsertionPoint.getZ();
    2993             : 
    2994             :     /* -------------------------------------------------------------------- */
    2995             :     /*      Lookup the block.                                               */
    2996             :     /* -------------------------------------------------------------------- */
    2997         245 :     DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
    2998             : 
    2999         245 :     if (poBlock == nullptr)
    3000             :     {
    3001             :         // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
    3002           9 :         poDS->PopBlockInsertion();
    3003           9 :         throw std::invalid_argument("osBlockName");
    3004             :     }
    3005             : 
    3006             :     /* -------------------------------------------------------------------- */
    3007             :     /*      If we have complete features associated with the block, push    */
    3008             :     /*      them on the pending feature stack copying over key override     */
    3009             :     /*      information.                                                    */
    3010             :     /*                                                                      */
    3011             :     /*      If bMergeGeometry is true, we merge the features                */
    3012             :     /*      (except text) into a single GeometryCollection.                 */
    3013             :     /* -------------------------------------------------------------------- */
    3014         236 :     OGRGeometryCollection *poMergedGeometry = nullptr;
    3015         236 :     if (bMergeGeometry)
    3016          93 :         poMergedGeometry = new OGRGeometryCollection();
    3017             : 
    3018         472 :     OGRDXFFeatureQueue apoInnerExtraFeatures;
    3019             : 
    3020        1975 :     for (unsigned int iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size();
    3021             :          iSubFeat++)
    3022             :     {
    3023             :         OGRDXFFeature *poSubFeature =
    3024        1741 :             poBlock->apoFeatures[iSubFeat]->CloneDXFFeature();
    3025             : 
    3026             :         // If the template feature is in PaperSpace, set this on the
    3027             :         // subfeature too
    3028        1741 :         if (poFeature->GetFieldAsInteger("PaperSpace"))
    3029          48 :             poSubFeature->SetField("PaperSpace", 1);
    3030             : 
    3031             :         // Does this feature represent a block reference? If so,
    3032             :         // insert that block
    3033        1741 :         if (bInlineRecursively && poSubFeature->IsBlockReference())
    3034             :         {
    3035             :             // Unpack the transformation data stored in fields of this
    3036             :             // feature
    3037           0 :             OGRDXFInsertTransformer oInnerTransformer;
    3038        1089 :             oInnerTransformer.dfXOffset = poSubFeature->oOriginalCoords.dfX;
    3039        1089 :             oInnerTransformer.dfYOffset = poSubFeature->oOriginalCoords.dfY;
    3040        1089 :             oInnerTransformer.dfZOffset = poSubFeature->oOriginalCoords.dfZ;
    3041        1089 :             oInnerTransformer.dfAngle = poSubFeature->dfBlockAngle * M_PI / 180;
    3042        1089 :             oInnerTransformer.dfXScale = poSubFeature->oBlockScale.dfX;
    3043        1089 :             oInnerTransformer.dfYScale = poSubFeature->oBlockScale.dfY;
    3044        1089 :             oInnerTransformer.dfZScale = poSubFeature->oBlockScale.dfZ;
    3045             : 
    3046        1089 :             poSubFeature->bIsBlockReference = false;
    3047             : 
    3048             :             // Keep a reference to the attributes that need to be inserted
    3049             :             std::vector<std::unique_ptr<OGRDXFFeature>> apoInnerAttribFeatures =
    3050        1089 :                 std::move(poSubFeature->apoAttribFeatures);
    3051             : 
    3052             :             // Insert this block recursively
    3053             :             try
    3054             :             {
    3055        2178 :                 poSubFeature = InsertBlockInline(
    3056        1089 :                     nInitialErrorCounter, poSubFeature->osBlockName,
    3057        1089 :                     std::move(oInnerTransformer), poSubFeature,
    3058             :                     apoInnerExtraFeatures, true, bMergeGeometry);
    3059             :             }
    3060           0 :             catch (const std::invalid_argument &)
    3061             :             {
    3062             :                 // Block doesn't exist. Skip it and keep going
    3063           0 :                 delete poSubFeature;
    3064           0 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    3065             :                 {
    3066           0 :                     break;
    3067             :                 }
    3068           0 :                 continue;
    3069             :             }
    3070             : 
    3071        1089 :             if (!poSubFeature)
    3072             :             {
    3073        1083 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    3074             :                 {
    3075           2 :                     break;
    3076             :                 }
    3077             : 
    3078             :                 // Append the attribute features to the pending feature stack
    3079        1083 :                 for (auto &poAttribFeature : apoInnerAttribFeatures)
    3080             :                 {
    3081             :                     // Clear the attribute tag so the feature doesn't get mistaken
    3082             :                     // for an ATTDEF and skipped
    3083           2 :                     poAttribFeature->osAttributeTag = "";
    3084             : 
    3085           2 :                     apoInnerExtraFeatures.push(poAttribFeature.release());
    3086             :                 }
    3087             : 
    3088        1081 :                 if (apoInnerExtraFeatures.empty())
    3089             :                 {
    3090             :                     // Block is empty and has no attributes. Skip it and keep going
    3091        1002 :                     continue;
    3092             :                 }
    3093             :                 else
    3094             :                 {
    3095             :                     // Load up the first extra feature ready for
    3096             :                     // transformation
    3097          79 :                     poSubFeature = apoInnerExtraFeatures.front();
    3098          79 :                     apoInnerExtraFeatures.pop();
    3099             :                 }
    3100             :             }
    3101             :         }
    3102             : 
    3103             :         // Go through the current feature and any extra features generated
    3104             :         // by the recursive insert, and apply transformations
    3105             :         while (true)
    3106             :         {
    3107         888 :             OGRGeometry *poSubFeatGeom = poSubFeature->GetGeometryRef();
    3108         888 :             if (poSubFeatGeom != nullptr)
    3109             :             {
    3110             :                 // Rotation and scaling first
    3111             :                 OGRDXFInsertTransformer oInnerTrans =
    3112        1770 :                     oTransformer.GetRotateScaleTransformer();
    3113         885 :                 poSubFeatGeom->transform(&oInnerTrans);
    3114             : 
    3115             :                 // Then the OCS to WCS transformation
    3116         885 :                 poFeature->ApplyOCSTransformer(poSubFeatGeom);
    3117             : 
    3118             :                 // Offset translation last
    3119         885 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    3120         885 :                 poSubFeatGeom->transform(&oInnerTrans);
    3121             :             }
    3122             :             // Transform the specially-stored data for ASM entities
    3123           3 :             else if (poSubFeature->poASMTransform)
    3124             :             {
    3125             :                 // Rotation and scaling first
    3126             :                 OGRDXFInsertTransformer oInnerTrans =
    3127           6 :                     oTransformer.GetRotateScaleTransformer();
    3128           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    3129             : 
    3130             :                 // Then the OCS to WCS transformation
    3131           3 :                 poFeature->ApplyOCSTransformer(
    3132             :                     poSubFeature->poASMTransform.get());
    3133             : 
    3134             :                 // Offset translation last
    3135           3 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    3136           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    3137             : 
    3138           3 :                 poSubFeature->poASMTransform->SetField(poSubFeature,
    3139             :                                                        "ASMTransform");
    3140             :             }
    3141             : 
    3142             :             // If we are merging features, and this is not text or a block
    3143             :             // reference, merge it into the GeometryCollection
    3144        1080 :             if (bMergeGeometry &&
    3145         192 :                 (poSubFeature->GetStyleString() == nullptr ||
    3146         175 :                  strstr(poSubFeature->GetStyleString(), "LABEL") == nullptr) &&
    3147        1197 :                 !poSubFeature->IsBlockReference() &&
    3148         117 :                 poSubFeature->GetGeometryRef())
    3149             :             {
    3150         114 :                 poMergedGeometry->addGeometryDirectly(
    3151         114 :                     poSubFeature->StealGeometry());
    3152         114 :                 delete poSubFeature;
    3153             :             }
    3154             :             // Import all other features, except ATTDEFs when inlining
    3155             :             // recursively
    3156         774 :             else if (!bInlineRecursively || poSubFeature->osAttributeTag == "")
    3157             :             {
    3158             :                 // If the subfeature is on layer 0, this is a special case: the
    3159             :                 // subfeature should take on the style properties of the layer
    3160             :                 // the block is being inserted onto.
    3161             :                 // But don't do this if we are inserting onto a Blocks layer
    3162             :                 // (that is, the owning feature has no layer).
    3163         973 :                 if (EQUAL(poSubFeature->GetFieldAsString("Layer"), "0") &&
    3164         231 :                     !EQUAL(poFeature->GetFieldAsString("Layer"), ""))
    3165             :                 {
    3166         200 :                     poSubFeature->SetField(
    3167             :                         "Layer", poFeature->GetFieldAsString("Layer"));
    3168             :                 }
    3169             : 
    3170             :                 // Update the style string to replace ByBlock and ByLayer
    3171             :                 // values.
    3172         742 :                 PrepareFeatureStyle(poSubFeature, poFeature);
    3173             : 
    3174         742 :                 ACAdjustText(oTransformer.dfAngle * 180 / M_PI,
    3175             :                              oTransformer.dfXScale, oTransformer.dfYScale,
    3176             :                              poSubFeature);
    3177             : 
    3178         742 :                 if (!EQUAL(poFeature->GetFieldAsString("EntityHandle"), ""))
    3179             :                 {
    3180         705 :                     poSubFeature->SetField(
    3181             :                         "EntityHandle",
    3182             :                         poFeature->GetFieldAsString("EntityHandle"));
    3183             :                 }
    3184             : 
    3185         742 :                 apoExtraFeatures.push(poSubFeature);
    3186             :             }
    3187             :             else
    3188             :             {
    3189          32 :                 delete poSubFeature;
    3190             :             }
    3191             : 
    3192         888 :             if (apoInnerExtraFeatures.empty())
    3193             :             {
    3194         737 :                 break;
    3195             :             }
    3196             :             else
    3197             :             {
    3198         151 :                 poSubFeature = apoInnerExtraFeatures.front();
    3199         151 :                 apoInnerExtraFeatures.pop();
    3200             :             }
    3201         151 :         }
    3202             :     }
    3203             : 
    3204         238 :     while (!apoInnerExtraFeatures.empty())
    3205             :     {
    3206           2 :         auto poFeatureToDelete = apoInnerExtraFeatures.front();
    3207           2 :         apoInnerExtraFeatures.pop();
    3208           2 :         delete poFeatureToDelete;
    3209             :     }
    3210             : 
    3211         236 :     poDS->PopBlockInsertion();
    3212             : 
    3213             :     /* -------------------------------------------------------------------- */
    3214             :     /*      Return the merged geometry if applicable.  Otherwise            */
    3215             :     /*      return NULL and let the machinery find the rest of the          */
    3216             :     /*      features in the pending feature stack.                          */
    3217             :     /* -------------------------------------------------------------------- */
    3218         236 :     if (bMergeGeometry)
    3219             :     {
    3220          93 :         if (poMergedGeometry->getNumGeometries() == 0)
    3221             :         {
    3222          14 :             delete poMergedGeometry;
    3223             :         }
    3224             :         else
    3225             :         {
    3226          79 :             poFeature->SetGeometryDirectly(
    3227             :                 SimplifyBlockGeometry(poMergedGeometry));
    3228             : 
    3229          79 :             PrepareLineStyle(poFeature);
    3230          79 :             return poFeature;
    3231             :         }
    3232             :     }
    3233             : 
    3234         157 :     delete poFeature;
    3235         157 :     return nullptr;
    3236             : }
    3237             : 
    3238             : /************************************************************************/
    3239             : /*                          TranslateINSERT()                           */
    3240             : /************************************************************************/
    3241             : 
    3242         141 : bool OGRDXFLayer::TranslateINSERT()
    3243             : 
    3244             : {
    3245             :     char szLineBuf[257];
    3246         141 :     int nCode = 0;
    3247             : 
    3248         141 :     m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
    3249         141 :     m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
    3250         141 :     m_oInsertState.m_osBlockName.clear();
    3251         141 :     m_oInsertState.m_nColumnCount = 1;
    3252         141 :     m_oInsertState.m_nRowCount = 1;
    3253         141 :     m_oInsertState.m_iCurCol = 0;
    3254         141 :     m_oInsertState.m_iCurRow = 0;
    3255         141 :     m_oInsertState.m_dfColumnSpacing = 0.0;
    3256         141 :     m_oInsertState.m_dfRowSpacing = 0.0;
    3257             : 
    3258         141 :     bool bHasAttribs = false;
    3259         141 :     m_oInsertState.m_apoAttribs.clear();
    3260         141 :     m_oInsertState.m_aosAttribs.Clear();
    3261             : 
    3262             :     /* -------------------------------------------------------------------- */
    3263             :     /*      Process values.                                                 */
    3264             :     /* -------------------------------------------------------------------- */
    3265        1469 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3266             :     {
    3267        1328 :         switch (nCode)
    3268             :         {
    3269         139 :             case 10:
    3270         139 :                 m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
    3271         139 :                 break;
    3272             : 
    3273         139 :             case 20:
    3274         139 :                 m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
    3275         139 :                 break;
    3276             : 
    3277         132 :             case 30:
    3278         132 :                 m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
    3279         132 :                 break;
    3280             : 
    3281          29 :             case 41:
    3282          29 :                 m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
    3283          29 :                 break;
    3284             : 
    3285          32 :             case 42:
    3286          32 :                 m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
    3287          32 :                 break;
    3288             : 
    3289          17 :             case 43:
    3290          17 :                 m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
    3291          17 :                 break;
    3292             : 
    3293           6 :             case 44:
    3294           6 :                 m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
    3295           6 :                 break;
    3296             : 
    3297           6 :             case 45:
    3298           6 :                 m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
    3299           6 :                 break;
    3300             : 
    3301          24 :             case 50:
    3302             :                 // We want to transform this to radians.
    3303             :                 // It is apparently always in degrees regardless of $AUNITS
    3304          24 :                 m_oInsertState.m_oTransformer.dfAngle =
    3305          24 :                     CPLAtof(szLineBuf) * M_PI / 180.0;
    3306          24 :                 break;
    3307             : 
    3308          14 :             case 66:
    3309          14 :                 bHasAttribs = atoi(szLineBuf) == 1;
    3310          14 :                 break;
    3311             : 
    3312           2 :             case 70:
    3313           2 :                 m_oInsertState.m_nColumnCount = atoi(szLineBuf);
    3314           2 :                 if (m_oInsertState.m_nColumnCount < 0)
    3315             :                 {
    3316           0 :                     DXF_LAYER_READER_ERROR();
    3317           0 :                     m_oInsertState.m_nRowCount = 0;
    3318           0 :                     m_oInsertState.m_nColumnCount = 0;
    3319           0 :                     return false;
    3320             :                 }
    3321           2 :                 break;
    3322             : 
    3323           3 :             case 71:
    3324           3 :                 m_oInsertState.m_nRowCount = atoi(szLineBuf);
    3325           3 :                 if (m_oInsertState.m_nRowCount < 0)
    3326             :                 {
    3327           0 :                     DXF_LAYER_READER_ERROR();
    3328           0 :                     m_oInsertState.m_nRowCount = 0;
    3329           0 :                     m_oInsertState.m_nColumnCount = 0;
    3330           0 :                     return false;
    3331             :                 }
    3332           3 :                 break;
    3333             : 
    3334         139 :             case 2:
    3335         139 :                 m_oInsertState.m_osBlockName = szLineBuf;
    3336         139 :                 break;
    3337             : 
    3338         646 :             default:
    3339         646 :                 TranslateGenericProperty(
    3340             :                     m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
    3341         646 :                 break;
    3342             :         }
    3343             :     }
    3344         141 :     if (nCode < 0)
    3345             :     {
    3346           0 :         DXF_LAYER_READER_ERROR();
    3347           0 :         m_oInsertState.m_nRowCount = 0;
    3348           0 :         m_oInsertState.m_nColumnCount = 0;
    3349           0 :         return false;
    3350             :     }
    3351             : 
    3352         141 :     if (m_oInsertState.m_nRowCount == 0 || m_oInsertState.m_nColumnCount == 0)
    3353             :     {
    3354             :         // AutoCad doesn't allow setting to 0 in its UI, but interprets 0
    3355             :         // as 1 (but other software such as LibreCAD interpret 0 as 0)
    3356           1 :         m_oInsertState.m_nRowCount = 1;
    3357           1 :         m_oInsertState.m_nColumnCount = 1;
    3358             :     }
    3359             : 
    3360             :     /* -------------------------------------------------------------------- */
    3361             :     /*      Process any attribute entities.                                 */
    3362             :     /* -------------------------------------------------------------------- */
    3363             : 
    3364         141 :     if (bHasAttribs)
    3365             :     {
    3366          41 :         while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
    3367             :         {
    3368          27 :             if (!EQUAL(szLineBuf, "ATTRIB"))
    3369             :             {
    3370           0 :                 DXF_LAYER_READER_ERROR();
    3371           0 :                 m_oInsertState.m_nRowCount = 0;
    3372           0 :                 m_oInsertState.m_nColumnCount = 0;
    3373           0 :                 return false;
    3374             :             }
    3375             : 
    3376             :             auto poAttribFeature =
    3377          27 :                 std::unique_ptr<OGRDXFFeature>(TranslateTEXT(true));
    3378             : 
    3379          27 :             if (poAttribFeature && poAttribFeature->osAttributeTag != "")
    3380             :             {
    3381             :                 m_oInsertState.m_apoAttribs.emplace_back(
    3382          27 :                     std::move(poAttribFeature));
    3383             :             }
    3384             : 
    3385          27 :             nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
    3386             :         }
    3387             :     }
    3388         127 :     else if (nCode == 0)
    3389             :     {
    3390         127 :         poDS->UnreadValue();
    3391             :     }
    3392             : 
    3393             :     /* -------------------------------------------------------------------- */
    3394             :     /*      Prepare a string list of the attributes and their text values   */
    3395             :     /*      as space-separated entries, to be stored in the                 */
    3396             :     /*      BlockAttributes field if we are not inlining blocks.            */
    3397             :     /* -------------------------------------------------------------------- */
    3398             : 
    3399         145 :     if (!poDS->InlineBlocks() && bHasAttribs &&
    3400           4 :         poFeatureDefn->GetFieldIndex("BlockAttributes") != -1)
    3401             :     {
    3402          10 :         for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3403             :         {
    3404          14 :             CPLString osAttribString = poAttr->osAttributeTag;
    3405           7 :             osAttribString += " ";
    3406           7 :             osAttribString += poAttr->GetFieldAsString("Text");
    3407             : 
    3408           7 :             m_oInsertState.m_aosAttribs.AddString(osAttribString);
    3409             :         }
    3410             :     }
    3411             : 
    3412         141 :     return true;
    3413             : }
    3414             : 
    3415             : /************************************************************************/
    3416             : /*                       GenerateINSERTFeatures()                       */
    3417             : /************************************************************************/
    3418             : 
    3419         908 : bool OGRDXFLayer::GenerateINSERTFeatures()
    3420             : {
    3421             :     OGRDXFFeature *poFeature =
    3422         908 :         m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
    3423             : 
    3424         908 :     const double dfExtraXOffset =
    3425         908 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3426         908 :             cos(m_oInsertState.m_oTransformer.dfAngle) +
    3427         908 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3428         908 :             -sin(m_oInsertState.m_oTransformer.dfAngle);
    3429         908 :     const double dfExtraYOffset =
    3430         908 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3431         908 :             sin(m_oInsertState.m_oTransformer.dfAngle) +
    3432         908 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3433         908 :             cos(m_oInsertState.m_oTransformer.dfAngle);
    3434             : 
    3435        1816 :     OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
    3436         908 :     oTransformer.dfXOffset += dfExtraXOffset;
    3437         908 :     oTransformer.dfYOffset += dfExtraYOffset;
    3438             : 
    3439             :     // If we are not inlining blocks, just insert a point that refers
    3440             :     // to this block
    3441         908 :     if (!poDS->InlineBlocks())
    3442             :     {
    3443         805 :         poFeature = InsertBlockReference(m_oInsertState.m_osBlockName,
    3444             :                                          oTransformer, poFeature);
    3445             : 
    3446         805 :         auto papszAttribs = m_oInsertState.m_aosAttribs.List();
    3447         805 :         if (papszAttribs)
    3448           3 :             poFeature->SetField("BlockAttributes", papszAttribs);
    3449             : 
    3450         805 :         poFeature->apoAttribFeatures = std::move(m_oInsertState.m_apoAttribs);
    3451             : 
    3452         805 :         apoPendingFeatures.push(poFeature);
    3453             :     }
    3454             :     // Otherwise, try inlining the contents of this block
    3455             :     else
    3456             :     {
    3457         103 :         OGRDXFFeatureQueue apoExtraFeatures;
    3458             :         try
    3459             :         {
    3460         206 :             poFeature = InsertBlockInline(
    3461         103 :                 CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
    3462         103 :                 std::move(oTransformer), poFeature, apoExtraFeatures, true,
    3463         103 :                 poDS->ShouldMergeBlockGeometries());
    3464             :         }
    3465           0 :         catch (const std::invalid_argument &)
    3466             :         {
    3467             :             // Block doesn't exist
    3468           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
    3469             :                      m_oInsertState.m_osBlockName.c_str());
    3470           0 :             delete poFeature;
    3471           0 :             return false;
    3472             :         }
    3473             : 
    3474         103 :         if (poFeature)
    3475          55 :             apoPendingFeatures.push(poFeature);
    3476             : 
    3477         410 :         while (!apoExtraFeatures.empty())
    3478             :         {
    3479         307 :             apoPendingFeatures.push(apoExtraFeatures.front());
    3480         307 :             apoExtraFeatures.pop();
    3481             :         }
    3482             : 
    3483             :         // Append the attribute features to the pending feature stack
    3484         103 :         if (!m_oInsertState.m_apoAttribs.empty())
    3485             :         {
    3486          16 :             OGRDXFInsertTransformer oAttribTransformer;
    3487          16 :             oAttribTransformer.dfXOffset = dfExtraXOffset;
    3488          16 :             oAttribTransformer.dfYOffset = dfExtraYOffset;
    3489             : 
    3490          42 :             for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3491             :             {
    3492          26 :                 OGRDXFFeature *poAttribFeature = poAttr->CloneDXFFeature();
    3493             : 
    3494          26 :                 if (poAttribFeature->GetGeometryRef())
    3495             :                 {
    3496          26 :                     poAttribFeature->GetGeometryRef()->transform(
    3497          26 :                         &oAttribTransformer);
    3498             :                 }
    3499             : 
    3500          26 :                 apoPendingFeatures.push(poAttribFeature);
    3501             :             }
    3502             :         }
    3503             :     }
    3504         908 :     return true;
    3505             : }
    3506             : 
    3507             : /************************************************************************/
    3508             : /*                      GetNextUnfilteredFeature()                      */
    3509             : /************************************************************************/
    3510             : 
    3511        2051 : OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
    3512             : 
    3513             : {
    3514        2051 :     OGRDXFFeature *poFeature = nullptr;
    3515        3861 :     while (poFeature == nullptr)
    3516             :     {
    3517             :         /* --------------------------------------------------------------------
    3518             :          */
    3519             :         /*      If we have pending features, return one of them. */
    3520             :         /* --------------------------------------------------------------------
    3521             :          */
    3522        3298 :         if (!apoPendingFeatures.empty())
    3523             :         {
    3524        1390 :             poFeature = apoPendingFeatures.front();
    3525        1390 :             apoPendingFeatures.pop();
    3526             : 
    3527        1390 :             poFeature->SetFID(iNextFID++);
    3528        1488 :             return poFeature;
    3529             :         }
    3530             : 
    3531             :         /* --------------------------------------------------------------------
    3532             :          */
    3533             :         /*      Emit INSERT features. */
    3534             :         /* --------------------------------------------------------------------
    3535             :          */
    3536        1908 :         if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
    3537             :         {
    3538        1018 :             if (m_oInsertState.m_iCurCol == m_oInsertState.m_nColumnCount)
    3539             :             {
    3540         871 :                 m_oInsertState.m_iCurRow++;
    3541         871 :                 m_oInsertState.m_iCurCol = 0;
    3542         871 :                 if (m_oInsertState.m_iCurRow == m_oInsertState.m_nRowCount)
    3543             :                 {
    3544         110 :                     m_oInsertState.m_nRowCount = 0;
    3545         110 :                     m_oInsertState.m_nColumnCount = 0;
    3546        1018 :                     continue;
    3547             :                 }
    3548             :             }
    3549         908 :             if (GenerateINSERTFeatures())
    3550             :             {
    3551         908 :                 m_oInsertState.m_iCurCol++;
    3552             :             }
    3553             :             else
    3554             :             {
    3555           0 :                 m_oInsertState.m_nRowCount = 0;
    3556           0 :                 m_oInsertState.m_nColumnCount = 0;
    3557             :             }
    3558         908 :             continue;
    3559             :         }
    3560             : 
    3561             :         // read ahead to an entity.
    3562             :         char szLineBuf[257];
    3563         890 :         int nCode = 0;
    3564        1016 :         while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3565             :         {
    3566             :         }
    3567         890 :         if (nCode < 0)
    3568             :         {
    3569           5 :             DXF_LAYER_READER_ERROR();
    3570           5 :             return nullptr;
    3571             :         }
    3572             : 
    3573         885 :         if (EQUAL(szLineBuf, "ENDSEC"))
    3574             :         {
    3575             :             // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
    3576          14 :             poDS->UnreadValue();
    3577          14 :             return nullptr;
    3578             :         }
    3579             : 
    3580         871 :         if (EQUAL(szLineBuf, "ENDBLK"))
    3581             :         {
    3582             :             // CPLDebug( "DXF", "Clean end of block at ENDBLK." );
    3583          79 :             poDS->UnreadValue();
    3584          79 :             return nullptr;
    3585             :         }
    3586             : 
    3587             :         /* --------------------------------------------------------------------
    3588             :          */
    3589             :         /*      Handle the entity. */
    3590             :         /* --------------------------------------------------------------------
    3591             :          */
    3592         792 :         if (EQUAL(szLineBuf, "POINT"))
    3593             :         {
    3594          35 :             poFeature = TranslatePOINT();
    3595             :         }
    3596         757 :         else if (EQUAL(szLineBuf, "MTEXT"))
    3597             :         {
    3598          40 :             poFeature = TranslateMTEXT();
    3599             :         }
    3600         717 :         else if (EQUAL(szLineBuf, "TEXT"))
    3601             :         {
    3602          26 :             poFeature = TranslateTEXT(false);
    3603             :         }
    3604         691 :         else if (EQUAL(szLineBuf, "ATTDEF"))
    3605             :         {
    3606          10 :             poFeature = TranslateTEXT(true);
    3607             :         }
    3608         681 :         else if (EQUAL(szLineBuf, "LINE"))
    3609             :         {
    3610         188 :             poFeature = TranslateLINE();
    3611             :         }
    3612         493 :         else if (EQUAL(szLineBuf, "POLYLINE"))
    3613             :         {
    3614          19 :             poFeature = TranslatePOLYLINE();
    3615             :         }
    3616         474 :         else if (EQUAL(szLineBuf, "LWPOLYLINE"))
    3617             :         {
    3618          37 :             poFeature = TranslateLWPOLYLINE();
    3619             :         }
    3620         437 :         else if (EQUAL(szLineBuf, "MLINE"))
    3621             :         {
    3622           3 :             poFeature = TranslateMLINE();
    3623             :         }
    3624         434 :         else if (EQUAL(szLineBuf, "CIRCLE"))
    3625             :         {
    3626          24 :             poFeature = TranslateCIRCLE();
    3627             :         }
    3628         410 :         else if (EQUAL(szLineBuf, "ELLIPSE"))
    3629             :         {
    3630          29 :             poFeature = TranslateELLIPSE();
    3631             :         }
    3632         381 :         else if (EQUAL(szLineBuf, "ARC"))
    3633             :         {
    3634          14 :             poFeature = TranslateARC();
    3635             :         }
    3636         367 :         else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
    3637             :         {
    3638          25 :             poFeature = TranslateSPLINE();
    3639             :         }
    3640         342 :         else if (EQUAL(szLineBuf, "3DFACE"))
    3641             :         {
    3642          10 :             poFeature = Translate3DFACE();
    3643             :         }
    3644         332 :         else if (EQUAL(szLineBuf, "INSERT"))
    3645             :         {
    3646         141 :             if (!TranslateINSERT())
    3647           0 :                 return nullptr;
    3648             :         }
    3649         191 :         else if (EQUAL(szLineBuf, "DIMENSION"))
    3650             :         {
    3651          30 :             poFeature = TranslateDIMENSION();
    3652             :         }
    3653         161 :         else if (EQUAL(szLineBuf, "HATCH"))
    3654             :         {
    3655          28 :             poFeature = TranslateHATCH();
    3656             :         }
    3657         133 :         else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
    3658             :         {
    3659          25 :             poFeature = TranslateSOLID();
    3660             :         }
    3661         108 :         else if (EQUAL(szLineBuf, "LEADER"))
    3662             :         {
    3663          14 :             poFeature = TranslateLEADER();
    3664             :         }
    3665          94 :         else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
    3666             :         {
    3667          18 :             poFeature = TranslateMLEADER();
    3668             :         }
    3669          76 :         else if (EQUAL(szLineBuf, "WIPEOUT"))
    3670             :         {
    3671           2 :             poFeature = TranslateWIPEOUT();
    3672             :         }
    3673          74 :         else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
    3674          72 :                  EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
    3675             :         {
    3676           2 :             if (poDS->In3DExtensibleMode())
    3677             :             {
    3678           2 :                 poFeature = TranslateASMEntity();
    3679             :             }
    3680           0 :             else if (oIgnoredEntities.count(szLineBuf) == 0)
    3681             :             {
    3682           0 :                 oIgnoredEntities.insert(szLineBuf);
    3683           0 :                 CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
    3684             :                          szLineBuf);
    3685             :             }
    3686             :         }
    3687             :         else
    3688             :         {
    3689          72 :             if (oIgnoredEntities.count(szLineBuf) == 0)
    3690             :             {
    3691          13 :                 oIgnoredEntities.insert(szLineBuf);
    3692          13 :                 CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
    3693             :                          szLineBuf);
    3694             :             }
    3695             :         }
    3696             :     }
    3697             : 
    3698             :     /* -------------------------------------------------------------------- */
    3699             :     /*      Set FID.                                                        */
    3700             :     /* -------------------------------------------------------------------- */
    3701         563 :     poFeature->SetFID(iNextFID++);
    3702         563 :     m_nFeaturesRead++;
    3703             : 
    3704         563 :     return poFeature;
    3705             : }
    3706             : 
    3707             : /************************************************************************/
    3708             : /*                           GetNextFeature()                           */
    3709             : /************************************************************************/
    3710             : 
    3711         934 : OGRFeature *OGRDXFLayer::GetNextFeature()
    3712             : 
    3713             : {
    3714             :     while (true)
    3715             :     {
    3716         934 :         OGRFeature *poFeature = GetNextUnfilteredFeature();
    3717             : 
    3718         934 :         if (poFeature == nullptr)
    3719          16 :             return nullptr;
    3720             : 
    3721        1836 :         if ((m_poFilterGeom == nullptr ||
    3722        1836 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    3723         918 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    3724             :         {
    3725         918 :             return poFeature;
    3726             :         }
    3727             : 
    3728           0 :         delete poFeature;
    3729           0 :     }
    3730             : }
    3731             : 
    3732             : /************************************************************************/
    3733             : /*                           TestCapability()                           */
    3734             : /************************************************************************/
    3735             : 
    3736           0 : int OGRDXFLayer::TestCapability(const char *pszCap)
    3737             : 
    3738             : {
    3739           0 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    3740           0 :         return true;
    3741           0 :     else if (EQUAL(pszCap, OLCZGeometries))
    3742           0 :         return true;
    3743           0 :     return false;
    3744             : }
    3745             : 
    3746             : /************************************************************************/
    3747             : /*                             GetDataset()                             */
    3748             : /************************************************************************/
    3749             : 
    3750           1 : GDALDataset *OGRDXFLayer::GetDataset()
    3751             : {
    3752           1 :     return poDS;
    3753             : }

Generated by: LCOV version 1.14