LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfwriterlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 441 508 86.8 %
Date: 2019-07-05 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements OGRDXFWriterLayer - the OGRLayer class used for
       5             :  *           writing a DXF file.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      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 "cpl_string.h"
      34             : #include "ogr_featurestyle.h"
      35             : 
      36             : #include <cstdlib>
      37             : 
      38             : CPL_CVSID("$Id$")
      39             : 
      40             : /************************************************************************/
      41             : /*                         OGRDXFWriterLayer()                          */
      42             : /************************************************************************/
      43             : 
      44          42 : OGRDXFWriterLayer::OGRDXFWriterLayer( OGRDXFWriterDS *poDSIn, VSILFILE *fpIn ) :
      45             :     fp(fpIn),
      46             :     poFeatureDefn(nullptr),  // TODO(schwehr): Can I move the new here?
      47          42 :     poDS(poDSIn)
      48             : {
      49          42 :     nNextAutoID = 1;
      50          42 :     bWriteHatch = CPLTestBool(CPLGetConfigOption("DXF_WRITE_HATCH", "YES"));
      51             : 
      52          42 :     poFeatureDefn = new OGRFeatureDefn( "entities" );
      53          42 :     poFeatureDefn->Reference();
      54             : 
      55             :     OGRDXFDataSource::AddStandardFields( poFeatureDefn,
      56          42 :         ODFM_IncludeBlockFields );
      57          42 : }
      58             : 
      59             : /************************************************************************/
      60             : /*                         ~OGRDXFWriterLayer()                         */
      61             : /************************************************************************/
      62             : 
      63         126 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
      64             : 
      65             : {
      66          42 :     if( poFeatureDefn )
      67          42 :         poFeatureDefn->Release();
      68          84 : }
      69             : 
      70             : /************************************************************************/
      71             : /*                              ResetFP()                               */
      72             : /*                                                                      */
      73             : /*      Redirect output.  Mostly used for writing block definitions.    */
      74             : /************************************************************************/
      75             : 
      76           2 : void OGRDXFWriterLayer::ResetFP( VSILFILE *fpNew )
      77             : 
      78             : {
      79           2 :     fp = fpNew;
      80           2 : }
      81             : 
      82             : /************************************************************************/
      83             : /*                           TestCapability()                           */
      84             : /************************************************************************/
      85             : 
      86         250 : int OGRDXFWriterLayer::TestCapability( const char * pszCap )
      87             : 
      88             : {
      89         250 :     if( EQUAL(pszCap,OLCStringsAsUTF8) )
      90           0 :         return TRUE;
      91         250 :     else if( EQUAL(pszCap,OLCSequentialWrite) )
      92          16 :         return TRUE;
      93             :     else
      94         234 :         return FALSE;
      95             : }
      96             : 
      97             : /************************************************************************/
      98             : /*                            CreateField()                             */
      99             : /*                                                                      */
     100             : /*      This is really a dummy as our fields are precreated.            */
     101             : /************************************************************************/
     102             : 
     103          80 : OGRErr OGRDXFWriterLayer::CreateField( OGRFieldDefn *poField,
     104             :                                        int bApproxOK )
     105             : 
     106             : {
     107         160 :     if( poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0
     108          80 :         && bApproxOK )
     109           0 :         return OGRERR_NONE;
     110             : 
     111             :     CPLError( CE_Failure, CPLE_AppDefined,
     112             :               "DXF layer does not support arbitrary field creation, field '%s' not created.",
     113          80 :               poField->GetNameRef() );
     114             : 
     115          80 :     return OGRERR_FAILURE;
     116             : }
     117             : 
     118             : /************************************************************************/
     119             : /*                             WriteValue()                             */
     120             : /************************************************************************/
     121             : 
     122         298 : int OGRDXFWriterLayer::WriteValue( int nCode, const char *pszValue )
     123             : 
     124             : {
     125         298 :     CPLString osLinePair;
     126             : 
     127         298 :     osLinePair.Printf( "%3d\n", nCode );
     128             : 
     129         298 :     if( strlen(pszValue) < 255 )
     130         298 :         osLinePair += pszValue;
     131             :     else
     132           0 :         osLinePair.append( pszValue, 255 );
     133             : 
     134         298 :     osLinePair += "\n";
     135             : 
     136         298 :     return VSIFWriteL( osLinePair.c_str(),
     137         596 :                        1, osLinePair.size(), fp ) == osLinePair.size();
     138             : }
     139             : 
     140             : /************************************************************************/
     141             : /*                             WriteValue()                             */
     142             : /************************************************************************/
     143             : 
     144         340 : int OGRDXFWriterLayer::WriteValue( int nCode, int nValue )
     145             : 
     146             : {
     147         340 :     CPLString osLinePair;
     148             : 
     149         340 :     osLinePair.Printf( "%3d\n%d\n", nCode, nValue );
     150             : 
     151         340 :     return VSIFWriteL( osLinePair.c_str(),
     152         680 :                        1, osLinePair.size(), fp ) == osLinePair.size();
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                             WriteValue()                             */
     157             : /************************************************************************/
     158             : 
     159         387 : int OGRDXFWriterLayer::WriteValue( int nCode, double dfValue )
     160             : 
     161             : {
     162             :     char szLinePair[64];
     163             : 
     164         387 :     CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue );
     165         387 :     size_t nLen = strlen(szLinePair);
     166             : 
     167             :     return VSIFWriteL( szLinePair,
     168         387 :                        1, nLen, fp ) == nLen;
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                             WriteCore()                              */
     173             : /*                                                                      */
     174             : /*      Write core fields common to all sorts of elements.              */
     175             : /************************************************************************/
     176             : 
     177          66 : OGRErr OGRDXFWriterLayer::WriteCore( OGRFeature *poFeature )
     178             : 
     179             : {
     180             : /* -------------------------------------------------------------------- */
     181             : /*      Write out an entity id.  I'm not sure why this is critical,     */
     182             : /*      but it seems that VoloView will just quietly fail to open       */
     183             : /*      dxf files without entity ids set on most/all entities.          */
     184             : /*      Also, for reasons I don't understand these ids seem to have     */
     185             : /*      to start somewhere around 0x50 hex (80 decimal).                */
     186             : /* -------------------------------------------------------------------- */
     187          66 :     poFeature->SetFID( poDS->WriteEntityID(fp,(int)poFeature->GetFID()) );
     188             : 
     189             : /* -------------------------------------------------------------------- */
     190             : /*      For now we assign everything to the default layer - layer       */
     191             : /*      "0" - if there is no layer property on the source features.     */
     192             : /* -------------------------------------------------------------------- */
     193          66 :     const char *pszLayer = poFeature->GetFieldAsString( "Layer" );
     194          66 :     if( pszLayer == nullptr || strlen(pszLayer) == 0 )
     195             :     {
     196          59 :         WriteValue( 8, "0" );
     197             :     }
     198             :     else
     199             :     {
     200           7 :         CPLString osSanitizedLayer(pszLayer);
     201             :         // Replaced restricted characters with underscore
     202             :         // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
     203             :         const char achForbiddenChars[] = {
     204           7 :           '<', '>', '/', '\\', '"', ':', ';', '?', '*', '|', '=', '\'' };
     205          91 :         for( size_t i = 0; i < CPL_ARRAYSIZE(achForbiddenChars); ++i )
     206             :         {
     207          84 :             osSanitizedLayer.replaceAll( achForbiddenChars[i], '_' );
     208             :         }
     209             : 
     210             :         // also remove newline characters (#15067)
     211           7 :         osSanitizedLayer.replaceAll( "\r\n", "_" );
     212           7 :         osSanitizedLayer.replaceAll( '\r', '_' );
     213           7 :         osSanitizedLayer.replaceAll( '\n', '_' );
     214             : 
     215             :         const char *pszExists =
     216           7 :             poDS->oHeaderDS.LookupLayerProperty( osSanitizedLayer, "Exists" );
     217          14 :         if( (pszExists == nullptr || strlen(pszExists) == 0)
     218          14 :             && CSLFindString( poDS->papszLayersToCreate, osSanitizedLayer ) == -1 )
     219             :         {
     220             :             poDS->papszLayersToCreate =
     221           3 :                 CSLAddString( poDS->papszLayersToCreate, osSanitizedLayer );
     222             :         }
     223             : 
     224           7 :         WriteValue( 8, osSanitizedLayer );
     225             :     }
     226             : 
     227          66 :     return OGRERR_NONE;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                            WriteINSERT()                             */
     232             : /************************************************************************/
     233             : 
     234           7 : OGRErr OGRDXFWriterLayer::WriteINSERT( OGRFeature *poFeature )
     235             : 
     236             : {
     237           7 :     WriteValue( 0, "INSERT" );
     238           7 :     WriteCore( poFeature );
     239           7 :     WriteValue( 100, "AcDbEntity" );
     240           7 :     WriteValue( 100, "AcDbBlockReference" );
     241           7 :     WriteValue( 2, poFeature->GetFieldAsString("BlockName") );
     242             : 
     243             :     // Write style symbol color
     244           7 :     OGRStyleTool *poTool = nullptr;
     245           7 :     OGRStyleMgr oSM;
     246           7 :     if( poFeature->GetStyleString() != nullptr )
     247             :     {
     248           0 :         oSM.InitFromFeature( poFeature );
     249             : 
     250           0 :         if( oSM.GetPartCount() > 0 )
     251           0 :             poTool = oSM.GetPart(0);
     252             :     }
     253           7 :     if( poTool && poTool->GetType() == OGRSTCSymbol )
     254             :     {
     255           0 :         OGRStyleSymbol *poSymbol = (OGRStyleSymbol *) poTool;
     256             :         GBool bDefault;
     257             : 
     258           0 :         if( poSymbol->Color(bDefault) != nullptr && !bDefault )
     259           0 :             WriteValue( 62, ColorStringToDXFColor( poSymbol->Color(bDefault) ) );
     260             :     }
     261           7 :     delete poTool;
     262             : 
     263             : /* -------------------------------------------------------------------- */
     264             : /*      Write location in OCS.                                          */
     265             : /* -------------------------------------------------------------------- */
     266           7 :     int nCoordCount = 0;
     267             :     const double *padfCoords =
     268           7 :         poFeature->GetFieldAsDoubleList( "BlockOCSCoords", &nCoordCount );
     269             : 
     270           7 :     if( nCoordCount == 3 )
     271             :     {
     272           0 :         WriteValue( 10, padfCoords[0] );
     273           0 :         WriteValue( 20, padfCoords[1] );
     274           0 :         if( !WriteValue( 30, padfCoords[2] ) )
     275           0 :             return OGRERR_FAILURE;
     276             :     }
     277             :     else
     278             :     {
     279             :         // We don't have an OCS; we will just assume that the location of
     280             :         // the geometry (in WCS) is the correct insertion point.
     281           7 :         OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     282             : 
     283           7 :         WriteValue( 10, poPoint->getX() );
     284           7 :         if( !WriteValue( 20, poPoint->getY() ) )
     285           0 :             return OGRERR_FAILURE;
     286             : 
     287           7 :         if( poPoint->getGeometryType() == wkbPoint25D )
     288             :         {
     289           0 :             if( !WriteValue( 30, poPoint->getZ() ) )
     290           0 :                 return OGRERR_FAILURE;
     291             :         }
     292             :     }
     293             : 
     294             : /* -------------------------------------------------------------------- */
     295             : /*      Write scaling.                                                  */
     296             : /* -------------------------------------------------------------------- */
     297           7 :     int nScaleCount = 0;
     298             :     const double *padfScale =
     299           7 :         poFeature->GetFieldAsDoubleList( "BlockScale", &nScaleCount );
     300             : 
     301           7 :     if( nScaleCount == 3 )
     302             :     {
     303           1 :         WriteValue( 41, padfScale[0] );
     304           1 :         WriteValue( 42, padfScale[1] );
     305           1 :         WriteValue( 43, padfScale[2] );
     306             :     }
     307             : 
     308             : /* -------------------------------------------------------------------- */
     309             : /*      Write rotation.                                                 */
     310             : /* -------------------------------------------------------------------- */
     311           7 :     const double dfAngle = poFeature->GetFieldAsDouble( "BlockAngle" );
     312             : 
     313           7 :     if( dfAngle != 0.0 )
     314             :     {
     315           1 :         WriteValue( 50, dfAngle ); // degrees
     316             :     }
     317             : 
     318             : /* -------------------------------------------------------------------- */
     319             : /*      Write OCS normal vector.                                        */
     320             : /* -------------------------------------------------------------------- */
     321           7 :     int nOCSCount = 0;
     322             :     const double *padfOCS =
     323           7 :         poFeature->GetFieldAsDoubleList( "BlockOCSNormal", &nOCSCount );
     324             : 
     325           7 :     if( nOCSCount == 3 )
     326             :     {
     327           0 :         WriteValue( 210, padfOCS[0] );
     328           0 :         WriteValue( 220, padfOCS[1] );
     329           0 :         WriteValue( 230, padfOCS[2] );
     330             :     }
     331             : 
     332           7 :     return OGRERR_NONE;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                             WritePOINT()                             */
     337             : /************************************************************************/
     338             : 
     339          11 : OGRErr OGRDXFWriterLayer::WritePOINT( OGRFeature *poFeature )
     340             : 
     341             : {
     342          11 :     WriteValue( 0, "POINT" );
     343          11 :     WriteCore( poFeature );
     344          11 :     WriteValue( 100, "AcDbEntity" );
     345          11 :     WriteValue( 100, "AcDbPoint" );
     346             : 
     347             :     // Write style pen color
     348          11 :     OGRStyleTool *poTool = nullptr;
     349          11 :     OGRStyleMgr oSM;
     350          11 :     if( poFeature->GetStyleString() != nullptr )
     351             :     {
     352           0 :         oSM.InitFromFeature( poFeature );
     353             : 
     354           0 :         if( oSM.GetPartCount() > 0 )
     355           0 :             poTool = oSM.GetPart(0);
     356             :     }
     357          11 :     if( poTool && poTool->GetType() == OGRSTCPen )
     358             :     {
     359           0 :         OGRStylePen *poPen = (OGRStylePen *) poTool;
     360             :         GBool  bDefault;
     361             : 
     362           0 :         if( poPen->Color(bDefault) != nullptr && !bDefault )
     363           0 :             WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
     364             :     }
     365          11 :     delete poTool;
     366             : 
     367          11 :     OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     368             : 
     369          11 :     WriteValue( 10, poPoint->getX() );
     370          11 :     if( !WriteValue( 20, poPoint->getY() ) )
     371           0 :         return OGRERR_FAILURE;
     372             : 
     373          11 :     if( poPoint->getGeometryType() == wkbPoint25D )
     374             :     {
     375           4 :         if( !WriteValue( 30, poPoint->getZ() ) )
     376           0 :             return OGRERR_FAILURE;
     377             :     }
     378             : 
     379          11 :     return OGRERR_NONE;
     380             : }
     381             : 
     382             : /************************************************************************/
     383             : /*                             TextEscape()                             */
     384             : /*                                                                      */
     385             : /*      Translate UTF8 to Win1252 and escape special characters like    */
     386             : /*      newline and space with DXF style escapes.  Note that            */
     387             : /*      non-win1252 unicode characters are translated using the         */
     388             : /*      unicode escape sequence.                                        */
     389             : /************************************************************************/
     390             : 
     391           1 : CPLString OGRDXFWriterLayer::TextEscape( const char *pszInput )
     392             : 
     393             : {
     394           1 :     CPLString osResult;
     395             :     wchar_t *panInput = CPLRecodeToWChar( pszInput,
     396             :                                           CPL_ENC_UTF8,
     397           1 :                                           CPL_ENC_UCS2 );
     398          31 :     for( int i = 0; panInput[i] != 0; i++ )
     399             :     {
     400          30 :         if( panInput[i] == '\n' )
     401             :         {
     402           0 :             osResult += "\\P";
     403             :         }
     404          30 :         else if( panInput[i] == ' ' )
     405             :         {
     406           2 :             osResult += "\\~";
     407             :         }
     408          28 :         else if( panInput[i] == '\\' )
     409             :         {
     410           0 :             osResult += "\\\\";
     411             :         }
     412          28 :         else if( panInput[i] == '^' )
     413             :         {
     414           1 :             osResult += "^ ";
     415             :         }
     416          27 :         else if( panInput[i] < ' ' )
     417             :         {
     418           1 :             osResult += '^';
     419           1 :             osResult += static_cast<char>( panInput[i] + '@' );
     420             :         }
     421          26 :         else if( panInput[i] > 255 )
     422             :         {
     423           0 :             CPLString osUnicode;
     424           0 :             osUnicode.Printf( "\\U+%04x", (int) panInput[i] );
     425           0 :             osResult += osUnicode;
     426             :         }
     427             :         else
     428             :         {
     429          26 :             osResult += (char) panInput[i];
     430             :         }
     431             :     }
     432             : 
     433           1 :     CPLFree(panInput);
     434             : 
     435           1 :     return osResult;
     436             : }
     437             : 
     438             : /************************************************************************/
     439             : /*                     PrepareTextStyleDefinition()                     */
     440             : /************************************************************************/
     441             : std::map<CPLString, CPLString>
     442           1 : OGRDXFWriterLayer::PrepareTextStyleDefinition( OGRStyleLabel *poLabelTool )
     443             : {
     444             :     GBool bDefault;
     445             : 
     446           1 :     std::map<CPLString, CPLString> oTextStyleDef;
     447             : 
     448             : /* -------------------------------------------------------------------- */
     449             : /*      Fetch the data for this text style.                             */
     450             : /* -------------------------------------------------------------------- */
     451           1 :     const char *pszFontName = poLabelTool->FontName(bDefault);
     452           1 :     if( !bDefault )
     453           1 :         oTextStyleDef["Font"] = pszFontName;
     454             : 
     455           1 :     const GBool bBold = poLabelTool->Bold(bDefault);
     456           1 :     if( !bDefault )
     457           1 :         oTextStyleDef["Bold"] = bBold ? "1" : "0";
     458             : 
     459           1 :     const GBool bItalic = poLabelTool->Italic(bDefault);
     460           1 :     if( !bDefault )
     461           0 :         oTextStyleDef["Italic"] = bItalic ? "1" : "0";
     462             : 
     463           1 :     const double dfStretch = poLabelTool->Stretch(bDefault);
     464           1 :     if( !bDefault )
     465             :     {
     466           2 :         oTextStyleDef["Width"] = CPLString().Printf( "%f",
     467           3 :             dfStretch / 100.0 );
     468             :     }
     469             : 
     470           1 :     return oTextStyleDef;
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                             WriteTEXT()                              */
     475             : /************************************************************************/
     476             : 
     477           1 : OGRErr OGRDXFWriterLayer::WriteTEXT( OGRFeature *poFeature )
     478             : 
     479             : {
     480           1 :     WriteValue( 0, "MTEXT" );
     481           1 :     WriteCore( poFeature );
     482           1 :     WriteValue( 100, "AcDbEntity" );
     483           1 :     WriteValue( 100, "AcDbMText" );
     484             : 
     485             : /* -------------------------------------------------------------------- */
     486             : /*      Do we have styling information?                                 */
     487             : /* -------------------------------------------------------------------- */
     488           1 :     OGRStyleTool *poTool = nullptr;
     489           1 :     OGRStyleMgr oSM;
     490             : 
     491           1 :     if( poFeature->GetStyleString() != nullptr )
     492             :     {
     493           1 :         oSM.InitFromFeature( poFeature );
     494             : 
     495           1 :         if( oSM.GetPartCount() > 0 )
     496           1 :             poTool = oSM.GetPart(0);
     497             :     }
     498             : 
     499             : /* ==================================================================== */
     500             : /*      Process the LABEL tool.                                         */
     501             : /* ==================================================================== */
     502           1 :     double dfDx = 0.0;
     503           1 :     double dfDy = 0.0;
     504             : 
     505           1 :     if( poTool && poTool->GetType() == OGRSTCLabel )
     506             :     {
     507           1 :         OGRStyleLabel *poLabel = (OGRStyleLabel *) poTool;
     508             :         GBool  bDefault;
     509             : 
     510             : /* -------------------------------------------------------------------- */
     511             : /*      Color                                                           */
     512             : /* -------------------------------------------------------------------- */
     513           1 :         if( poLabel->ForeColor(bDefault) != nullptr && !bDefault )
     514             :             WriteValue( 62, ColorStringToDXFColor(
     515           1 :                             poLabel->ForeColor(bDefault) ) );
     516             : 
     517             : /* -------------------------------------------------------------------- */
     518             : /*      Angle                                                           */
     519             : /* -------------------------------------------------------------------- */
     520           1 :         const double dfAngle = poLabel->Angle(bDefault);
     521             : 
     522           1 :         if( !bDefault )
     523           1 :             WriteValue( 50, dfAngle );
     524             : 
     525             : /* -------------------------------------------------------------------- */
     526             : /*      Height - We need to fetch this in georeferenced units - I'm     */
     527             : /*      doubt the default translation mechanism will be much good.      */
     528             : /* -------------------------------------------------------------------- */
     529           1 :         poTool->SetUnit( OGRSTUGround );
     530           1 :         const double dfHeight = poLabel->Size(bDefault);
     531             : 
     532           1 :         if( !bDefault )
     533           1 :             WriteValue( 40, dfHeight );
     534             : 
     535             : /* -------------------------------------------------------------------- */
     536             : /*      Anchor / Attachment Point                                       */
     537             : /* -------------------------------------------------------------------- */
     538           1 :         const int nAnchor = poLabel->Anchor(bDefault);
     539             : 
     540           1 :         if( !bDefault )
     541             :         {
     542             :             const static int anAnchorMap[] =
     543             :                 { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3, 7, 8, 9 };
     544             : 
     545           0 :             if( nAnchor > 0 && nAnchor < 13 )
     546           0 :                 WriteValue( 71, anAnchorMap[nAnchor] );
     547             :         }
     548             : 
     549             : /* -------------------------------------------------------------------- */
     550             : /*      Offset                                                          */
     551             : /* -------------------------------------------------------------------- */
     552           1 :         dfDx = poLabel->SpacingX(bDefault);
     553           1 :         dfDy = poLabel->SpacingY(bDefault);
     554             : 
     555             : /* -------------------------------------------------------------------- */
     556             : /*      Escape the text, and convert to ISO8859.                        */
     557             : /* -------------------------------------------------------------------- */
     558           1 :         const char *pszText = poLabel->TextString( bDefault );
     559             : 
     560           1 :         if( pszText != nullptr && !bDefault )
     561             :         {
     562           1 :             CPLString osEscaped = TextEscape( pszText );
     563           2 :             while( osEscaped.size() > 250 )
     564             :             {
     565           0 :                 WriteValue( 3, osEscaped.substr( 0, 250 ).c_str() );
     566           0 :                 osEscaped.erase( 0, 250 );
     567             :             }
     568           1 :             WriteValue( 1, osEscaped );
     569             :         }
     570             : 
     571             : /* -------------------------------------------------------------------- */
     572             : /*      Store the text style in the map.                                */
     573             : /* -------------------------------------------------------------------- */
     574             :         std::map<CPLString, CPLString> oTextStyleDef =
     575           1 :             PrepareTextStyleDefinition( poLabel );
     576           2 :         CPLString osStyleName;
     577             : 
     578           1 :         for( const auto& oPair: oNewTextStyles )
     579             :         {
     580           0 :             if( oPair.second == oTextStyleDef )
     581             :             {
     582           0 :                 osStyleName = oPair.first;
     583           0 :                 break;
     584             :             }
     585             :         }
     586             : 
     587           1 :         if( osStyleName == "" )
     588             :         {
     589             : 
     590           1 :             do
     591             :             {
     592           1 :                 osStyleName.Printf( "AutoTextStyle-%d", nNextAutoID++ );
     593             :             }
     594           1 :             while( poDS->oHeaderDS.TextStyleExists( osStyleName ) );
     595             : 
     596           1 :             oNewTextStyles[osStyleName] = oTextStyleDef;
     597             :         }
     598             : 
     599           2 :         WriteValue( 7, osStyleName );
     600             :     }
     601             : 
     602           1 :     delete poTool;
     603             : 
     604             : /* -------------------------------------------------------------------- */
     605             : /*      Write the location.                                             */
     606             : /* -------------------------------------------------------------------- */
     607           1 :     OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
     608             : 
     609           1 :     WriteValue( 10, poPoint->getX() + dfDx );
     610           1 :     if( !WriteValue( 20, poPoint->getY() + dfDy ) )
     611           0 :         return OGRERR_FAILURE;
     612             : 
     613           1 :     if( poPoint->getGeometryType() == wkbPoint25D )
     614             :     {
     615           1 :         if( !WriteValue( 30, poPoint->getZ() ) )
     616           0 :             return OGRERR_FAILURE;
     617             :     }
     618             : 
     619           1 :     return OGRERR_NONE;
     620             : }
     621             : 
     622             : /************************************************************************/
     623             : /*                     PrepareLineTypeDefinition()                      */
     624             : /************************************************************************/
     625             : std::vector<double>
     626           5 : OGRDXFWriterLayer::PrepareLineTypeDefinition( OGRStylePen *poPen )
     627             : {
     628             : 
     629             : /* -------------------------------------------------------------------- */
     630             : /*      Fetch pattern.                                                  */
     631             : /* -------------------------------------------------------------------- */
     632             :     GBool bDefault;
     633           5 :     const char *pszPattern = poPen->Pattern( bDefault );
     634             : 
     635           5 :     if( bDefault || strlen(pszPattern) == 0 )
     636           0 :         return std::vector<double>();
     637             : 
     638             : /* -------------------------------------------------------------------- */
     639             : /*      Split into pen up / pen down bits.                              */
     640             : /* -------------------------------------------------------------------- */
     641           5 :     char **papszTokens = CSLTokenizeString(pszPattern);
     642           5 :     std::vector<double> adfWeightTokens;
     643             : 
     644          15 :     for( int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++ )
     645             :     {
     646          10 :         const char *pszToken = papszTokens[i];
     647          10 :         CPLString osAmount;
     648          20 :         CPLString osDXFEntry;
     649             : 
     650             :         // Split amount and unit.
     651          10 :         const char *pszUnit = pszToken;  // Used after for.
     652          58 :         for( ;
     653          48 :              strchr( "0123456789.", *pszUnit) != nullptr;
     654             :              pszUnit++ ) {}
     655             : 
     656          10 :         osAmount.assign(pszToken,(int) (pszUnit-pszToken));
     657             : 
     658             :         // If the unit is other than 'g' we really should be trying to
     659             :         // do some type of transformation - but what to do?  Pretty hard.
     660             : 
     661             :         // Even entries are "pen down" represented as positive in DXF.
     662             :         // "Pen up" entries (gaps) are represented as negative.
     663          10 :         if( i%2 == 0 )
     664           5 :             adfWeightTokens.push_back( CPLAtof( osAmount ) );
     665             :         else
     666           5 :             adfWeightTokens.push_back( -CPLAtof( osAmount ) );
     667          10 :     }
     668             : 
     669           5 :     CSLDestroy( papszTokens );
     670             : 
     671           5 :     return adfWeightTokens;
     672             : }
     673             : 
     674             : /************************************************************************/
     675             : /*                       IsLineTypeProportional()                       */
     676             : /************************************************************************/
     677             : 
     678           7 : static double IsLineTypeProportional( const std::vector<double>& adfA,
     679             :     const std::vector<double>& adfB )
     680             : {
     681             :     // If they are not the same length, they are not the same linetype
     682           7 :     if( adfA.size() != adfB.size() )
     683           0 :         return 0.0;
     684             : 
     685             :     // Determine the proportion of the first elements
     686           7 :     const double dfRatio = ( adfA[0] != 0.0 ) ? ( adfB[0] / adfA[0] ) : 0.0;
     687             : 
     688             :     // Check if all elements follow this proportionality
     689           9 :     for( size_t iIndex = 1; iIndex < adfA.size(); iIndex++ )
     690           7 :         if( fabs( adfB[iIndex] - ( adfA[iIndex] * dfRatio ) ) > 1e-6 )
     691           5 :             return 0.0;
     692             : 
     693           2 :     return dfRatio;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*                           WritePOLYLINE()                            */
     698             : /************************************************************************/
     699             : 
     700          30 : OGRErr OGRDXFWriterLayer::WritePOLYLINE( OGRFeature *poFeature,
     701             :                                          const OGRGeometry *poGeom )
     702             : 
     703             : {
     704             : /* -------------------------------------------------------------------- */
     705             : /*      For now we handle multilinestrings by writing a series of       */
     706             : /*      entities.                                                       */
     707             : /* -------------------------------------------------------------------- */
     708          30 :     if( poGeom == nullptr )
     709          26 :         poGeom = poFeature->GetGeometryRef();
     710             : 
     711          30 :     if ( poGeom->IsEmpty() )
     712             :     {
     713           0 :         return OGRERR_NONE;
     714             :     }
     715             : 
     716          60 :     if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
     717          30 :         || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString )
     718             :     {
     719           4 :         const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
     720           4 :         OGRErr eErr = OGRERR_NONE;
     721           8 :         for( auto&& poMember: *poGC )
     722             :         {
     723           4 :             eErr = WritePOLYLINE( poFeature, poMember );
     724           4 :             if( eErr != OGRERR_NONE )
     725           0 :                 break;
     726             :         }
     727             : 
     728           4 :         return eErr;
     729             :     }
     730             : 
     731             : /* -------------------------------------------------------------------- */
     732             : /*      Polygons are written with on entity per ring.                   */
     733             : /* -------------------------------------------------------------------- */
     734          52 :     if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon
     735          26 :         || wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
     736             :     {
     737           0 :         const OGRPolygon *poPoly = poGeom->toPolygon();
     738           0 :         OGRErr eErr = OGRERR_NONE;
     739           0 :         for( auto&& poRing: *poPoly )
     740             :         {
     741           0 :             eErr = WritePOLYLINE( poFeature, poRing );
     742           0 :             if( eErr != OGRERR_NONE )
     743           0 :                 break;
     744             :         }
     745             : 
     746           0 :         return eErr;
     747             :     }
     748             : 
     749             : /* -------------------------------------------------------------------- */
     750             : /*      Do we now have a geometry we can work with?                     */
     751             : /* -------------------------------------------------------------------- */
     752          26 :     if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString )
     753           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     754             : 
     755          26 :     const OGRLineString *poLS = poGeom->toLineString();
     756             : 
     757             : /* -------------------------------------------------------------------- */
     758             : /*      Write as a lightweight polygon,                                 */
     759             : /*       or as POLYLINE if the line contains different heights          */
     760             : /* -------------------------------------------------------------------- */
     761          26 :     int bHasDifferentZ = FALSE;
     762          26 :     if( poLS->getGeometryType() == wkbLineString25D )
     763             :     {
     764           8 :         double z0 = poLS->getZ(0);
     765          23 :         for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
     766             :         {
     767          16 :             if (z0 != poLS->getZ(iVert))
     768             :             {
     769           1 :                 bHasDifferentZ = TRUE;
     770           1 :                 break;
     771             :             }
     772             :         }
     773             :     }
     774             : 
     775          26 :     WriteValue( 0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE" );
     776          26 :     WriteCore( poFeature );
     777          26 :     WriteValue( 100, "AcDbEntity" );
     778          26 :     if( bHasDifferentZ )
     779             :     {
     780           1 :         WriteValue( 100, "AcDb3dPolyline" );
     781           1 :         WriteValue( 10, 0.0 );
     782           1 :         WriteValue( 20, 0.0 );
     783           1 :         WriteValue( 30, 0.0 );
     784             :     }
     785             :     else
     786          25 :         WriteValue( 100, "AcDbPolyline" );
     787          26 :     if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
     788           0 :         WriteValue( 70, 1 + (bHasDifferentZ ? 8 : 0) );
     789             :     else
     790          26 :         WriteValue( 70, 0 + (bHasDifferentZ ? 8 : 0) );
     791          26 :     if( !bHasDifferentZ )
     792          25 :         WriteValue( 90, poLS->getNumPoints() );
     793             :     else
     794           1 :         WriteValue( 66, "1" );  // Vertex Flag
     795             : 
     796             : /* -------------------------------------------------------------------- */
     797             : /*      Do we have styling information?                                 */
     798             : /* -------------------------------------------------------------------- */
     799          26 :     OGRStyleTool *poTool = nullptr;
     800          26 :     OGRStyleMgr oSM;
     801             : 
     802          26 :     if( poFeature->GetStyleString() != nullptr )
     803             :     {
     804           5 :         oSM.InitFromFeature( poFeature );
     805             : 
     806           5 :         if( oSM.GetPartCount() > 0 )
     807           5 :             poTool = oSM.GetPart(0);
     808             :     }
     809             : 
     810             : /* -------------------------------------------------------------------- */
     811             : /*      Handle a PEN tool to control drawing color and width.           */
     812             : /*      Perhaps one day also dottedness, etc.                           */
     813             : /* -------------------------------------------------------------------- */
     814          26 :     if( poTool && poTool->GetType() == OGRSTCPen )
     815             :     {
     816           5 :         OGRStylePen *poPen = (OGRStylePen *) poTool;
     817             :         GBool bDefault;
     818             : 
     819           5 :         if( poPen->Color(bDefault) != nullptr && !bDefault )
     820           5 :             WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
     821             : 
     822             :         // we want to fetch the width in ground units.
     823           5 :         poPen->SetUnit( OGRSTUGround, 1.0 );
     824           5 :         const double dfWidth = poPen->Width(bDefault);
     825             : 
     826           5 :         if( !bDefault )
     827           5 :             WriteValue( 370, (int) floor(dfWidth * 100 + 0.5) );
     828             :     }
     829             : 
     830             : /* -------------------------------------------------------------------- */
     831             : /*      Do we have a Linetype for the feature?                          */
     832             : /* -------------------------------------------------------------------- */
     833          52 :     CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
     834          26 :     double dfLineTypeScale = 0.0;
     835             : 
     836          26 :     bool bGotLinetype = false;
     837             : 
     838          26 :     if( !osLineType.empty() )
     839             :     {
     840             :         std::vector<double> adfLineType =
     841           2 :             poDS->oHeaderDS.LookupLineType( osLineType );
     842             : 
     843           2 :         if( adfLineType.empty() && oNewLineTypes.count(osLineType) > 0 )
     844           0 :             adfLineType = oNewLineTypes[osLineType];
     845             : 
     846           2 :         if( !adfLineType.empty() )
     847             :         {
     848           1 :             bGotLinetype = true;
     849           1 :             WriteValue( 6, osLineType );
     850             : 
     851             :             // If the given linetype is proportional to the linetype data
     852             :             // in the style string, then apply a linetype scale
     853           1 :             if( poTool != nullptr && poTool->GetType() == OGRSTCPen )
     854             :             {
     855             :                 std::vector<double> adfDefinition = PrepareLineTypeDefinition(
     856           1 :                     static_cast<OGRStylePen *>( poTool ) );
     857             :                 
     858           1 :                 if( !adfDefinition.empty() )
     859             :                 {
     860             :                     dfLineTypeScale = IsLineTypeProportional( adfLineType,
     861           1 :                         adfDefinition );
     862             : 
     863           1 :                     if( dfLineTypeScale != 0.0 && 
     864           0 :                         fabs( dfLineTypeScale - 1.0 ) > 1e-4 )
     865             :                     {
     866           0 :                         WriteValue( 48, dfLineTypeScale );
     867             :                     }
     868           1 :                 }
     869             :             }
     870           2 :         }
     871             :     }
     872             : 
     873          26 :     if( !bGotLinetype && poTool != nullptr && poTool->GetType() == OGRSTCPen )
     874             :     {
     875             :         std::vector<double> adfDefinition = PrepareLineTypeDefinition(
     876           4 :             static_cast<OGRStylePen *>( poTool ) );
     877             : 
     878           4 :         if( !adfDefinition.empty() )
     879             :         {
     880             :             // Is this definition already created and named?
     881           7 :             for( const auto& oPair : poDS->oHeaderDS.GetLineTypeTable() )
     882             :             {
     883             :                 dfLineTypeScale = IsLineTypeProportional( oPair.second,
     884           4 :                     adfDefinition );
     885           4 :                 if( dfLineTypeScale != 0.0 )
     886             :                 {
     887           1 :                     osLineType = oPair.first;
     888           1 :                     break;
     889             :                 }
     890             :             }
     891             : 
     892           4 :             if( dfLineTypeScale == 0.0 )
     893             :             {
     894           4 :                 for( const auto& oPair : oNewLineTypes )
     895             :                 {
     896             :                     dfLineTypeScale = IsLineTypeProportional( oPair.second,
     897           2 :                         adfDefinition );
     898           2 :                     if( dfLineTypeScale != 0.0 )
     899             :                     {
     900           1 :                         osLineType = oPair.first;
     901           1 :                         break;
     902             :                     }
     903             :                 }
     904             :             }
     905             : 
     906             :             // If not, create an automatic name for it.
     907           4 :             if( osLineType == "" )
     908             :             {
     909           1 :                 dfLineTypeScale = 1.0;
     910           2 :                 do
     911             :                 {
     912           1 :                     osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
     913             :                 }
     914           2 :                 while( poDS->oHeaderDS.LookupLineType(osLineType).size() > 0 );
     915             :             }
     916             : 
     917             :             // If it isn't already defined, add it now.
     918           7 :             if( poDS->oHeaderDS.LookupLineType( osLineType ).empty() &&
     919           3 :                 oNewLineTypes.count( osLineType ) == 0 )
     920             :             {
     921           2 :                 oNewLineTypes[osLineType] = adfDefinition;
     922             :             }
     923             : 
     924           4 :             WriteValue( 6, osLineType );
     925             : 
     926           4 :             if( dfLineTypeScale != 0.0 && fabs( dfLineTypeScale - 1.0 ) > 1e-4 )
     927             :             {
     928           2 :                 WriteValue( 48, dfLineTypeScale );
     929             :             }
     930           4 :         }
     931             :     }
     932             : 
     933             : /* -------------------------------------------------------------------- */
     934             : /*      Write the vertices                                              */
     935             : /* -------------------------------------------------------------------- */
     936             : 
     937          26 :     if( !bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D )
     938             :     {
     939             :      // if LWPOLYLINE with Z write it only once
     940           7 :         if( !WriteValue( 38, poLS->getZ(0) ) )
     941           0 :             return OGRERR_FAILURE;
     942             :     }
     943             : 
     944          78 :     for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
     945             :     {
     946          52 :         if( bHasDifferentZ )
     947             :         {
     948           2 :             WriteValue( 0, "VERTEX" );
     949           2 :             WriteValue( 100, "AcDbEntity" );
     950           2 :             WriteValue( 100, "AcDbVertex" );
     951           2 :             WriteValue( 100, "AcDb3dPolylineVertex" );
     952           2 :             WriteCore( poFeature );
     953             :         }
     954          52 :         WriteValue( 10, poLS->getX(iVert) );
     955          52 :         if( !WriteValue( 20, poLS->getY(iVert) ) )
     956           0 :             return OGRERR_FAILURE;
     957             : 
     958          52 :         if( bHasDifferentZ )
     959             :         {
     960           2 :             if( !WriteValue( 30 , poLS->getZ(iVert) ) )
     961           0 :                 return OGRERR_FAILURE;
     962           2 :             WriteValue( 70, 32 );
     963             :         }
     964             :     }
     965             : 
     966          26 :     if( bHasDifferentZ )
     967             :     {
     968           1 :         WriteValue( 0, "SEQEND" );
     969           1 :         WriteCore( poFeature );
     970           1 :         WriteValue( 100, "AcDbEntity" );
     971             :     }
     972             : 
     973          26 :     delete poTool;
     974             : 
     975          52 :     return OGRERR_NONE;
     976             : 
     977             : #ifdef notdef
     978             : /* -------------------------------------------------------------------- */
     979             : /*      Alternate unmaintained implementation as a polyline entity.     */
     980             : /* -------------------------------------------------------------------- */
     981             :     WriteValue( 0, "POLYLINE" );
     982             :     WriteCore( poFeature );
     983             :     WriteValue( 100, "AcDbEntity" );
     984             :     WriteValue( 100, "AcDbPolyline" );
     985             :     if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
     986             :         WriteValue( 70, 1 );
     987             :     else
     988             :         WriteValue( 70, 0 );
     989             :     WriteValue( 66, "1" );
     990             : 
     991             :     for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
     992             :     {
     993             :         WriteValue( 0, "VERTEX" );
     994             :         WriteValue( 8, "0" );
     995             :         WriteValue( 10, poLS->getX(iVert) );
     996             :         if( !WriteValue( 20, poLS->getY(iVert) ) )
     997             :             return OGRERR_FAILURE;
     998             : 
     999             :         if( poLS->getGeometryType() == wkbLineString25D )
    1000             :         {
    1001             :             if( !WriteValue( 30, poLS->getZ(iVert) ) )
    1002             :                 return OGRERR_FAILURE;
    1003             :         }
    1004             :     }
    1005             : 
    1006             :     WriteValue( 0, "SEQEND" );
    1007             :     WriteValue( 8, "0" );
    1008             : 
    1009             :     return OGRERR_NONE;
    1010             : #endif
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                             WriteHATCH()                             */
    1015             : /************************************************************************/
    1016             : 
    1017          22 : OGRErr OGRDXFWriterLayer::WriteHATCH( OGRFeature *poFeature,
    1018             :                                       OGRGeometry *poGeom )
    1019             : 
    1020             : {
    1021             : /* -------------------------------------------------------------------- */
    1022             : /*      For now we handle multipolygons by writing a series of          */
    1023             : /*      entities.                                                       */
    1024             : /* -------------------------------------------------------------------- */
    1025          22 :     if( poGeom == nullptr )
    1026          18 :         poGeom = poFeature->GetGeometryRef();
    1027             : 
    1028          22 :     if ( poGeom->IsEmpty() )
    1029             :     {
    1030           0 :         return OGRERR_NONE;
    1031             :     }
    1032             : 
    1033          22 :     if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon )
    1034             :     {
    1035           4 :         OGRErr eErr = OGRERR_NONE;
    1036           8 :         for( auto&& poMember: poGeom->toMultiPolygon() )
    1037             :         {
    1038           4 :             eErr = WriteHATCH( poFeature, poMember );
    1039           4 :             if( eErr != OGRERR_NONE )
    1040           0 :                 break;
    1041             :         }
    1042             : 
    1043           4 :         return eErr;
    1044             :     }
    1045             : 
    1046             : /* -------------------------------------------------------------------- */
    1047             : /*      Do we now have a geometry we can work with?                     */
    1048             : /* -------------------------------------------------------------------- */
    1049          19 :     if( wkbFlatten(poGeom->getGeometryType()) != wkbPolygon &&
    1050           1 :         wkbFlatten(poGeom->getGeometryType()) != wkbTriangle )
    1051             :     {
    1052           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    1053             :     }
    1054             : 
    1055             : /* -------------------------------------------------------------------- */
    1056             : /*      Write as a hatch.                                               */
    1057             : /* -------------------------------------------------------------------- */
    1058          18 :     WriteValue( 0, "HATCH" );
    1059          18 :     WriteCore( poFeature );
    1060          18 :     WriteValue( 100, "AcDbEntity" );
    1061          18 :     WriteValue( 100, "AcDbHatch" );
    1062             : 
    1063             :     // Figure out "average" elevation
    1064          18 :     OGREnvelope3D oEnv;
    1065          18 :     poGeom->getEnvelope( &oEnv );
    1066          18 :     WriteValue( 10, 0 ); // elevation point X = 0
    1067          18 :     WriteValue( 20, 0 ); // elevation point Y = 0
    1068             :     // elevation point Z = constant elevation
    1069          18 :     WriteValue( 30, oEnv.MinZ + ( oEnv.MaxZ - oEnv.MinZ ) / 2 );
    1070             : 
    1071          18 :     WriteValue(210, 0 ); // extrusion direction X
    1072          18 :     WriteValue(220, 0 ); // extrusion direction Y
    1073          18 :     WriteValue(230,1.0); // extrusion direction Z
    1074             : 
    1075          18 :     WriteValue( 2, "SOLID" ); // fill pattern
    1076          18 :     WriteValue( 70, 1 ); // solid fill
    1077          18 :     WriteValue( 71, 0 ); // associativity
    1078             : 
    1079             : /* -------------------------------------------------------------------- */
    1080             : /*      Do we have styling information?                                 */
    1081             : /* -------------------------------------------------------------------- */
    1082          18 :     OGRStyleTool *poTool = nullptr;
    1083          18 :     OGRStyleMgr oSM;
    1084             : 
    1085          18 :     if( poFeature->GetStyleString() != nullptr )
    1086             :     {
    1087           1 :         oSM.InitFromFeature( poFeature );
    1088             : 
    1089           1 :         if( oSM.GetPartCount() > 0 )
    1090           1 :             poTool = oSM.GetPart(0);
    1091             :     }
    1092             :     // Write style brush fore color
    1093          18 :     if( poTool && poTool->GetType() == OGRSTCBrush )
    1094             :     {
    1095           1 :         OGRStyleBrush *poBrush = (OGRStyleBrush *) poTool;
    1096             :         GBool  bDefault;
    1097             : 
    1098           1 :         if( poBrush->ForeColor(bDefault) != nullptr && !bDefault )
    1099           1 :             WriteValue( 62, ColorStringToDXFColor( poBrush->ForeColor(bDefault) ) );
    1100             :     }
    1101          18 :     delete poTool;
    1102             : 
    1103             : /* -------------------------------------------------------------------- */
    1104             : /*      Handle a PEN tool to control drawing color and width.           */
    1105             : /*      Perhaps one day also dottedness, etc.                           */
    1106             : /* -------------------------------------------------------------------- */
    1107             : #ifdef notdef
    1108             :     if( poTool && poTool->GetType() == OGRSTCPen )
    1109             :     {
    1110             :         OGRStylePen *poPen = (OGRStylePen *) poTool;
    1111             :         GBool  bDefault;
    1112             : 
    1113             :         if( poPen->Color(bDefault) != NULL && !bDefault )
    1114             :             WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) );
    1115             : 
    1116             :         double dfWidthInMM = poPen->Width(bDefault);
    1117             : 
    1118             :         if( !bDefault )
    1119             :             WriteValue( 370, (int) floor(dfWidthInMM * 100 + 0.5) );
    1120             :     }
    1121             : 
    1122             : /* -------------------------------------------------------------------- */
    1123             : /*      Do we have a Linetype for the feature?                          */
    1124             : /* -------------------------------------------------------------------- */
    1125             :     CPLString osLineType = poFeature->GetFieldAsString( "Linetype" );
    1126             : 
    1127             :     if( !osLineType.empty()
    1128             :         && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL
    1129             :             || oNewLineTypes.count(osLineType) > 0 ) )
    1130             :     {
    1131             :         // Already define -> just reference it.
    1132             :         WriteValue( 6, osLineType );
    1133             :     }
    1134             :     else if( poTool != NULL && poTool->GetType() == OGRSTCPen )
    1135             :     {
    1136             :         CPLString osDefinition = PrepareLineTypeDefinition( poFeature,
    1137             :                                                             poTool );
    1138             : 
    1139             :         if( osDefinition != "" && osLineType == "" )
    1140             :         {
    1141             :             // Is this definition already created and named?
    1142             :             std::map<CPLString,CPLString>::iterator it;
    1143             : 
    1144             :             for( it = oNewLineTypes.begin();
    1145             :                  it != oNewLineTypes.end();
    1146             :                  it++ )
    1147             :             {
    1148             :                 if( (*it).second == osDefinition )
    1149             :                 {
    1150             :                     osLineType = (*it).first;
    1151             :                     break;
    1152             :                 }
    1153             :             }
    1154             : 
    1155             :             // create an automatic name for it.
    1156             :             if( osLineType == "" )
    1157             :             {
    1158             :                 do
    1159             :                 {
    1160             :                     osLineType.Printf( "AutoLineType-%d", nNextAutoID++ );
    1161             :                 }
    1162             :                 while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL );
    1163             :             }
    1164             :         }
    1165             : 
    1166             :         // If it isn't already defined, add it now.
    1167             :         if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 )
    1168             :         {
    1169             :             oNewLineTypes[osLineType] = osDefinition;
    1170             :             WriteValue( 6, osLineType );
    1171             :         }
    1172             :     }
    1173             :     delete poTool;
    1174             : #endif
    1175             : 
    1176             : /* -------------------------------------------------------------------- */
    1177             : /*      Process the loops (rings).                                      */
    1178             : /* -------------------------------------------------------------------- */
    1179          18 :     OGRPolygon *poPoly = poGeom->toPolygon();
    1180             : 
    1181          18 :     WriteValue( 91, poPoly->getNumInteriorRings() + 1 );
    1182             : 
    1183          37 :     for( auto&& poLR: *poPoly )
    1184             :     {
    1185          19 :         WriteValue( 92, 2 ); // Polyline
    1186          19 :         WriteValue( 72, 0 ); // has bulge
    1187          19 :         WriteValue( 73, 1 ); // is closed
    1188          19 :         WriteValue( 93, poLR->getNumPoints() );
    1189             : 
    1190         111 :         for( int iVert = 0; iVert < poLR->getNumPoints(); iVert++ )
    1191             :         {
    1192          92 :             WriteValue( 10, poLR->getX(iVert) );
    1193          92 :             WriteValue( 20, poLR->getY(iVert) );
    1194             :         }
    1195             : 
    1196          19 :         WriteValue( 97, 0 ); // 0 source boundary objects
    1197             :     }
    1198             : 
    1199          18 :     WriteValue( 75, 0 ); // hatch style = Hatch "odd parity" area (Normal style)
    1200          18 :     WriteValue( 76, 1 ); // hatch pattern type = predefined
    1201          18 :     WriteValue( 98, 0 ); // 0 seed points
    1202             : 
    1203          18 :     return OGRERR_NONE;
    1204             : 
    1205             : #ifdef notdef
    1206             : /* -------------------------------------------------------------------- */
    1207             : /*      Alternate unmaintained implementation as a polyline entity.     */
    1208             : /* -------------------------------------------------------------------- */
    1209             :     WriteValue( 0, "POLYLINE" );
    1210             :     WriteCore( poFeature );
    1211             :     WriteValue( 100, "AcDbEntity" );
    1212             :     WriteValue( 100, "AcDbPolyline" );
    1213             :     if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) )
    1214             :         WriteValue( 70, 1 );
    1215             :     else
    1216             :         WriteValue( 70, 0 );
    1217             :     WriteValue( 66, "1" );
    1218             : 
    1219             :     for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ )
    1220             :     {
    1221             :         WriteValue( 0, "VERTEX" );
    1222             :         WriteValue( 8, "0" );
    1223             :         WriteValue( 10, poLS->getX(iVert) );
    1224             :         if( !WriteValue( 20, poLS->getY(iVert) ) )
    1225             :             return OGRERR_FAILURE;
    1226             : 
    1227             :         if( poLS->getGeometryType() == wkbLineString25D )
    1228             :         {
    1229             :             if( !WriteValue( 30, poLS->getZ(iVert) ) )
    1230             :                 return OGRERR_FAILURE;
    1231             :         }
    1232             :     }
    1233             : 
    1234             :     WriteValue( 0, "SEQEND" );
    1235             :     WriteValue( 8, "0" );
    1236             : 
    1237             :     return OGRERR_NONE;
    1238             : #endif
    1239             : }
    1240             : 
    1241             : /************************************************************************/
    1242             : /*                           ICreateFeature()                            */
    1243             : /************************************************************************/
    1244             : 
    1245         109 : OGRErr OGRDXFWriterLayer::ICreateFeature( OGRFeature *poFeature )
    1246             : 
    1247             : {
    1248         109 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1249         109 :     OGRwkbGeometryType eGType = wkbNone;
    1250             : 
    1251         109 :     if( poGeom != nullptr )
    1252             :     {
    1253          76 :         if( !poGeom->IsEmpty() )
    1254             :         {
    1255          74 :             OGREnvelope sEnvelope;
    1256          74 :             poGeom->getEnvelope(&sEnvelope);
    1257          74 :             poDS->UpdateExtent(&sEnvelope);
    1258             :         }
    1259          76 :         eGType = wkbFlatten(poGeom->getGeometryType());
    1260             :     }
    1261             : 
    1262         109 :     if( eGType == wkbPoint )
    1263             :     {
    1264          19 :         const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
    1265             : 
    1266             :         // We don't want to treat as a blocks ref if the block is not defined
    1267          19 :         if( pszBlockName
    1268          19 :             && poDS->oHeaderDS.LookupBlock(pszBlockName) == nullptr )
    1269             :         {
    1270          34 :             if( poDS->poBlocksLayer == nullptr
    1271          17 :                 || poDS->poBlocksLayer->FindBlock(pszBlockName) == nullptr )
    1272          12 :                 pszBlockName = nullptr;
    1273             :         }
    1274             : 
    1275          19 :         if( pszBlockName != nullptr )
    1276           7 :             return WriteINSERT( poFeature );
    1277             : 
    1278          24 :         else if( poFeature->GetStyleString() != nullptr
    1279          12 :             && STARTS_WITH_CI(poFeature->GetStyleString(), "LABEL") )
    1280           1 :             return WriteTEXT( poFeature );
    1281             :         else
    1282          11 :             return WritePOINT( poFeature );
    1283             :     }
    1284          90 :     else if( eGType == wkbLineString
    1285          68 :              || eGType == wkbMultiLineString )
    1286          26 :         return WritePOLYLINE( poFeature );
    1287             : 
    1288          64 :     else if( eGType == wkbPolygon
    1289          51 :              || eGType == wkbTriangle
    1290          50 :              || eGType == wkbMultiPolygon)
    1291             :     {
    1292          18 :         if( bWriteHatch )
    1293          18 :             return WriteHATCH( poFeature );
    1294             :         else
    1295           0 :             return WritePOLYLINE( poFeature );
    1296             :     }
    1297             : 
    1298             :     // Explode geometry collections into multiple entities.
    1299          46 :     else if( eGType == wkbGeometryCollection )
    1300             :     {
    1301             :         OGRGeometryCollection *poGC =
    1302           8 :             poFeature->StealGeometry()->toGeometryCollection();
    1303          26 :         for( auto&& poMember: poGC )
    1304             :         {
    1305          19 :             poFeature->SetGeometry( poMember );
    1306             : 
    1307          19 :             OGRErr eErr = CreateFeature( poFeature );
    1308             : 
    1309          19 :             if( eErr != OGRERR_NONE )
    1310             :             {
    1311           1 :                 delete poGC;
    1312           1 :                 return eErr;
    1313             :             }
    1314             :         }
    1315             : 
    1316           7 :         poFeature->SetGeometryDirectly( poGC );
    1317           7 :         return OGRERR_NONE;
    1318             :     }
    1319             :     else
    1320             :     {
    1321             :         CPLError( CE_Failure, CPLE_AppDefined,
    1322             :                   "No known way to write feature with geometry '%s'.",
    1323          38 :                   OGRGeometryTypeToName(eGType) );
    1324          38 :         return OGRERR_FAILURE;
    1325             :     }
    1326             : }
    1327             : 
    1328             : /************************************************************************/
    1329             : /*                       ColorStringToDXFColor()                        */
    1330             : /************************************************************************/
    1331             : 
    1332           7 : int OGRDXFWriterLayer::ColorStringToDXFColor( const char *pszRGB )
    1333             : 
    1334             : {
    1335             : /* -------------------------------------------------------------------- */
    1336             : /*      Parse the RGB string.                                           */
    1337             : /* -------------------------------------------------------------------- */
    1338           7 :     if( pszRGB == nullptr )
    1339           0 :         return -1;
    1340             : 
    1341           7 :     unsigned int nRed = 0;
    1342           7 :     unsigned int nGreen = 0;
    1343           7 :     unsigned int nBlue = 0;
    1344           7 :     unsigned int nTransparency = 255;
    1345             : 
    1346             :     const int nCount =
    1347           7 :         sscanf(pszRGB, "#%2x%2x%2x%2x", &nRed, &nGreen, &nBlue, &nTransparency);
    1348             : 
    1349           7 :     if (nCount < 3 )
    1350           0 :         return -1;
    1351             : 
    1352             : /* -------------------------------------------------------------------- */
    1353             : /*      Find near color in DXF palette.                                 */
    1354             : /* -------------------------------------------------------------------- */
    1355           7 :     const unsigned char *pabyDXFColors = ACGetColorTable();
    1356           7 :     int nMinDist = 768;
    1357           7 :     int nBestColor = -1;
    1358             : 
    1359        1792 :     for( int i = 1; i < 256; i++ )
    1360             :     {
    1361             :         const int nDist =
    1362        1785 :             std::abs(static_cast<int>(nRed) - pabyDXFColors[i*3+0])
    1363        1785 :             + std::abs(static_cast<int>(nGreen) - pabyDXFColors[i*3+1])
    1364        1785 :             + std::abs(static_cast<int>(nBlue)  - pabyDXFColors[i*3+2]);
    1365             : 
    1366        1785 :         if( nDist < nMinDist )
    1367             :         {
    1368          12 :             nBestColor = i;
    1369          12 :             nMinDist = nDist;
    1370             :         }
    1371             :     }
    1372             : 
    1373           7 :     return nBestColor;
    1374             : }

Generated by: LCOV version 1.10