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