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

Generated by: LCOV version 1.14