LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1613 1766 91.3 %
Date: 2024-11-21 22:18:42 Functions: 39 40 97.5 %

          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        1736 : void OGRDXFFeatureQueue::push(OGRDXFFeature *poFeature)
      31             : {
      32        1736 :     apoFeatures.push(poFeature);
      33        1736 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                                 pop()                                */
      37             : /************************************************************************/
      38             : 
      39        1736 : void OGRDXFFeatureQueue::pop()
      40             : {
      41        1736 :     CPLAssert(!apoFeatures.empty());
      42        1736 :     apoFeatures.pop();
      43        1736 : }
      44             : 
      45             : /************************************************************************/
      46             : /*                            OGRDXFLayer()                             */
      47             : /************************************************************************/
      48             : 
      49         167 : OGRDXFLayer::OGRDXFLayer(OGRDXFDataSource *poDSIn)
      50         167 :     : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("entities")), iNextFID(0)
      51             : {
      52         167 :     poFeatureDefn->Reference();
      53             : 
      54         167 :     int nModes = ODFM_None;
      55         167 :     if (!poDS->InlineBlocks())
      56          24 :         nModes |= ODFM_IncludeBlockFields;
      57         167 :     if (poDS->ShouldIncludeRawCodeValues())
      58           1 :         nModes |= ODFM_IncludeRawCodeValues;
      59         167 :     if (poDS->In3DExtensibleMode())
      60           1 :         nModes |= ODFM_Include3DModeFields;
      61         167 :     OGRDXFDataSource::AddStandardFields(poFeatureDefn, nModes);
      62             : 
      63         167 :     SetDescription(poFeatureDefn->GetName());
      64         167 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                           ~OGRDXFLayer()                           */
      68             : /************************************************************************/
      69             : 
      70         314 : OGRDXFLayer::~OGRDXFLayer()
      71             : 
      72             : {
      73         167 :     ClearPendingFeatures();
      74         167 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
      75             :     {
      76          70 :         CPLDebug("DXF", "%d features read on layer '%s'.", (int)m_nFeaturesRead,
      77          70 :                  poFeatureDefn->GetName());
      78             :     }
      79             : 
      80         167 :     if (poFeatureDefn)
      81         167 :         poFeatureDefn->Release();
      82         314 : }
      83             : 
      84             : /************************************************************************/
      85             : /*                        ClearPendingFeatures()                        */
      86             : /************************************************************************/
      87             : 
      88         379 : void OGRDXFLayer::ClearPendingFeatures()
      89             : 
      90             : {
      91         379 :     while (!apoPendingFeatures.empty())
      92             :     {
      93          55 :         OGRDXFFeature *poFeature = apoPendingFeatures.front();
      94          55 :         apoPendingFeatures.pop();
      95          55 :         delete poFeature;
      96             :     }
      97         324 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                            ResetReading()                            */
     101             : /************************************************************************/
     102             : 
     103         157 : void OGRDXFLayer::ResetReading()
     104             : 
     105             : {
     106         157 :     iNextFID = 0;
     107         157 :     ClearPendingFeatures();
     108         157 :     m_oInsertState.m_nRowCount = 0;
     109         157 :     m_oInsertState.m_nColumnCount = 0;
     110         157 :     poDS->RestartEntities();
     111         157 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                      TranslateGenericProperty()                      */
     115             : /*                                                                      */
     116             : /*      Try and convert entity properties handled similarly for most    */
     117             : /*      or all entity types.                                            */
     118             : /************************************************************************/
     119             : 
     120        6555 : void OGRDXFLayer::TranslateGenericProperty(OGRDXFFeature *poFeature, int nCode,
     121             :                                            char *pszValue)
     122             : 
     123             : {
     124        6555 :     switch (nCode)
     125             :     {
     126         698 :         case 8:
     127         698 :             poFeature->SetField("Layer", TextRecode(pszValue));
     128         698 :             break;
     129             : 
     130        1324 :         case 100:
     131             :         {
     132        2648 :             CPLString osSubClass = poFeature->GetFieldAsString("SubClasses");
     133        1324 :             if (!osSubClass.empty())
     134         717 :                 osSubClass += ":";
     135        1324 :             osSubClass += pszValue;
     136        1324 :             poFeature->SetField("SubClasses", osSubClass.c_str());
     137             :         }
     138        1324 :         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         318 :         case 62:
     175         318 :             poFeature->oStyleProperties["Color"] = pszValue;
     176         318 :             break;
     177             : 
     178          12 :         case 420:
     179          12 :             poFeature->oStyleProperties["TrueColor"] = pszValue;
     180          12 :             break;
     181             : 
     182         256 :         case 6:
     183         256 :             poFeature->SetField("Linetype", TextRecode(pszValue));
     184         256 :             break;
     185             : 
     186           7 :         case 48:
     187           7 :             poFeature->oStyleProperties["LinetypeScale"] = pszValue;
     188           7 :             break;
     189             : 
     190         202 :         case 370:
     191             :         case 39:
     192         202 :             poFeature->oStyleProperties["LineWeight"] = pszValue;
     193         202 :             break;
     194             : 
     195         689 :         case 5:
     196         689 :             poFeature->SetField("EntityHandle", pszValue);
     197         689 :             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        2595 :         default:
     213        2595 :             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        2595 :             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         568 : void OGRDXFLayer::PrepareFeatureStyle(
     245             :     OGRDXFFeature *const poFeature,
     246             :     OGRDXFFeature *const poBlockFeature /* = NULL */)
     247             : 
     248             : {
     249         568 :     const char *pszStyleString = poFeature->GetStyleString();
     250             : 
     251         568 :     if (pszStyleString && STARTS_WITH_CI(pszStyleString, "BRUSH("))
     252             :     {
     253          51 :         PrepareBrushStyle(poFeature, poBlockFeature);
     254             :     }
     255         517 :     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         453 :         PrepareLineStyle(poFeature, poBlockFeature);
     280             :     }
     281         568 : }
     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         930 : void OGRDXFLayer::PrepareLineStyle(
     304             :     OGRDXFFeature *const poFeature,
     305             :     OGRDXFFeature *const poBlockFeature /* = NULL */)
     306             : 
     307             : {
     308        1860 :     const CPLString osLayer = poFeature->GetFieldAsString("Layer");
     309             : 
     310             :     /* -------------------------------------------------------------------- */
     311             :     /*      Get line weight if available.                                   */
     312             :     /* -------------------------------------------------------------------- */
     313         930 :     double dfWeight = 0.0;
     314        1860 :     CPLString osWeight = "-1";
     315             : 
     316         930 :     if (poFeature->oStyleProperties.count("LineWeight") > 0)
     317         268 :         osWeight = poFeature->oStyleProperties["LineWeight"];
     318             : 
     319             :     // Use ByBlock lineweight?
     320         930 :     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         930 :     if (CPLAtof(osWeight) == -1)
     341             :     {
     342         881 :         osWeight = poDS->LookupLayerProperty(osLayer, "LineWeight");
     343             :     }
     344             : 
     345             :     // Will be zero in the case of an invalid value
     346         930 :     dfWeight = CPLAtof(osWeight) / 100.0;
     347             : 
     348             :     /* -------------------------------------------------------------------- */
     349             :     /*      Do we have a dash/dot line style?                               */
     350             :     /* -------------------------------------------------------------------- */
     351         930 :     const char *pszLinetype = poFeature->GetFieldAsString("Linetype");
     352             : 
     353             :     // Use ByBlock line style?
     354         930 :     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         930 :     if (pszLinetype && EQUAL(pszLinetype, ""))
     366             :     {
     367         655 :         pszLinetype = poDS->LookupLayerProperty(osLayer, "Linetype");
     368             :     }
     369             : 
     370        1860 :     const std::vector<double> oLineType = poDS->LookupLineType(pszLinetype);
     371             : 
     372             :     // Linetype scale is not inherited from the block feature
     373         930 :     double dfLineTypeScale = CPLAtof(poDS->GetVariable("$LTSCALE", "1.0"));
     374         930 :     if (poFeature->oStyleProperties.count("LinetypeScale") > 0)
     375          16 :         dfLineTypeScale *=
     376          16 :             CPLAtof(poFeature->oStyleProperties["LinetypeScale"]);
     377             : 
     378        1860 :     CPLString osPattern;
     379        1020 :     for (std::vector<double>::const_iterator oIt = oLineType.begin();
     380        1020 :          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         930 :     if (osPattern.length() > 0)
     388          45 :         osPattern.erase(osPattern.end() - 1);
     389             : 
     390             :     /* -------------------------------------------------------------------- */
     391             :     /*      Format the style string.                                        */
     392             :     /* -------------------------------------------------------------------- */
     393             : 
     394        1860 :     CPLString osStyle = "PEN(c:";
     395         930 :     osStyle += poFeature->GetColor(poDS, poBlockFeature);
     396             : 
     397         930 :     if (dfWeight > 0.0)
     398             :     {
     399             :         char szBuffer[64];
     400          13 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight);
     401          13 :         osStyle += CPLString().Printf(",w:%sg", szBuffer);
     402             :     }
     403             : 
     404         930 :     if (osPattern != "")
     405             :     {
     406          45 :         osStyle += ",p:\"";
     407          45 :         osStyle += osPattern;
     408          45 :         osStyle += "\"";
     409             :     }
     410             : 
     411         930 :     osStyle += ")";
     412             : 
     413         930 :     poFeature->SetStyleString(osStyle);
     414         930 : }
     415             : 
     416             : /************************************************************************/
     417             : /*                             TextRecode()                             */
     418             : /************************************************************************/
     419             : 
     420        1463 : CPLString OGRDXFLayer::TextRecode(const char *pszInput)
     421             : 
     422             : {
     423        2926 :     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          33 : OGRDXFFeature *OGRDXFLayer::TranslatePOINT()
     906             : 
     907             : {
     908             :     char szLineBuf[257];
     909          33 :     int nCode = 0;
     910          66 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
     911          33 :     double dfX = 0.0;
     912          33 :     double dfY = 0.0;
     913          33 :     double dfZ = 0.0;
     914          33 :     bool bHaveZ = false;
     915             : 
     916         349 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     917             :     {
     918         316 :         switch (nCode)
     919             :         {
     920          33 :             case 10:
     921          33 :                 dfX = CPLAtof(szLineBuf);
     922          33 :                 break;
     923             : 
     924          33 :             case 20:
     925          33 :                 dfY = CPLAtof(szLineBuf);
     926          33 :                 break;
     927             : 
     928          31 :             case 30:
     929          31 :                 dfZ = CPLAtof(szLineBuf);
     930          31 :                 bHaveZ = true;
     931          31 :                 break;
     932             : 
     933         219 :             default:
     934         219 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
     935         219 :                 break;
     936             :         }
     937             :     }
     938          33 :     if (nCode < 0)
     939             :     {
     940           0 :         DXF_LAYER_READER_ERROR();
     941           0 :         return nullptr;
     942             :     }
     943             : 
     944          33 :     if (nCode == 0)
     945          33 :         poDS->UnreadValue();
     946             : 
     947          33 :     OGRPoint *poGeom = nullptr;
     948          33 :     if (bHaveZ)
     949          31 :         poGeom = new OGRPoint(dfX, dfY, dfZ);
     950             :     else
     951           2 :         poGeom = new OGRPoint(dfX, dfY);
     952             : 
     953          33 :     poFeature->SetGeometryDirectly(poGeom);
     954             : 
     955             :     // Set style pen color
     956          33 :     PrepareLineStyle(poFeature.get());
     957             : 
     958          33 :     return poFeature.release();
     959             : }
     960             : 
     961             : /************************************************************************/
     962             : /*                           TranslateLINE()                            */
     963             : /************************************************************************/
     964             : 
     965         163 : OGRDXFFeature *OGRDXFLayer::TranslateLINE()
     966             : 
     967             : {
     968             :     char szLineBuf[257];
     969         163 :     int nCode = 0;
     970         326 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
     971         163 :     double dfX1 = 0.0;
     972         163 :     double dfY1 = 0.0;
     973         163 :     double dfZ1 = 0.0;
     974         163 :     double dfX2 = 0.0;
     975         163 :     double dfY2 = 0.0;
     976         163 :     double dfZ2 = 0.0;
     977         163 :     bool bHaveZ = false;
     978             : 
     979             :     /* -------------------------------------------------------------------- */
     980             :     /*      Process values.                                                 */
     981             :     /* -------------------------------------------------------------------- */
     982        2209 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
     983             :     {
     984        2046 :         switch (nCode)
     985             :         {
     986         163 :             case 10:
     987         163 :                 dfX1 = CPLAtof(szLineBuf);
     988         163 :                 break;
     989             : 
     990         163 :             case 11:
     991         163 :                 dfX2 = CPLAtof(szLineBuf);
     992         163 :                 break;
     993             : 
     994         163 :             case 20:
     995         163 :                 dfY1 = CPLAtof(szLineBuf);
     996         163 :                 break;
     997             : 
     998         163 :             case 21:
     999         163 :                 dfY2 = CPLAtof(szLineBuf);
    1000         163 :                 break;
    1001             : 
    1002         163 :             case 30:
    1003         163 :                 dfZ1 = CPLAtof(szLineBuf);
    1004         163 :                 bHaveZ = true;
    1005         163 :                 break;
    1006             : 
    1007         163 :             case 31:
    1008         163 :                 dfZ2 = CPLAtof(szLineBuf);
    1009         163 :                 bHaveZ = true;
    1010         163 :                 break;
    1011             : 
    1012        1068 :             default:
    1013        1068 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1014        1068 :                 break;
    1015             :         }
    1016             :     }
    1017         163 :     if (nCode < 0)
    1018             :     {
    1019           0 :         DXF_LAYER_READER_ERROR();
    1020           0 :         return nullptr;
    1021             :     }
    1022             : 
    1023         163 :     if (nCode == 0)
    1024         163 :         poDS->UnreadValue();
    1025             : 
    1026             :     /* -------------------------------------------------------------------- */
    1027             :     /*      Create geometry                                                 */
    1028             :     /* -------------------------------------------------------------------- */
    1029         326 :     auto poLS = std::make_unique<OGRLineString>();
    1030         163 :     if (bHaveZ)
    1031             :     {
    1032         163 :         poLS->addPoint(dfX1, dfY1, dfZ1);
    1033         163 :         poLS->addPoint(dfX2, dfY2, dfZ2);
    1034             :     }
    1035             :     else
    1036             :     {
    1037           0 :         poLS->addPoint(dfX1, dfY1);
    1038           0 :         poLS->addPoint(dfX2, dfY2);
    1039             :     }
    1040             : 
    1041         163 :     poFeature->SetGeometryDirectly(poLS.release());
    1042             : 
    1043         163 :     PrepareLineStyle(poFeature.get());
    1044             : 
    1045         163 :     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          18 : OGRDXFFeature *OGRDXFLayer::TranslatePOLYLINE()
    1192             : 
    1193             : {
    1194             :     char szLineBuf[257];
    1195          18 :     int nCode = 0;
    1196          18 :     int nPolylineFlag = 0;
    1197          36 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1198             : 
    1199             :     /* -------------------------------------------------------------------- */
    1200             :     /*      Collect information from the POLYLINE object itself.            */
    1201             :     /* -------------------------------------------------------------------- */
    1202         208 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1203             :     {
    1204         190 :         switch (nCode)
    1205             :         {
    1206          18 :             case 70:
    1207          18 :                 nPolylineFlag = atoi(szLineBuf);
    1208          18 :                 break;
    1209             : 
    1210         172 :             default:
    1211         172 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1212         172 :                 break;
    1213             :         }
    1214             :     }
    1215          18 :     if (nCode < 0)
    1216             :     {
    1217           0 :         DXF_LAYER_READER_ERROR();
    1218           0 :         return nullptr;
    1219             :     }
    1220             : 
    1221          18 :     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          18 :     double dfX = 0.0;
    1231          18 :     double dfY = 0.0;
    1232          18 :     double dfZ = 0.0;
    1233          18 :     double dfBulge = 0.0;
    1234          18 :     int nVertexFlag = 0;
    1235          36 :     DXFSmoothPolyline smoothPolyline;
    1236          18 :     unsigned int vertexIndex71 = 0;
    1237          18 :     unsigned int vertexIndex72 = 0;
    1238          18 :     unsigned int vertexIndex73 = 0;
    1239          18 :     unsigned int vertexIndex74 = 0;
    1240          36 :     std::vector<OGRPoint> aoPoints;
    1241          36 :     auto poPS = std::make_unique<OGRPolyhedralSurface>();
    1242             : 
    1243          18 :     smoothPolyline.setCoordinateDimension(2);
    1244             : 
    1245          93 :     while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
    1246             :     {
    1247             :         // Eat non-vertex objects.
    1248          75 :         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         874 :         while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1264             :         {
    1265         799 :             switch (nCode)
    1266             :             {
    1267          75 :                 case 10:
    1268          75 :                     dfX = CPLAtof(szLineBuf);
    1269          75 :                     break;
    1270             : 
    1271          75 :                 case 20:
    1272          75 :                     dfY = CPLAtof(szLineBuf);
    1273          75 :                     break;
    1274             : 
    1275          75 :                 case 30:
    1276          75 :                     dfZ = CPLAtof(szLineBuf);
    1277          75 :                     smoothPolyline.setCoordinateDimension(3);
    1278          75 :                     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         482 :                 default:
    1307         482 :                     break;
    1308             :             }
    1309             :         }
    1310             : 
    1311          75 :         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          75 :         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          75 :         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          75 :         if ((nVertexFlag & 16) == 0)
    1391          75 :             smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
    1392          75 :         dfBulge = 0.0;
    1393             :     }
    1394             : 
    1395          18 :     if (smoothPolyline.IsEmpty())
    1396             :     {
    1397           0 :         return nullptr;
    1398             :     }
    1399             : 
    1400          18 :     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          17 :     const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
    1411          17 :     if (bIsClosed)
    1412          10 :         smoothPolyline.Close();
    1413             : 
    1414          17 :     const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
    1415             : 
    1416          17 :     smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
    1417          17 :     OGRGeometry *poGeom = smoothPolyline.Tessellate(bAsPolygon);
    1418             : 
    1419          17 :     if ((nPolylineFlag & 8) == 0)
    1420           2 :         poFeature->ApplyOCSTransformer(poGeom);
    1421          17 :     poFeature->SetGeometryDirectly(poGeom);
    1422             : 
    1423          17 :     PrepareLineStyle(poFeature.get());
    1424             : 
    1425          17 :     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          23 : OGRDXFFeature *OGRDXFLayer::TranslateCIRCLE()
    1638             : 
    1639             : {
    1640             :     char szLineBuf[257];
    1641          23 :     int nCode = 0;
    1642          46 :     auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
    1643          23 :     double dfX1 = 0.0;
    1644          23 :     double dfY1 = 0.0;
    1645          23 :     double dfZ1 = 0.0;
    1646          23 :     double dfRadius = 0.0;
    1647          23 :     double dfThickness = 0.0;
    1648          23 :     bool bHaveZ = false;
    1649             : 
    1650             :     /* -------------------------------------------------------------------- */
    1651             :     /*      Process values.                                                 */
    1652             :     /* -------------------------------------------------------------------- */
    1653         236 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    1654             :     {
    1655         213 :         switch (nCode)
    1656             :         {
    1657          23 :             case 10:
    1658          23 :                 dfX1 = CPLAtof(szLineBuf);
    1659          23 :                 break;
    1660             : 
    1661          23 :             case 20:
    1662          23 :                 dfY1 = CPLAtof(szLineBuf);
    1663          23 :                 break;
    1664             : 
    1665          23 :             case 30:
    1666          23 :                 dfZ1 = CPLAtof(szLineBuf);
    1667          23 :                 bHaveZ = true;
    1668          23 :                 break;
    1669             : 
    1670           1 :             case 39:
    1671           1 :                 dfThickness = CPLAtof(szLineBuf);
    1672           1 :                 break;
    1673             : 
    1674          23 :             case 40:
    1675          23 :                 dfRadius = CPLAtof(szLineBuf);
    1676          23 :                 break;
    1677             : 
    1678         120 :             default:
    1679         120 :                 TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
    1680         120 :                 break;
    1681             :         }
    1682             :     }
    1683          23 :     if (nCode < 0)
    1684             :     {
    1685           0 :         DXF_LAYER_READER_ERROR();
    1686           0 :         return nullptr;
    1687             :     }
    1688             : 
    1689          23 :     if (nCode == 0)
    1690          23 :         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          23 :                                                  poDS->InlineBlocks())
    1699          46 :             ->toLineString());
    1700             : 
    1701          23 :     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          23 :     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          22 :         if (!bHaveZ)
    1775           0 :             poCircle->flattenTo2D();
    1776             : 
    1777          22 :         poFeature->ApplyOCSTransformer(poCircle.get());
    1778          22 :         poFeature->SetGeometryDirectly(poCircle.release());
    1779             :     }
    1780             : 
    1781          23 :     PrepareLineStyle(poFeature.get());
    1782             : 
    1783          23 :     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          75 : OGRDXFLayer::SimplifyBlockGeometry(OGRGeometryCollection *poCollection)
    2685             : {
    2686             :     /* -------------------------------------------------------------------- */
    2687             :     /*      If there is only one geometry in the collection, just return    */
    2688             :     /*      it.                                                             */
    2689             :     /* -------------------------------------------------------------------- */
    2690          75 :     if (poCollection->getNumGeometries() == 1)
    2691             :     {
    2692          64 :         OGRGeometry *poReturn = poCollection->getGeometryRef(0);
    2693          64 :         poCollection->removeGeometry(0, FALSE);
    2694          64 :         delete poCollection;
    2695          64 :         return poReturn;
    2696             :     }
    2697             : 
    2698             :     /* -------------------------------------------------------------------- */
    2699             :     /*      Convert to polygon, multipolygon, multilinestring or multipoint */
    2700             :     /* -------------------------------------------------------------------- */
    2701             : 
    2702             :     OGRwkbGeometryType eType =
    2703          11 :         wkbFlatten(poCollection->getGeometryRef(0)->getGeometryType());
    2704             :     int i;
    2705          37 :     for (i = 1; i < poCollection->getNumGeometries(); i++)
    2706             :     {
    2707          27 :         if (wkbFlatten(poCollection->getGeometryRef(i)->getGeometryType()) !=
    2708             :             eType)
    2709             :         {
    2710           1 :             eType = wkbUnknown;
    2711           1 :             break;
    2712             :         }
    2713             :     }
    2714          11 :     if (eType == wkbPoint || eType == wkbLineString)
    2715             :     {
    2716             :         OGRGeometryCollection *poNewColl;
    2717           9 :         if (eType == wkbPoint)
    2718           0 :             poNewColl = new OGRMultiPoint();
    2719             :         else
    2720           9 :             poNewColl = new OGRMultiLineString();
    2721          43 :         while (poCollection->getNumGeometries() > 0)
    2722             :         {
    2723          34 :             OGRGeometry *poGeom = poCollection->getGeometryRef(0);
    2724          34 :             poCollection->removeGeometry(0, FALSE);
    2725          34 :             poNewColl->addGeometryDirectly(poGeom);
    2726             :         }
    2727           9 :         delete poCollection;
    2728           9 :         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             : /*                       InsertBlockReference()                         */
    2760             : /*                                                                      */
    2761             : /*     Returns a point geometry located at the block's insertion        */
    2762             : /*     point.                                                           */
    2763             : /************************************************************************/
    2764             : OGRDXFFeature *
    2765         801 : OGRDXFLayer::InsertBlockReference(const CPLString &osBlockName,
    2766             :                                   const OGRDXFInsertTransformer &oTransformer,
    2767             :                                   OGRDXFFeature *const poFeature)
    2768             : {
    2769             :     // Store the block's properties in the special DXF-specific members
    2770             :     // on the feature object
    2771         801 :     poFeature->bIsBlockReference = true;
    2772         801 :     poFeature->osBlockName = osBlockName;
    2773         801 :     poFeature->dfBlockAngle = oTransformer.dfAngle * 180 / M_PI;
    2774        1602 :     poFeature->oBlockScale = DXFTriple(
    2775         801 :         oTransformer.dfXScale, oTransformer.dfYScale, oTransformer.dfZScale);
    2776        1602 :     poFeature->oOriginalCoords = DXFTriple(
    2777         801 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    2778             : 
    2779             :     // Only if DXF_INLINE_BLOCKS is false should we ever need to expose these
    2780             :     // to the end user as fields.
    2781         801 :     if (poFeature->GetFieldIndex("BlockName") != -1)
    2782             :     {
    2783          13 :         poFeature->SetField("BlockName", poFeature->osBlockName);
    2784          13 :         poFeature->SetField("BlockAngle", poFeature->dfBlockAngle);
    2785          13 :         poFeature->SetField("BlockScale", 3, &(poFeature->oBlockScale.dfX));
    2786          13 :         poFeature->SetField("BlockOCSNormal", 3, &(poFeature->oOCS.dfX));
    2787          13 :         poFeature->SetField("BlockOCSCoords", 3,
    2788          13 :                             &(poFeature->oOriginalCoords.dfX));
    2789             :     }
    2790             : 
    2791             :     // For convenience to the end user, the point geometry will be located
    2792             :     // at the WCS coordinates of the insertion point.
    2793             :     OGRPoint *poInsertionPoint = new OGRPoint(
    2794         801 :         oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
    2795             : 
    2796         801 :     poFeature->ApplyOCSTransformer(poInsertionPoint);
    2797         801 :     poFeature->SetGeometryDirectly(poInsertionPoint);
    2798             : 
    2799         801 :     return poFeature;
    2800             : }
    2801             : 
    2802             : /************************************************************************/
    2803             : /*                         InsertBlockInline()                          */
    2804             : /*                                                                      */
    2805             : /*     Inserts the given block at the location specified by the given   */
    2806             : /*     transformer.  Returns poFeature, or NULL if all features on      */
    2807             : /*     the block have been pushed to the extra feature queue.           */
    2808             : /*     If poFeature is not returned, it is deleted.                     */
    2809             : /*     Throws std::invalid_argument if the requested block              */
    2810             : /*     doesn't exist.                                                   */
    2811             : /*                                                                      */
    2812             : /*     - poFeature: The feature to use as a template. This feature's    */
    2813             : /*       OCS will be applied to the block.                              */
    2814             : /*     - bInlineRecursively: If true, INSERTs within this block         */
    2815             : /*       will be recursively inserted.  Otherwise, they will be         */
    2816             : /*       represented as a point geometry using InsertBlockReference.    */
    2817             : /*     - bMergeGeometry: If true, all features in the block,            */
    2818             : /*       apart from text features, are merged into a                    */
    2819             : /*       GeometryCollection which is returned by the function.          */
    2820             : /************************************************************************/
    2821             : 
    2822        1224 : OGRDXFFeature *OGRDXFLayer::InsertBlockInline(
    2823             :     GUInt32 nInitialErrorCounter, const CPLString &osBlockName,
    2824             :     OGRDXFInsertTransformer oTransformer, OGRDXFFeature *const poFeature,
    2825             :     OGRDXFFeatureQueue &apoExtraFeatures, const bool bInlineRecursively,
    2826             :     const bool bMergeGeometry)
    2827             : {
    2828             :     /* -------------------------------------------------------------------- */
    2829             :     /*      Set up protection against excessive recursion on this layer.    */
    2830             :     /* -------------------------------------------------------------------- */
    2831        1224 :     if (!poDS->PushBlockInsertion(osBlockName))
    2832             :     {
    2833        1003 :         delete poFeature;
    2834        1003 :         return nullptr;
    2835             :     }
    2836             : 
    2837             :     /* -------------------------------------------------------------------- */
    2838             :     /*      Transform the insertion point from OCS into                     */
    2839             :     /*      world coordinates.                                              */
    2840             :     /* -------------------------------------------------------------------- */
    2841             :     OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
    2842         442 :                              oTransformer.dfZOffset);
    2843             : 
    2844         221 :     poFeature->ApplyOCSTransformer(&oInsertionPoint);
    2845             : 
    2846         221 :     oTransformer.dfXOffset = oInsertionPoint.getX();
    2847         221 :     oTransformer.dfYOffset = oInsertionPoint.getY();
    2848         221 :     oTransformer.dfZOffset = oInsertionPoint.getZ();
    2849             : 
    2850             :     /* -------------------------------------------------------------------- */
    2851             :     /*      Lookup the block.                                               */
    2852             :     /* -------------------------------------------------------------------- */
    2853         221 :     DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
    2854             : 
    2855         221 :     if (poBlock == nullptr)
    2856             :     {
    2857             :         // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
    2858           9 :         poDS->PopBlockInsertion();
    2859           9 :         throw std::invalid_argument("osBlockName");
    2860             :     }
    2861             : 
    2862             :     /* -------------------------------------------------------------------- */
    2863             :     /*      If we have complete features associated with the block, push    */
    2864             :     /*      them on the pending feature stack copying over key override     */
    2865             :     /*      information.                                                    */
    2866             :     /*                                                                      */
    2867             :     /*      If bMergeGeometry is true, we merge the features                */
    2868             :     /*      (except text) into a single GeometryCollection.                 */
    2869             :     /* -------------------------------------------------------------------- */
    2870         212 :     OGRGeometryCollection *poMergedGeometry = nullptr;
    2871         212 :     if (bMergeGeometry)
    2872          89 :         poMergedGeometry = new OGRGeometryCollection();
    2873             : 
    2874         424 :     OGRDXFFeatureQueue apoInnerExtraFeatures;
    2875             : 
    2876        1823 :     for (unsigned int iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size();
    2877             :          iSubFeat++)
    2878             :     {
    2879             :         OGRDXFFeature *poSubFeature =
    2880        1613 :             poBlock->apoFeatures[iSubFeat]->CloneDXFFeature();
    2881             : 
    2882             :         // If the template feature is in PaperSpace, set this on the
    2883             :         // subfeature too
    2884        1613 :         if (poFeature->GetFieldAsInteger("PaperSpace"))
    2885          48 :             poSubFeature->SetField("PaperSpace", 1);
    2886             : 
    2887             :         // Does this feature represent a block reference? If so,
    2888             :         // insert that block
    2889        1613 :         if (bInlineRecursively && poSubFeature->IsBlockReference())
    2890             :         {
    2891             :             // Unpack the transformation data stored in fields of this
    2892             :             // feature
    2893           0 :             OGRDXFInsertTransformer oInnerTransformer;
    2894        1073 :             oInnerTransformer.dfXOffset = poSubFeature->oOriginalCoords.dfX;
    2895        1073 :             oInnerTransformer.dfYOffset = poSubFeature->oOriginalCoords.dfY;
    2896        1073 :             oInnerTransformer.dfZOffset = poSubFeature->oOriginalCoords.dfZ;
    2897        1073 :             oInnerTransformer.dfAngle = poSubFeature->dfBlockAngle * M_PI / 180;
    2898        1073 :             oInnerTransformer.dfXScale = poSubFeature->oBlockScale.dfX;
    2899        1073 :             oInnerTransformer.dfYScale = poSubFeature->oBlockScale.dfY;
    2900        1073 :             oInnerTransformer.dfZScale = poSubFeature->oBlockScale.dfZ;
    2901             : 
    2902        1073 :             poSubFeature->bIsBlockReference = false;
    2903             : 
    2904             :             // Keep a reference to the attributes that need to be inserted
    2905             :             std::vector<std::unique_ptr<OGRDXFFeature>> apoInnerAttribFeatures =
    2906        1073 :                 std::move(poSubFeature->apoAttribFeatures);
    2907             : 
    2908             :             // Insert this block recursively
    2909             :             try
    2910             :             {
    2911        2146 :                 poSubFeature = InsertBlockInline(
    2912        1073 :                     nInitialErrorCounter, poSubFeature->osBlockName,
    2913        1073 :                     std::move(oInnerTransformer), poSubFeature,
    2914             :                     apoInnerExtraFeatures, true, bMergeGeometry);
    2915             :             }
    2916           0 :             catch (const std::invalid_argument &)
    2917             :             {
    2918             :                 // Block doesn't exist. Skip it and keep going
    2919           0 :                 delete poSubFeature;
    2920           0 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    2921             :                 {
    2922           0 :                     break;
    2923             :                 }
    2924           0 :                 continue;
    2925             :             }
    2926             : 
    2927        1073 :             if (!poSubFeature)
    2928             :             {
    2929        1067 :                 if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
    2930             :                 {
    2931           2 :                     break;
    2932             :                 }
    2933             : 
    2934             :                 // Append the attribute features to the pending feature stack
    2935        1067 :                 for (auto &poAttribFeature : apoInnerAttribFeatures)
    2936             :                 {
    2937             :                     // Clear the attribute tag so the feature doesn't get mistaken
    2938             :                     // for an ATTDEF and skipped
    2939           2 :                     poAttribFeature->osAttributeTag = "";
    2940             : 
    2941           2 :                     apoInnerExtraFeatures.push(poAttribFeature.release());
    2942             :                 }
    2943             : 
    2944        1065 :                 if (apoInnerExtraFeatures.empty())
    2945             :                 {
    2946             :                     // Block is empty and has no attributes. Skip it and keep going
    2947        1002 :                     continue;
    2948             :                 }
    2949             :                 else
    2950             :                 {
    2951             :                     // Load up the first extra feature ready for
    2952             :                     // transformation
    2953          63 :                     poSubFeature = apoInnerExtraFeatures.front();
    2954          63 :                     apoInnerExtraFeatures.pop();
    2955             :                 }
    2956             :             }
    2957             :         }
    2958             : 
    2959             :         // Go through the current feature and any extra features generated
    2960             :         // by the recursive insert, and apply transformations
    2961             :         while (true)
    2962             :         {
    2963         696 :             OGRGeometry *poSubFeatGeom = poSubFeature->GetGeometryRef();
    2964         696 :             if (poSubFeatGeom != nullptr)
    2965             :             {
    2966             :                 // Rotation and scaling first
    2967             :                 OGRDXFInsertTransformer oInnerTrans =
    2968        1386 :                     oTransformer.GetRotateScaleTransformer();
    2969         693 :                 poSubFeatGeom->transform(&oInnerTrans);
    2970             : 
    2971             :                 // Then the OCS to WCS transformation
    2972         693 :                 poFeature->ApplyOCSTransformer(poSubFeatGeom);
    2973             : 
    2974             :                 // Offset translation last
    2975         693 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    2976         693 :                 poSubFeatGeom->transform(&oInnerTrans);
    2977             :             }
    2978             :             // Transform the specially-stored data for ASM entities
    2979           3 :             else if (poSubFeature->poASMTransform)
    2980             :             {
    2981             :                 // Rotation and scaling first
    2982             :                 OGRDXFInsertTransformer oInnerTrans =
    2983           6 :                     oTransformer.GetRotateScaleTransformer();
    2984           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    2985             : 
    2986             :                 // Then the OCS to WCS transformation
    2987           3 :                 poFeature->ApplyOCSTransformer(
    2988             :                     poSubFeature->poASMTransform.get());
    2989             : 
    2990             :                 // Offset translation last
    2991           3 :                 oInnerTrans = oTransformer.GetOffsetTransformer();
    2992           3 :                 poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
    2993             : 
    2994           3 :                 poSubFeature->poASMTransform->SetField(poSubFeature,
    2995             :                                                        "ASMTransform");
    2996             :             }
    2997             : 
    2998             :             // If we are merging features, and this is not text or a block
    2999             :             // reference, merge it into the GeometryCollection
    3000         876 :             if (bMergeGeometry &&
    3001         180 :                 (poSubFeature->GetStyleString() == nullptr ||
    3002         163 :                  strstr(poSubFeature->GetStyleString(), "LABEL") == nullptr) &&
    3003         981 :                 !poSubFeature->IsBlockReference() &&
    3004         105 :                 poSubFeature->GetGeometryRef())
    3005             :             {
    3006         102 :                 poMergedGeometry->addGeometryDirectly(
    3007         102 :                     poSubFeature->StealGeometry());
    3008         102 :                 delete poSubFeature;
    3009             :             }
    3010             :             // Import all other features, except ATTDEFs when inlining
    3011             :             // recursively
    3012         594 :             else if (!bInlineRecursively || poSubFeature->osAttributeTag == "")
    3013             :             {
    3014             :                 // If the subfeature is on layer 0, this is a special case: the
    3015             :                 // subfeature should take on the style properties of the layer
    3016             :                 // the block is being inserted onto.
    3017             :                 // But don't do this if we are inserting onto a Blocks layer
    3018             :                 // (that is, the owning feature has no layer).
    3019         773 :                 if (EQUAL(poSubFeature->GetFieldAsString("Layer"), "0") &&
    3020         211 :                     !EQUAL(poFeature->GetFieldAsString("Layer"), ""))
    3021             :                 {
    3022         180 :                     poSubFeature->SetField(
    3023             :                         "Layer", poFeature->GetFieldAsString("Layer"));
    3024             :                 }
    3025             : 
    3026             :                 // Update the style string to replace ByBlock and ByLayer
    3027             :                 // values.
    3028         562 :                 PrepareFeatureStyle(poSubFeature, poFeature);
    3029             : 
    3030         562 :                 ACAdjustText(oTransformer.dfAngle * 180 / M_PI,
    3031             :                              oTransformer.dfXScale, oTransformer.dfYScale,
    3032             :                              poSubFeature);
    3033             : 
    3034         562 :                 if (!EQUAL(poFeature->GetFieldAsString("EntityHandle"), ""))
    3035             :                 {
    3036         525 :                     poSubFeature->SetField(
    3037             :                         "EntityHandle",
    3038             :                         poFeature->GetFieldAsString("EntityHandle"));
    3039             :                 }
    3040             : 
    3041         562 :                 apoExtraFeatures.push(poSubFeature);
    3042             :             }
    3043             :             else
    3044             :             {
    3045          32 :                 delete poSubFeature;
    3046             :             }
    3047             : 
    3048         696 :             if (apoInnerExtraFeatures.empty())
    3049             :             {
    3050         609 :                 break;
    3051             :             }
    3052             :             else
    3053             :             {
    3054          87 :                 poSubFeature = apoInnerExtraFeatures.front();
    3055          87 :                 apoInnerExtraFeatures.pop();
    3056             :             }
    3057          87 :         }
    3058             :     }
    3059             : 
    3060         214 :     while (!apoInnerExtraFeatures.empty())
    3061             :     {
    3062           2 :         auto poFeatureToDelete = apoInnerExtraFeatures.front();
    3063           2 :         apoInnerExtraFeatures.pop();
    3064           2 :         delete poFeatureToDelete;
    3065             :     }
    3066             : 
    3067         212 :     poDS->PopBlockInsertion();
    3068             : 
    3069             :     /* -------------------------------------------------------------------- */
    3070             :     /*      Return the merged geometry if applicable.  Otherwise            */
    3071             :     /*      return NULL and let the machinery find the rest of the          */
    3072             :     /*      features in the pending feature stack.                          */
    3073             :     /* -------------------------------------------------------------------- */
    3074         212 :     if (bMergeGeometry)
    3075             :     {
    3076          89 :         if (poMergedGeometry->getNumGeometries() == 0)
    3077             :         {
    3078          14 :             delete poMergedGeometry;
    3079             :         }
    3080             :         else
    3081             :         {
    3082          75 :             poFeature->SetGeometryDirectly(
    3083             :                 SimplifyBlockGeometry(poMergedGeometry));
    3084             : 
    3085          75 :             PrepareLineStyle(poFeature);
    3086          75 :             return poFeature;
    3087             :         }
    3088             :     }
    3089             : 
    3090         137 :     delete poFeature;
    3091         137 :     return nullptr;
    3092             : }
    3093             : 
    3094             : /************************************************************************/
    3095             : /*                          TranslateINSERT()                           */
    3096             : /************************************************************************/
    3097             : 
    3098         130 : bool OGRDXFLayer::TranslateINSERT()
    3099             : 
    3100             : {
    3101             :     char szLineBuf[257];
    3102         130 :     int nCode = 0;
    3103             : 
    3104         130 :     m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
    3105         130 :     m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
    3106         130 :     m_oInsertState.m_osBlockName.clear();
    3107         130 :     m_oInsertState.m_nColumnCount = 1;
    3108         130 :     m_oInsertState.m_nRowCount = 1;
    3109         130 :     m_oInsertState.m_iCurCol = 0;
    3110         130 :     m_oInsertState.m_iCurRow = 0;
    3111         130 :     m_oInsertState.m_dfColumnSpacing = 0.0;
    3112         130 :     m_oInsertState.m_dfRowSpacing = 0.0;
    3113             : 
    3114         130 :     bool bHasAttribs = false;
    3115         130 :     m_oInsertState.m_apoAttribs.clear();
    3116         130 :     m_oInsertState.m_aosAttribs.Clear();
    3117             : 
    3118             :     /* -------------------------------------------------------------------- */
    3119             :     /*      Process values.                                                 */
    3120             :     /* -------------------------------------------------------------------- */
    3121        1368 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3122             :     {
    3123        1238 :         switch (nCode)
    3124             :         {
    3125         128 :             case 10:
    3126         128 :                 m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
    3127         128 :                 break;
    3128             : 
    3129         128 :             case 20:
    3130         128 :                 m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
    3131         128 :                 break;
    3132             : 
    3133         121 :             case 30:
    3134         121 :                 m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
    3135         121 :                 break;
    3136             : 
    3137          28 :             case 41:
    3138          28 :                 m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
    3139          28 :                 break;
    3140             : 
    3141          31 :             case 42:
    3142          31 :                 m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
    3143          31 :                 break;
    3144             : 
    3145          16 :             case 43:
    3146          16 :                 m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
    3147          16 :                 break;
    3148             : 
    3149           5 :             case 44:
    3150           5 :                 m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
    3151           5 :                 break;
    3152             : 
    3153           5 :             case 45:
    3154           5 :                 m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
    3155           5 :                 break;
    3156             : 
    3157          23 :             case 50:
    3158             :                 // We want to transform this to radians.
    3159             :                 // It is apparently always in degrees regardless of $AUNITS
    3160          23 :                 m_oInsertState.m_oTransformer.dfAngle =
    3161          23 :                     CPLAtof(szLineBuf) * M_PI / 180.0;
    3162          23 :                 break;
    3163             : 
    3164          13 :             case 66:
    3165          13 :                 bHasAttribs = atoi(szLineBuf) == 1;
    3166          13 :                 break;
    3167             : 
    3168           1 :             case 70:
    3169           1 :                 m_oInsertState.m_nColumnCount = atoi(szLineBuf);
    3170           1 :                 if (m_oInsertState.m_nColumnCount <= 0)
    3171             :                 {
    3172           0 :                     DXF_LAYER_READER_ERROR();
    3173           0 :                     m_oInsertState.m_nRowCount = 0;
    3174           0 :                     m_oInsertState.m_nColumnCount = 0;
    3175           0 :                     return false;
    3176             :                 }
    3177           1 :                 break;
    3178             : 
    3179           3 :             case 71:
    3180           3 :                 m_oInsertState.m_nRowCount = atoi(szLineBuf);
    3181           3 :                 if (m_oInsertState.m_nRowCount <= 0)
    3182             :                 {
    3183           0 :                     DXF_LAYER_READER_ERROR();
    3184           0 :                     m_oInsertState.m_nRowCount = 0;
    3185           0 :                     m_oInsertState.m_nColumnCount = 0;
    3186           0 :                     return false;
    3187             :                 }
    3188           3 :                 break;
    3189             : 
    3190         128 :             case 2:
    3191         128 :                 m_oInsertState.m_osBlockName = szLineBuf;
    3192         128 :                 break;
    3193             : 
    3194         608 :             default:
    3195         608 :                 TranslateGenericProperty(
    3196             :                     m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
    3197         608 :                 break;
    3198             :         }
    3199             :     }
    3200         130 :     if (nCode < 0)
    3201             :     {
    3202           0 :         DXF_LAYER_READER_ERROR();
    3203           0 :         m_oInsertState.m_nRowCount = 0;
    3204           0 :         m_oInsertState.m_nColumnCount = 0;
    3205           0 :         return false;
    3206             :     }
    3207             : 
    3208             :     /* -------------------------------------------------------------------- */
    3209             :     /*      Process any attribute entities.                                 */
    3210             :     /* -------------------------------------------------------------------- */
    3211             : 
    3212         130 :     if (bHasAttribs)
    3213             :     {
    3214          40 :         while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
    3215             :         {
    3216          27 :             if (!EQUAL(szLineBuf, "ATTRIB"))
    3217             :             {
    3218           0 :                 DXF_LAYER_READER_ERROR();
    3219           0 :                 m_oInsertState.m_nRowCount = 0;
    3220           0 :                 m_oInsertState.m_nColumnCount = 0;
    3221           0 :                 return false;
    3222             :             }
    3223             : 
    3224             :             auto poAttribFeature =
    3225          27 :                 std::unique_ptr<OGRDXFFeature>(TranslateTEXT(true));
    3226             : 
    3227          27 :             if (poAttribFeature && poAttribFeature->osAttributeTag != "")
    3228             :             {
    3229             :                 m_oInsertState.m_apoAttribs.emplace_back(
    3230          27 :                     std::move(poAttribFeature));
    3231             :             }
    3232             : 
    3233          27 :             nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
    3234             :         }
    3235             :     }
    3236         117 :     else if (nCode == 0)
    3237             :     {
    3238         117 :         poDS->UnreadValue();
    3239             :     }
    3240             : 
    3241             :     /* -------------------------------------------------------------------- */
    3242             :     /*      Prepare a string list of the attributes and their text values   */
    3243             :     /*      as space-separated entries, to be stored in the                 */
    3244             :     /*      BlockAttributes field if we are not inlining blocks.            */
    3245             :     /* -------------------------------------------------------------------- */
    3246             : 
    3247         134 :     if (!poDS->InlineBlocks() && bHasAttribs &&
    3248           4 :         poFeatureDefn->GetFieldIndex("BlockAttributes") != -1)
    3249             :     {
    3250          10 :         for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3251             :         {
    3252          14 :             CPLString osAttribString = poAttr->osAttributeTag;
    3253           7 :             osAttribString += " ";
    3254           7 :             osAttribString += poAttr->GetFieldAsString("Text");
    3255             : 
    3256           7 :             m_oInsertState.m_aosAttribs.AddString(osAttribString);
    3257             :         }
    3258             :     }
    3259             : 
    3260         130 :     return true;
    3261             : }
    3262             : 
    3263             : /************************************************************************/
    3264             : /*                       GenerateINSERTFeatures()                       */
    3265             : /************************************************************************/
    3266             : 
    3267         897 : bool OGRDXFLayer::GenerateINSERTFeatures()
    3268             : {
    3269             :     OGRDXFFeature *poFeature =
    3270         897 :         m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
    3271             : 
    3272         897 :     const double dfExtraXOffset =
    3273         897 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3274         897 :             cos(m_oInsertState.m_oTransformer.dfAngle) +
    3275         897 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3276         897 :             -sin(m_oInsertState.m_oTransformer.dfAngle);
    3277         897 :     const double dfExtraYOffset =
    3278         897 :         m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
    3279         897 :             sin(m_oInsertState.m_oTransformer.dfAngle) +
    3280         897 :         m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
    3281         897 :             cos(m_oInsertState.m_oTransformer.dfAngle);
    3282             : 
    3283        1794 :     OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
    3284         897 :     oTransformer.dfXOffset += dfExtraXOffset;
    3285         897 :     oTransformer.dfYOffset += dfExtraYOffset;
    3286             : 
    3287             :     // If we are not inlining blocks, just insert a point that refers
    3288             :     // to this block
    3289         897 :     if (!poDS->InlineBlocks())
    3290             :     {
    3291         801 :         poFeature = InsertBlockReference(m_oInsertState.m_osBlockName,
    3292             :                                          oTransformer, poFeature);
    3293             : 
    3294         801 :         auto papszAttribs = m_oInsertState.m_aosAttribs.List();
    3295         801 :         if (papszAttribs)
    3296           3 :             poFeature->SetField("BlockAttributes", papszAttribs);
    3297             : 
    3298         801 :         poFeature->apoAttribFeatures = std::move(m_oInsertState.m_apoAttribs);
    3299             : 
    3300         801 :         apoPendingFeatures.push(poFeature);
    3301             :     }
    3302             :     // Otherwise, try inlining the contents of this block
    3303             :     else
    3304             :     {
    3305          96 :         OGRDXFFeatureQueue apoExtraFeatures;
    3306             :         try
    3307             :         {
    3308         192 :             poFeature = InsertBlockInline(
    3309          96 :                 CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
    3310          96 :                 std::move(oTransformer), poFeature, apoExtraFeatures, true,
    3311          96 :                 poDS->ShouldMergeBlockGeometries());
    3312             :         }
    3313           0 :         catch (const std::invalid_argument &)
    3314             :         {
    3315             :             // Block doesn't exist
    3316           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
    3317             :                      m_oInsertState.m_osBlockName.c_str());
    3318           0 :             delete poFeature;
    3319           0 :             return false;
    3320             :         }
    3321             : 
    3322          96 :         if (poFeature)
    3323          52 :             apoPendingFeatures.push(poFeature);
    3324             : 
    3325         303 :         while (!apoExtraFeatures.empty())
    3326             :         {
    3327         207 :             apoPendingFeatures.push(apoExtraFeatures.front());
    3328         207 :             apoExtraFeatures.pop();
    3329             :         }
    3330             : 
    3331             :         // Append the attribute features to the pending feature stack
    3332          96 :         if (!m_oInsertState.m_apoAttribs.empty())
    3333             :         {
    3334          16 :             OGRDXFInsertTransformer oAttribTransformer;
    3335          16 :             oAttribTransformer.dfXOffset = dfExtraXOffset;
    3336          16 :             oAttribTransformer.dfYOffset = dfExtraYOffset;
    3337             : 
    3338          42 :             for (const auto &poAttr : m_oInsertState.m_apoAttribs)
    3339             :             {
    3340          26 :                 OGRDXFFeature *poAttribFeature = poAttr->CloneDXFFeature();
    3341             : 
    3342          26 :                 if (poAttribFeature->GetGeometryRef())
    3343             :                 {
    3344          26 :                     poAttribFeature->GetGeometryRef()->transform(
    3345          26 :                         &oAttribTransformer);
    3346             :                 }
    3347             : 
    3348          26 :                 apoPendingFeatures.push(poAttribFeature);
    3349             :             }
    3350             :         }
    3351             :     }
    3352         897 :     return true;
    3353             : }
    3354             : 
    3355             : /************************************************************************/
    3356             : /*                      GetNextUnfilteredFeature()                      */
    3357             : /************************************************************************/
    3358             : 
    3359        1905 : OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
    3360             : 
    3361             : {
    3362        1905 :     OGRDXFFeature *poFeature = nullptr;
    3363        3653 :     while (poFeature == nullptr)
    3364             :     {
    3365             :         /* --------------------------------------------------------------------
    3366             :          */
    3367             :         /*      If we have pending features, return one of them. */
    3368             :         /* --------------------------------------------------------------------
    3369             :          */
    3370        3121 :         if (!apoPendingFeatures.empty())
    3371             :         {
    3372        1283 :             poFeature = apoPendingFeatures.front();
    3373        1283 :             apoPendingFeatures.pop();
    3374             : 
    3375        1283 :             poFeature->SetFID(iNextFID++);
    3376        1373 :             return poFeature;
    3377             :         }
    3378             : 
    3379             :         /* --------------------------------------------------------------------
    3380             :          */
    3381             :         /*      Emit INSERT features. */
    3382             :         /* --------------------------------------------------------------------
    3383             :          */
    3384        1838 :         if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
    3385             :         {
    3386         998 :             if (m_oInsertState.m_iCurCol == m_oInsertState.m_nColumnCount)
    3387             :             {
    3388         862 :                 m_oInsertState.m_iCurRow++;
    3389         862 :                 m_oInsertState.m_iCurCol = 0;
    3390         862 :                 if (m_oInsertState.m_iCurRow == m_oInsertState.m_nRowCount)
    3391             :                 {
    3392         101 :                     m_oInsertState.m_nRowCount = 0;
    3393         101 :                     m_oInsertState.m_nColumnCount = 0;
    3394         998 :                     continue;
    3395             :                 }
    3396             :             }
    3397         897 :             if (GenerateINSERTFeatures())
    3398             :             {
    3399         897 :                 m_oInsertState.m_iCurCol++;
    3400             :             }
    3401             :             else
    3402             :             {
    3403           0 :                 m_oInsertState.m_nRowCount = 0;
    3404           0 :                 m_oInsertState.m_nColumnCount = 0;
    3405             :             }
    3406         897 :             continue;
    3407             :         }
    3408             : 
    3409             :         // read ahead to an entity.
    3410             :         char szLineBuf[257];
    3411         840 :         int nCode = 0;
    3412         960 :         while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
    3413             :         {
    3414             :         }
    3415         840 :         if (nCode < 0)
    3416             :         {
    3417           5 :             DXF_LAYER_READER_ERROR();
    3418           5 :             return nullptr;
    3419             :         }
    3420             : 
    3421         835 :         if (EQUAL(szLineBuf, "ENDSEC"))
    3422             :         {
    3423             :             // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
    3424          13 :             poDS->UnreadValue();
    3425          13 :             return nullptr;
    3426             :         }
    3427             : 
    3428         822 :         if (EQUAL(szLineBuf, "ENDBLK"))
    3429             :         {
    3430             :             // CPLDebug( "DXF", "Clean end of block at ENDBLK." );
    3431          72 :             poDS->UnreadValue();
    3432          72 :             return nullptr;
    3433             :         }
    3434             : 
    3435             :         /* --------------------------------------------------------------------
    3436             :          */
    3437             :         /*      Handle the entity. */
    3438             :         /* --------------------------------------------------------------------
    3439             :          */
    3440         750 :         if (EQUAL(szLineBuf, "POINT"))
    3441             :         {
    3442          33 :             poFeature = TranslatePOINT();
    3443             :         }
    3444         717 :         else if (EQUAL(szLineBuf, "MTEXT"))
    3445             :         {
    3446          40 :             poFeature = TranslateMTEXT();
    3447             :         }
    3448         677 :         else if (EQUAL(szLineBuf, "TEXT"))
    3449             :         {
    3450          26 :             poFeature = TranslateTEXT(false);
    3451             :         }
    3452         651 :         else if (EQUAL(szLineBuf, "ATTDEF"))
    3453             :         {
    3454          10 :             poFeature = TranslateTEXT(true);
    3455             :         }
    3456         641 :         else if (EQUAL(szLineBuf, "LINE"))
    3457             :         {
    3458         163 :             poFeature = TranslateLINE();
    3459             :         }
    3460         478 :         else if (EQUAL(szLineBuf, "POLYLINE"))
    3461             :         {
    3462          18 :             poFeature = TranslatePOLYLINE();
    3463             :         }
    3464         460 :         else if (EQUAL(szLineBuf, "LWPOLYLINE"))
    3465             :         {
    3466          37 :             poFeature = TranslateLWPOLYLINE();
    3467             :         }
    3468         423 :         else if (EQUAL(szLineBuf, "MLINE"))
    3469             :         {
    3470           3 :             poFeature = TranslateMLINE();
    3471             :         }
    3472         420 :         else if (EQUAL(szLineBuf, "CIRCLE"))
    3473             :         {
    3474          23 :             poFeature = TranslateCIRCLE();
    3475             :         }
    3476         397 :         else if (EQUAL(szLineBuf, "ELLIPSE"))
    3477             :         {
    3478          29 :             poFeature = TranslateELLIPSE();
    3479             :         }
    3480         368 :         else if (EQUAL(szLineBuf, "ARC"))
    3481             :         {
    3482          14 :             poFeature = TranslateARC();
    3483             :         }
    3484         354 :         else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
    3485             :         {
    3486          25 :             poFeature = TranslateSPLINE();
    3487             :         }
    3488         329 :         else if (EQUAL(szLineBuf, "3DFACE"))
    3489             :         {
    3490          10 :             poFeature = Translate3DFACE();
    3491             :         }
    3492         319 :         else if (EQUAL(szLineBuf, "INSERT"))
    3493             :         {
    3494         130 :             if (!TranslateINSERT())
    3495           0 :                 return nullptr;
    3496             :         }
    3497         189 :         else if (EQUAL(szLineBuf, "DIMENSION"))
    3498             :         {
    3499          30 :             poFeature = TranslateDIMENSION();
    3500             :         }
    3501         159 :         else if (EQUAL(szLineBuf, "HATCH"))
    3502             :         {
    3503          28 :             poFeature = TranslateHATCH();
    3504             :         }
    3505         131 :         else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
    3506             :         {
    3507          25 :             poFeature = TranslateSOLID();
    3508             :         }
    3509         106 :         else if (EQUAL(szLineBuf, "LEADER"))
    3510             :         {
    3511          14 :             poFeature = TranslateLEADER();
    3512             :         }
    3513          92 :         else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
    3514             :         {
    3515          18 :             poFeature = TranslateMLEADER();
    3516             :         }
    3517          74 :         else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
    3518          72 :                  EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
    3519             :         {
    3520           2 :             if (poDS->In3DExtensibleMode())
    3521             :             {
    3522           2 :                 poFeature = TranslateASMEntity();
    3523             :             }
    3524           0 :             else if (oIgnoredEntities.count(szLineBuf) == 0)
    3525             :             {
    3526           0 :                 oIgnoredEntities.insert(szLineBuf);
    3527           0 :                 CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
    3528             :                          szLineBuf);
    3529             :             }
    3530             :         }
    3531             :         else
    3532             :         {
    3533          72 :             if (oIgnoredEntities.count(szLineBuf) == 0)
    3534             :             {
    3535          13 :                 oIgnoredEntities.insert(szLineBuf);
    3536          13 :                 CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
    3537             :                          szLineBuf);
    3538             :             }
    3539             :         }
    3540             :     }
    3541             : 
    3542             :     /* -------------------------------------------------------------------- */
    3543             :     /*      Set FID.                                                        */
    3544             :     /* -------------------------------------------------------------------- */
    3545         532 :     poFeature->SetFID(iNextFID++);
    3546         532 :     m_nFeaturesRead++;
    3547             : 
    3548         532 :     return poFeature;
    3549             : }
    3550             : 
    3551             : /************************************************************************/
    3552             : /*                           GetNextFeature()                           */
    3553             : /************************************************************************/
    3554             : 
    3555         824 : OGRFeature *OGRDXFLayer::GetNextFeature()
    3556             : 
    3557             : {
    3558             :     while (true)
    3559             :     {
    3560         824 :         OGRFeature *poFeature = GetNextUnfilteredFeature();
    3561             : 
    3562         824 :         if (poFeature == nullptr)
    3563          15 :             return nullptr;
    3564             : 
    3565        1618 :         if ((m_poFilterGeom == nullptr ||
    3566        1618 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    3567         809 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    3568             :         {
    3569         809 :             return poFeature;
    3570             :         }
    3571             : 
    3572           0 :         delete poFeature;
    3573           0 :     }
    3574             : }
    3575             : 
    3576             : /************************************************************************/
    3577             : /*                           TestCapability()                           */
    3578             : /************************************************************************/
    3579             : 
    3580           0 : int OGRDXFLayer::TestCapability(const char *pszCap)
    3581             : 
    3582             : {
    3583           0 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    3584           0 :         return true;
    3585           0 :     else if (EQUAL(pszCap, OLCZGeometries))
    3586           0 :         return true;
    3587           0 :     return false;
    3588             : }
    3589             : 
    3590             : /************************************************************************/
    3591             : /*                             GetDataset()                             */
    3592             : /************************************************************************/
    3593             : 
    3594           1 : GDALDataset *OGRDXFLayer::GetDataset()
    3595             : {
    3596           1 :     return poDS;
    3597             : }

Generated by: LCOV version 1.14