1 /******************************************************************************
2  * $Id: ogrdxfwriterds.cpp 27945 2014-11-11 01:33:15Z rouault $
3  *
4  * Project:  DXF Translator
5  * Purpose:  Implements OGRDXFWriterDS - the OGRDataSource class used for
6  *           writing a DXF file.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
11  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at mines-paris dot org>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "ogr_dxf.h"
33 #include "cpl_conv.h"
34 #include "cpl_string.h"
35 
36 CPL_CVSID("$Id: ogrdxfwriterds.cpp 27945 2014-11-11 01:33:15Z rouault $");
37 
38 /************************************************************************/
39 /*                          OGRDXFWriterDS()                          */
40 /************************************************************************/
41 
OGRDXFWriterDS()42 OGRDXFWriterDS::OGRDXFWriterDS()
43 
44 {
45     fp = NULL;
46     fpTemp = NULL;
47     poLayer = NULL;
48     poBlocksLayer = NULL;
49     papszLayersToCreate = NULL;
50     nNextFID = 80;
51     nHANDSEEDOffset = 0;
52 }
53 
54 /************************************************************************/
55 /*                         ~OGRDXFWriterDS()                          */
56 /************************************************************************/
57 
~OGRDXFWriterDS()58 OGRDXFWriterDS::~OGRDXFWriterDS()
59 
60 {
61     if (fp != NULL)
62     {
63 /* -------------------------------------------------------------------- */
64 /*      Transfer over the header into the destination file with any     */
65 /*      adjustments or insertions needed.                               */
66 /* -------------------------------------------------------------------- */
67         CPLDebug( "DXF", "Compose final DXF file from components." );
68 
69         TransferUpdateHeader( fp );
70 
71         if (fpTemp != NULL)
72         {
73 /* -------------------------------------------------------------------- */
74 /*      Copy in the temporary file contents.                            */
75 /* -------------------------------------------------------------------- */
76             const char *pszLine;
77 
78             VSIFCloseL(fpTemp);
79             fpTemp = VSIFOpenL( osTempFilename, "r" );
80 
81             while( (pszLine = CPLReadLineL(fpTemp)) != NULL )
82             {
83                 VSIFWriteL( pszLine, 1, strlen(pszLine), fp );
84                 VSIFWriteL( "\n", 1, 1, fp );
85             }
86 
87 /* -------------------------------------------------------------------- */
88 /*      Cleanup temporary file.                                         */
89 /* -------------------------------------------------------------------- */
90             VSIFCloseL( fpTemp );
91             VSIUnlink( osTempFilename );
92         }
93 
94 /* -------------------------------------------------------------------- */
95 /*      Write trailer.                                                  */
96 /* -------------------------------------------------------------------- */
97         if( osTrailerFile != "" )
98             TransferUpdateTrailer( fp );
99 
100 /* -------------------------------------------------------------------- */
101 /*      Fixup the HANDSEED value now that we know all the entity ids    */
102 /*      created.                                                        */
103 /* -------------------------------------------------------------------- */
104         FixupHANDSEED( fp );
105 
106 /* -------------------------------------------------------------------- */
107 /*      Close file.                                                     */
108 /* -------------------------------------------------------------------- */
109 
110         VSIFCloseL( fp );
111         fp = NULL;
112     }
113 
114 /* -------------------------------------------------------------------- */
115 /*      Destroy layers.                                                 */
116 /* -------------------------------------------------------------------- */
117     delete poLayer;
118     delete poBlocksLayer;
119 
120     CSLDestroy(papszLayersToCreate);
121 }
122 
123 /************************************************************************/
124 /*                           TestCapability()                           */
125 /************************************************************************/
126 
TestCapability(const char * pszCap)127 int OGRDXFWriterDS::TestCapability( const char * pszCap )
128 
129 {
130     if( EQUAL(pszCap,ODsCCreateLayer) )
131         // Unable to have more than one OGR entities layer in a DXF file, with one options blocks layer.
132         return poBlocksLayer == NULL || poLayer == NULL;
133     else
134         return FALSE;
135 }
136 
137 /************************************************************************/
138 /*                              GetLayer()                              */
139 /************************************************************************/
140 
141 
GetLayer(int iLayer)142 OGRLayer *OGRDXFWriterDS::GetLayer( int iLayer )
143 
144 {
145     if( iLayer == 0 )
146         return poLayer;
147     else
148         return NULL;
149 }
150 
151 /************************************************************************/
152 /*                           GetLayerCount()                            */
153 /************************************************************************/
154 
GetLayerCount()155 int OGRDXFWriterDS::GetLayerCount()
156 
157 {
158     if( poLayer )
159         return 1;
160     else
161         return 0;
162 }
163 
164 /************************************************************************/
165 /*                                Open()                                */
166 /************************************************************************/
167 
Open(const char * pszFilename,char ** papszOptions)168 int OGRDXFWriterDS::Open( const char * pszFilename, char **papszOptions )
169 
170 {
171 /* -------------------------------------------------------------------- */
172 /*      Open the standard header, or a user provided header.            */
173 /* -------------------------------------------------------------------- */
174     if( CSLFetchNameValue(papszOptions,"HEADER") != NULL )
175         osHeaderFile = CSLFetchNameValue(papszOptions,"HEADER");
176     else
177     {
178         const char *pszValue = CPLFindFile( "gdal", "header.dxf" );
179         if( pszValue == NULL )
180         {
181             CPLError( CE_Failure, CPLE_OpenFailed,
182                       "Failed to find template header file header.dxf for reading,\nis GDAL_DATA set properly?" );
183             return FALSE;
184         }
185         osHeaderFile = pszValue;
186     }
187 
188 /* -------------------------------------------------------------------- */
189 /*      Establish the name for our trailer file.                        */
190 /* -------------------------------------------------------------------- */
191     if( CSLFetchNameValue(papszOptions,"TRAILER") != NULL )
192         osTrailerFile = CSLFetchNameValue(papszOptions,"TRAILER");
193     else
194     {
195         const char *pszValue = CPLFindFile( "gdal", "trailer.dxf" );
196         if( pszValue != NULL )
197             osTrailerFile = pszValue;
198     }
199 
200 /* -------------------------------------------------------------------- */
201 /*      What entity id do we want to start with when writing?  Small    */
202 /*      values run a risk of overlapping some undetected entity in      */
203 /*      the header or trailer despite the prescanning we do.            */
204 /* -------------------------------------------------------------------- */
205 #ifdef DEBUG
206     nNextFID = 80;
207 #else
208     nNextFID = 131072;
209 #endif
210 
211     if( CSLFetchNameValue( papszOptions, "FIRST_ENTITY" ) != NULL )
212         nNextFID = atoi(CSLFetchNameValue( papszOptions, "FIRST_ENTITY" ));
213 
214 /* -------------------------------------------------------------------- */
215 /*      Prescan the header and trailer for entity codes.                */
216 /* -------------------------------------------------------------------- */
217     ScanForEntities( osHeaderFile, "HEADER" );
218     ScanForEntities( osTrailerFile, "TRAILER" );
219 
220 /* -------------------------------------------------------------------- */
221 /*      Attempt to read the template header file so we have a list      */
222 /*      of layers, linestyles and blocks.                               */
223 /* -------------------------------------------------------------------- */
224     if( !oHeaderDS.Open( osHeaderFile, TRUE ) )
225         return FALSE;
226 
227 /* -------------------------------------------------------------------- */
228 /*      Create the output file.                                         */
229 /* -------------------------------------------------------------------- */
230     fp = VSIFOpenL( pszFilename, "w+" );
231 
232     if( fp == NULL )
233     {
234         CPLError( CE_Failure, CPLE_OpenFailed,
235                   "Failed to open '%s' for writing.",
236                   pszFilename );
237         return FALSE;
238     }
239 
240 /* -------------------------------------------------------------------- */
241 /*      Establish the temporary file.                                   */
242 /* -------------------------------------------------------------------- */
243     osTempFilename = pszFilename;
244     osTempFilename += ".tmp";
245 
246     fpTemp = VSIFOpenL( osTempFilename, "w" );
247     if( fpTemp == NULL )
248     {
249         CPLError( CE_Failure, CPLE_OpenFailed,
250                   "Failed to open '%s' for writing.",
251                   osTempFilename.c_str() );
252         return FALSE;
253     }
254 
255     return TRUE;
256 }
257 
258 /************************************************************************/
259 /*                           ICreateLayer()                             */
260 /************************************************************************/
261 
ICreateLayer(const char * pszName,OGRSpatialReference *,OGRwkbGeometryType,char **)262 OGRLayer *OGRDXFWriterDS::ICreateLayer( const char *pszName,
263                                        OGRSpatialReference *,
264                                        OGRwkbGeometryType,
265                                        char ** )
266 
267 {
268     if( EQUAL(pszName,"blocks") && poBlocksLayer == NULL )
269     {
270         poBlocksLayer = new OGRDXFBlocksWriterLayer( this );
271         return poBlocksLayer;
272     }
273     else if( poLayer == NULL )
274     {
275         poLayer = new OGRDXFWriterLayer( this, fpTemp );
276         return poLayer;
277     }
278     else
279     {
280         CPLError( CE_Failure, CPLE_AppDefined,
281                   "Unable to have more than one OGR entities layer in a DXF file, with one options blocks layer." );
282         return NULL;
283     }
284 }
285 
286 /************************************************************************/
287 /*                             WriteValue()                             */
288 /************************************************************************/
289 
WriteValue(VSILFILE * fp,int nCode,const char * pszLine)290 static int WriteValue( VSILFILE *fp, int nCode, const char *pszLine )
291 
292 {
293     char szLinePair[300];
294 
295     snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine );
296     size_t nLen = strlen(szLinePair);
297     if( VSIFWriteL( szLinePair, 1, nLen, fp ) != nLen )
298     {
299         CPLError( CE_Failure, CPLE_FileIO,
300                   "Attempt to write line to DXF file failed, disk full?." );
301         return FALSE;
302     }
303     else
304         return TRUE;
305 }
306 
307 /************************************************************************/
308 /*                             WriteValue()                             */
309 /************************************************************************/
310 
WriteValue(VSILFILE * fp,int nCode,double dfValue)311 static int WriteValue( VSILFILE *fp, int nCode, double dfValue )
312 
313 {
314     char szLinePair[64];
315 
316     CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue );
317     size_t nLen = strlen(szLinePair);
318     if( VSIFWriteL( szLinePair, 1, nLen, fp ) != nLen )
319     {
320         CPLError( CE_Failure, CPLE_FileIO,
321                   "Attempt to write line to DXF file failed, disk full?." );
322         return FALSE;
323     }
324     else
325         return TRUE;
326 }
327 /************************************************************************/
328 /*                        TransferUpdateHeader()                        */
329 /************************************************************************/
330 
TransferUpdateHeader(VSILFILE * fpOut)331 int OGRDXFWriterDS::TransferUpdateHeader( VSILFILE *fpOut )
332 
333 {
334     oHeaderDS.ResetReadPointer( 0 );
335 
336 /* -------------------------------------------------------------------- */
337 /*      Copy header, inserting in new objects as needed.                */
338 /* -------------------------------------------------------------------- */
339     char szLineBuf[257];
340     int nCode;
341     CPLString osSection, osTable, osEntity;
342 
343     while( (nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1
344            && osSection != "ENTITIES" )
345     {
346         if( nCode == 0 && EQUAL(szLineBuf,"ENDTAB") )
347         {
348             // If we are at the end of the LAYER TABLE consider inserting
349             // missing definitions.
350             if( osTable == "LAYER" )
351             {
352                 if( !WriteNewLayerDefinitions( fp ) )
353                     return FALSE;
354             }
355 
356             // If at the end of the BLOCK_RECORD TABLE consider inserting
357             // missing definitions.
358             if( osTable == "BLOCK_RECORD" && poBlocksLayer )
359             {
360                 if( !WriteNewBlockRecords( fp ) )
361                     return FALSE;
362             }
363 
364             // If at the end of the LTYPE TABLE consider inserting
365             // missing layer type definitions.
366             if( osTable == "LTYPE" )
367             {
368                 if( !WriteNewLineTypeRecords( fp ) )
369                     return FALSE;
370             }
371 
372             osTable = "";
373         }
374 
375         // If we are at the end of the BLOCKS section, consider inserting
376         // suplementary blocks.
377         if( nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf,"ENDSEC")
378             && poBlocksLayer != NULL )
379         {
380             if( !WriteNewBlockDefinitions( fp ) )
381                 return FALSE;
382         }
383 
384         // We need to keep track of where $HANDSEED is so that we can
385         // come back and fix it up when we have generated all entity ids.
386         if( nCode == 9 && EQUAL(szLineBuf,"$HANDSEED") )
387         {
388             if( !WriteValue( fpOut, nCode, szLineBuf ) )
389                 return FALSE;
390 
391             nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
392 
393             // ensure we have room to overwrite with a longer value.
394             while( strlen(szLineBuf) < 8 )
395             {
396                 memmove( szLineBuf+1, szLineBuf, strlen(szLineBuf)+1 );
397                 szLineBuf[0] = '0';
398             }
399 
400             nHANDSEEDOffset = VSIFTellL( fpOut );
401         }
402 
403         // Patch EXTMIN with minx and miny
404         if( nCode == 9 && EQUAL(szLineBuf,"$EXTMIN") )
405         {
406             if( !WriteValue( fpOut, nCode, szLineBuf ) )
407                 return FALSE;
408 
409             nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
410             if (nCode == 10)
411             {
412                 if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinX ) )
413                     return FALSE;
414 
415                 nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
416                 if (nCode == 20)
417                 {
418                     if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinY ) )
419                         return FALSE;
420 
421                     continue;
422                 }
423             }
424         }
425 
426         // Patch EXTMAX with maxx and maxy
427         if( nCode == 9 && EQUAL(szLineBuf,"$EXTMAX") )
428         {
429             if( !WriteValue( fpOut, nCode, szLineBuf ) )
430                 return FALSE;
431 
432             nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
433             if (nCode == 10)
434             {
435                 if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxX ) )
436                     return FALSE;
437 
438                 nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
439                 if (nCode == 20)
440                 {
441                     if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxY ) )
442                         return FALSE;
443 
444                     continue;
445                 }
446             }
447         }
448 
449         // Copy over the source line.
450         if( !WriteValue( fpOut, nCode, szLineBuf ) )
451             return FALSE;
452 
453         // Track what entity we are in - that is the last "code 0" object.
454         if( nCode == 0  )
455             osEntity = szLineBuf;
456 
457         // Track what section we are in.
458         if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
459         {
460             nCode = oHeaderDS.ReadValue( szLineBuf );
461             if( nCode == -1 )
462                 break;
463 
464             if( !WriteValue( fpOut, nCode, szLineBuf ) )
465                 return FALSE;
466 
467             osSection = szLineBuf;
468         }
469 
470         // Track what TABLE we are in.
471         if( nCode == 0 && EQUAL(szLineBuf,"TABLE") )
472         {
473             nCode = oHeaderDS.ReadValue( szLineBuf );
474             if( !WriteValue( fpOut, nCode, szLineBuf ) )
475                 return FALSE;
476 
477             osTable = szLineBuf;
478         }
479 
480         // If we are starting the first layer, then capture
481         // the layer contents while copying so we can duplicate
482         // it for any new layer definitions.
483         if( nCode == 0 && EQUAL(szLineBuf,"LAYER")
484             && osTable == "LAYER" && aosDefaultLayerText.size() == 0 )
485         {
486             do {
487                 anDefaultLayerCode.push_back( nCode );
488                 aosDefaultLayerText.push_back( szLineBuf );
489 
490                 if( nCode != 0 && !WriteValue( fpOut, nCode, szLineBuf ) )
491                     return FALSE;
492 
493                 nCode = oHeaderDS.ReadValue( szLineBuf );
494 
495                 if( nCode == 2 && !EQUAL(szLineBuf,"0") )
496                 {
497                     anDefaultLayerCode.resize(0);
498                     aosDefaultLayerText.resize(0);
499                     break;
500                 }
501             } while( nCode != 0 );
502 
503             oHeaderDS.UnreadValue();
504         }
505     }
506 
507     return TRUE;
508 }
509 
510 /************************************************************************/
511 /*                       TransferUpdateTrailer()                        */
512 /************************************************************************/
513 
TransferUpdateTrailer(VSILFILE * fpOut)514 int OGRDXFWriterDS::TransferUpdateTrailer( VSILFILE *fpOut )
515 {
516     OGRDXFReader oReader;
517     VSILFILE *fp;
518 
519 /* -------------------------------------------------------------------- */
520 /*      Open the file and setup a reader.                               */
521 /* -------------------------------------------------------------------- */
522     fp = VSIFOpenL( osTrailerFile, "r" );
523 
524     if( fp == NULL )
525         return FALSE;
526 
527     oReader.Initialize( fp );
528 
529 /* -------------------------------------------------------------------- */
530 /*      Scan ahead to find the OBJECTS section.                         */
531 /* -------------------------------------------------------------------- */
532     char szLineBuf[257];
533     int  nCode;
534 
535     while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
536     {
537         if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
538         {
539             nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) );
540             if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") )
541                 break;
542         }
543     }
544 
545     if( nCode == -1 )
546     {
547         CPLError( CE_Failure, CPLE_AppDefined,
548                   "Failed to find OBJECTS section in trailer file '%s'.",
549                   osTrailerFile.c_str() );
550         return FALSE;
551     }
552 
553 /* -------------------------------------------------------------------- */
554 /*      Insert the "end of section" for ENTITIES, and the start of      */
555 /*      the OBJECTS section.                                            */
556 /* -------------------------------------------------------------------- */
557     WriteValue( fpOut, 0, "ENDSEC" );
558     WriteValue( fpOut, 0, "SECTION" );
559     WriteValue( fpOut, 2, "OBJECTS" );
560 
561 /* -------------------------------------------------------------------- */
562 /*      Copy the remainder of the file.                                 */
563 /* -------------------------------------------------------------------- */
564     while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
565     {
566         if( !WriteValue( fpOut, nCode, szLineBuf ) )
567         {
568             VSIFCloseL( fp );
569             return FALSE;
570         }
571     }
572 
573     VSIFCloseL( fp );
574 
575     return TRUE;
576 }
577 
578 /************************************************************************/
579 /*                           FixupHANDSEED()                            */
580 /*                                                                      */
581 /*      Fixup the next entity id information in the $HANDSEED header    */
582 /*      variable.                                                       */
583 /************************************************************************/
584 
FixupHANDSEED(VSILFILE * fp)585 int OGRDXFWriterDS::FixupHANDSEED( VSILFILE *fp )
586 
587 {
588 /* -------------------------------------------------------------------- */
589 /*      What is a good next handle seed?  Scan existing values.         */
590 /* -------------------------------------------------------------------- */
591     unsigned int   nHighestHandle = 0;
592     std::set<CPLString>::iterator it;
593 
594     for( it = aosUsedEntities.begin(); it != aosUsedEntities.end(); it++ )
595     {
596         unsigned int nHandle;
597         if( sscanf( (*it).c_str(), "%x", &nHandle ) == 1 )
598         {
599             if( nHandle > nHighestHandle )
600                 nHighestHandle = nHandle;
601         }
602     }
603 
604 /* -------------------------------------------------------------------- */
605 /*      Read the existing handseed value, replace it, and write back.   */
606 /* -------------------------------------------------------------------- */
607     char szWorkBuf[30];
608     int  i = 0;
609 
610     if( nHANDSEEDOffset == 0 )
611         return FALSE;
612 
613     VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET );
614     VSIFReadL( szWorkBuf, 1, sizeof(szWorkBuf), fp );
615 
616     while( szWorkBuf[i] != '\n' )
617         i++;
618 
619     i++;
620     if( szWorkBuf[i] == '\r' )
621         i++;
622 
623     CPLString osNewValue;
624 
625     osNewValue.Printf( "%08X", nHighestHandle + 1 );
626     strncpy( szWorkBuf + i, osNewValue.c_str(), osNewValue.size() );
627 
628     VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET );
629     VSIFWriteL( szWorkBuf, 1, sizeof(szWorkBuf), fp );
630 
631     return TRUE;
632 }
633 
634 /************************************************************************/
635 /*                      WriteNewLayerDefinitions()                      */
636 /************************************************************************/
637 
WriteNewLayerDefinitions(VSILFILE * fpOut)638 int  OGRDXFWriterDS::WriteNewLayerDefinitions( VSILFILE * fpOut )
639 
640 {
641     int iLayer, nNewLayers = CSLCount(papszLayersToCreate);
642 
643     for( iLayer = 0; iLayer < nNewLayers; iLayer++ )
644     {
645         for( unsigned i = 0; i < aosDefaultLayerText.size(); i++ )
646         {
647             if( anDefaultLayerCode[i] == 2 )
648             {
649                 if( !WriteValue( fpOut, 2, papszLayersToCreate[iLayer] ) )
650                     return FALSE;
651             }
652             else if( anDefaultLayerCode[i] == 5 )
653             {
654                 WriteEntityID( fpOut );
655             }
656             else
657             {
658                 if( !WriteValue( fpOut,
659                                  anDefaultLayerCode[i],
660                                  aosDefaultLayerText[i] ) )
661                     return FALSE;
662             }
663         }
664     }
665 
666     return TRUE;
667 }
668 
669 /************************************************************************/
670 /*                      WriteNewLineTypeRecords()                       */
671 /************************************************************************/
672 
WriteNewLineTypeRecords(VSILFILE * fp)673 int OGRDXFWriterDS::WriteNewLineTypeRecords( VSILFILE *fp )
674 
675 {
676     if( poLayer == NULL )
677         return TRUE;
678 
679     std::map<CPLString,CPLString>::iterator oIt;
680     std::map<CPLString,CPLString>& oNewLineTypes =
681         poLayer->GetNewLineTypeMap();
682 
683     for( oIt = oNewLineTypes.begin();
684          oIt != oNewLineTypes.end(); oIt++ )
685     {
686         WriteValue( fp, 0, "LTYPE" );
687         WriteEntityID( fp );
688         WriteValue( fp, 100, "AcDbSymbolTableRecord" );
689         WriteValue( fp, 100, "AcDbLinetypeTableRecord" );
690         WriteValue( fp, 2, (*oIt).first );
691         WriteValue( fp, 70, "0" );
692         WriteValue( fp, 3, "" );
693         WriteValue( fp, 72, "65" );
694         VSIFWriteL( (*oIt).second.c_str(), 1, (*oIt).second.size(), fp );
695 
696         CPLDebug( "DXF", "Define Line type '%s'.",
697                   (*oIt).first.c_str() );
698     }
699 
700     return TRUE;
701 }
702 
703 /************************************************************************/
704 /*                        WriteNewBlockRecords()                        */
705 /************************************************************************/
706 
WriteNewBlockRecords(VSILFILE * fp)707 int OGRDXFWriterDS::WriteNewBlockRecords( VSILFILE * fp )
708 
709 {
710     std::set<CPLString> aosAlreadyHandled;
711 
712 /* ==================================================================== */
713 /*      Loop over all block objects written via the blocks layer.       */
714 /* ==================================================================== */
715     for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ )
716     {
717         OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
718 
719 /* -------------------------------------------------------------------- */
720 /*      Is this block already defined in the template header?           */
721 /* -------------------------------------------------------------------- */
722         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName");
723 
724         if( oHeaderDS.LookupBlock( osBlockName ) != NULL )
725             continue;
726 
727 /* -------------------------------------------------------------------- */
728 /*      Have we already written a BLOCK_RECORD for this block?          */
729 /* -------------------------------------------------------------------- */
730         if( aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end() )
731             continue;
732 
733         aosAlreadyHandled.insert( osBlockName );
734 
735 /* -------------------------------------------------------------------- */
736 /*      Write the block record.                                         */
737 /* -------------------------------------------------------------------- */
738         WriteValue( fp, 0, "BLOCK_RECORD" );
739         WriteEntityID( fp );
740         WriteValue( fp, 100, "AcDbSymbolTableRecord" );
741         WriteValue( fp, 100, "AcDbBlockTableRecord" );
742         WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") );
743         if( !WriteValue( fp, 340, "0" ) )
744             return FALSE;
745     }
746 
747     return TRUE;
748 }
749 
750 /************************************************************************/
751 /*                      WriteNewBlockDefinitions()                      */
752 /************************************************************************/
753 
WriteNewBlockDefinitions(VSILFILE * fp)754 int OGRDXFWriterDS::WriteNewBlockDefinitions( VSILFILE * fp )
755 
756 {
757     poLayer->ResetFP( fp );
758 
759 /* ==================================================================== */
760 /*      Loop over all block objects written via the blocks layer.       */
761 /* ==================================================================== */
762     for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ )
763     {
764         OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
765 
766 /* -------------------------------------------------------------------- */
767 /*      Is this block already defined in the template header?           */
768 /* -------------------------------------------------------------------- */
769         CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName");
770 
771         if( oHeaderDS.LookupBlock( osBlockName ) != NULL )
772             continue;
773 
774 /* -------------------------------------------------------------------- */
775 /*      Write the block definition preamble.                            */
776 /* -------------------------------------------------------------------- */
777         CPLDebug( "DXF", "Writing BLOCK definition for '%s'.",
778                   poThisBlockFeat->GetFieldAsString("BlockName") );
779 
780         WriteValue( fp, 0, "BLOCK" );
781         WriteEntityID( fp );
782         WriteValue( fp, 100, "AcDbEntity" );
783         if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 )
784             WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") );
785         else
786             WriteValue( fp, 8, "0" );
787         WriteValue( fp, 100, "AcDbBlockBegin" );
788         WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") );
789         WriteValue( fp, 70, "0" );
790 
791         // Origin
792         WriteValue( fp, 10, "0.0" );
793         WriteValue( fp, 20, "0.0" );
794         WriteValue( fp, 30, "0.0" );
795 
796         WriteValue( fp, 3, poThisBlockFeat->GetFieldAsString("BlockName") );
797         WriteValue( fp, 1, "" );
798 
799 /* -------------------------------------------------------------------- */
800 /*      Write out the feature entities.                                 */
801 /* -------------------------------------------------------------------- */
802         if( poLayer->CreateFeature( poThisBlockFeat ) != OGRERR_NONE )
803             return FALSE;
804 
805 /* -------------------------------------------------------------------- */
806 /*      Write out following features if they are the same block.        */
807 /* -------------------------------------------------------------------- */
808         while( iBlock < poBlocksLayer->apoBlocks.size()-1
809             && EQUAL(poBlocksLayer->apoBlocks[iBlock+1]->GetFieldAsString("BlockName"),
810                      osBlockName) )
811         {
812             iBlock++;
813 
814             if( poLayer->CreateFeature( poBlocksLayer->apoBlocks[iBlock] )
815                 != OGRERR_NONE )
816                 return FALSE;
817         }
818 
819 /* -------------------------------------------------------------------- */
820 /*      Write out the block definition postamble.                       */
821 /* -------------------------------------------------------------------- */
822         WriteValue( fp, 0, "ENDBLK" );
823         WriteEntityID( fp );
824         WriteValue( fp, 100, "AcDbEntity" );
825         if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 )
826             WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") );
827         else
828             WriteValue( fp, 8, "0" );
829         WriteValue( fp, 100, "AcDbBlockEnd" );
830     }
831 
832     return TRUE;
833 }
834 
835 /************************************************************************/
836 /*                          ScanForEntities()                           */
837 /*                                                                      */
838 /*      Scan the indicated file for entity ids ("5" records) and        */
839 /*      build them up as a set so we can be careful to avoid            */
840 /*      creating new entities with conflicting ids.                     */
841 /************************************************************************/
842 
ScanForEntities(const char * pszFilename,const char * pszTarget)843 void OGRDXFWriterDS::ScanForEntities( const char *pszFilename,
844                                       const char *pszTarget )
845 
846 {
847     OGRDXFReader oReader;
848     VSILFILE *fp;
849 
850 /* -------------------------------------------------------------------- */
851 /*      Open the file and setup a reader.                               */
852 /* -------------------------------------------------------------------- */
853     fp = VSIFOpenL( pszFilename, "r" );
854 
855     if( fp == NULL )
856         return;
857 
858     oReader.Initialize( fp );
859 
860 /* -------------------------------------------------------------------- */
861 /*      Add every code "5" line to our entities list.                   */
862 /* -------------------------------------------------------------------- */
863     char szLineBuf[257];
864     int  nCode;
865     const char *pszPortion = "HEADER";
866 
867     while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
868     {
869         if( (nCode == 5 || nCode == 105) && EQUAL(pszTarget,pszPortion) )
870         {
871             CPLString osEntity( szLineBuf );
872 
873             if( CheckEntityID( osEntity ) )
874                 CPLDebug( "DXF", "Encounted entity '%s' multiple times.",
875                           osEntity.c_str() );
876             else
877                 aosUsedEntities.insert( osEntity );
878         }
879 
880         if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
881         {
882             nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) );
883             if( nCode == 2 && EQUAL(szLineBuf,"ENTITIES") )
884                 pszPortion = "BODY";
885             if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") )
886                 pszPortion = "TRAILER";
887         }
888     }
889 
890     VSIFCloseL( fp );
891 }
892 
893 /************************************************************************/
894 /*                           CheckEntityID()                            */
895 /*                                                                      */
896 /*      Does the mentioned entity already exist?                        */
897 /************************************************************************/
898 
CheckEntityID(const char * pszEntityID)899 int OGRDXFWriterDS::CheckEntityID( const char *pszEntityID )
900 
901 {
902     std::set<CPLString>::iterator it;
903 
904     it = aosUsedEntities.find( pszEntityID );
905     if( it != aosUsedEntities.end() )
906         return TRUE;
907     else
908         return FALSE;
909 }
910 
911 /************************************************************************/
912 /*                           WriteEntityID()                            */
913 /************************************************************************/
914 
WriteEntityID(VSILFILE * fp,long nPreferredFID)915 long OGRDXFWriterDS::WriteEntityID( VSILFILE *fp, long nPreferredFID )
916 
917 {
918     CPLString osEntityID;
919 
920     if( nPreferredFID != OGRNullFID )
921     {
922 
923         osEntityID.Printf( "%X", (unsigned int) nPreferredFID );
924         if( !CheckEntityID( osEntityID ) )
925         {
926             aosUsedEntities.insert( osEntityID );
927             WriteValue( fp, 5, osEntityID );
928             return nPreferredFID;
929         }
930     }
931 
932     do
933     {
934         osEntityID.Printf( "%X", nNextFID++ );
935     }
936     while( CheckEntityID( osEntityID ) );
937 
938     aosUsedEntities.insert( osEntityID );
939     WriteValue( fp, 5, osEntityID );
940 
941     return nNextFID - 1;
942 }
943 
944 /************************************************************************/
945 /*                           UpdateExtent()                             */
946 /************************************************************************/
947 
UpdateExtent(OGREnvelope * psEnvelope)948 void OGRDXFWriterDS::UpdateExtent( OGREnvelope* psEnvelope )
949 {
950     oGlobalEnvelope.Merge(*psEnvelope);
951 }
952