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