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