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