1 : /******************************************************************************
2 : * $Id: ogrdxfwriterds.cpp 24359 2012-05-01 17:01:22Z 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 : *
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 24359 2012-05-01 17:01:22Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRDXFWriterDS() */
39 : /************************************************************************/
40 :
41 7 : OGRDXFWriterDS::OGRDXFWriterDS()
42 :
43 : {
44 7 : fp = NULL;
45 7 : fpTemp = NULL;
46 7 : poLayer = NULL;
47 7 : poBlocksLayer = NULL;
48 7 : papszLayersToCreate = NULL;
49 7 : nNextFID = 80;
50 7 : nHANDSEEDOffset = 0;
51 7 : }
52 :
53 : /************************************************************************/
54 : /* ~OGRDXFWriterDS() */
55 : /************************************************************************/
56 :
57 7 : OGRDXFWriterDS::~OGRDXFWriterDS()
58 :
59 : {
60 7 : if (fp != NULL)
61 : {
62 : /* -------------------------------------------------------------------- */
63 : /* Transfer over the header into the destination file with any */
64 : /* adjustments or insertions needed. */
65 : /* -------------------------------------------------------------------- */
66 7 : CPLDebug( "DXF", "Compose final DXF file from components." );
67 :
68 7 : TransferUpdateHeader( fp );
69 :
70 7 : if (fpTemp != NULL)
71 : {
72 : /* -------------------------------------------------------------------- */
73 : /* Copy in the temporary file contents. */
74 : /* -------------------------------------------------------------------- */
75 : const char *pszLine;
76 :
77 7 : VSIFCloseL(fpTemp);
78 7 : fpTemp = VSIFOpenL( osTempFilename, "r" );
79 :
80 474 : while( (pszLine = CPLReadLineL(fpTemp)) != NULL )
81 : {
82 460 : VSIFWriteL( pszLine, 1, strlen(pszLine), fp );
83 460 : VSIFWriteL( "\n", 1, 1, fp );
84 : }
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Cleanup temporary file. */
88 : /* -------------------------------------------------------------------- */
89 7 : VSIFCloseL( fpTemp );
90 7 : VSIUnlink( osTempFilename );
91 : }
92 :
93 : /* -------------------------------------------------------------------- */
94 : /* Write trailer. */
95 : /* -------------------------------------------------------------------- */
96 7 : if( osTrailerFile != "" )
97 7 : TransferUpdateTrailer( fp );
98 :
99 : /* -------------------------------------------------------------------- */
100 : /* Fixup the HANDSEED value now that we know all the entity ids */
101 : /* created. */
102 : /* -------------------------------------------------------------------- */
103 7 : FixupHANDSEED( fp );
104 :
105 : /* -------------------------------------------------------------------- */
106 : /* Close file. */
107 : /* -------------------------------------------------------------------- */
108 :
109 7 : VSIFCloseL( fp );
110 7 : fp = NULL;
111 : }
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Destroy layers. */
115 : /* -------------------------------------------------------------------- */
116 7 : delete poLayer;
117 7 : delete poBlocksLayer;
118 :
119 7 : CSLDestroy(papszLayersToCreate);
120 7 : }
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 7 : int OGRDXFWriterDS::Open( const char * pszFilename, char **papszOptions )
167 :
168 : {
169 : /* -------------------------------------------------------------------- */
170 : /* Open the standard header, or a user provided header. */
171 : /* -------------------------------------------------------------------- */
172 7 : if( CSLFetchNameValue(papszOptions,"HEADER") != NULL )
173 3 : 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 7 : if( CSLFetchNameValue(papszOptions,"TRAILER") != NULL )
190 0 : osTrailerFile = CSLFetchNameValue(papszOptions,"TRAILER");
191 : else
192 : {
193 7 : const char *pszValue = CPLFindFile( "gdal", "trailer.dxf" );
194 7 : if( pszValue != NULL )
195 7 : 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 7 : nNextFID = 80;
205 : #else
206 : nNextFID = 131072;
207 : #endif
208 :
209 7 : if( CSLFetchNameValue( papszOptions, "FIRST_ENTITY" ) != NULL )
210 1 : nNextFID = atoi(CSLFetchNameValue( papszOptions, "FIRST_ENTITY" ));
211 :
212 : /* -------------------------------------------------------------------- */
213 : /* Prescan the header and trailer for entity codes. */
214 : /* -------------------------------------------------------------------- */
215 7 : ScanForEntities( osHeaderFile, "HEADER" );
216 7 : 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 7 : if( !oHeaderDS.Open( osHeaderFile, TRUE ) )
223 0 : return FALSE;
224 :
225 : /* -------------------------------------------------------------------- */
226 : /* Create the output file. */
227 : /* -------------------------------------------------------------------- */
228 7 : fp = VSIFOpenL( pszFilename, "w+" );
229 :
230 7 : 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 7 : osTempFilename = pszFilename;
242 7 : osTempFilename += ".tmp";
243 :
244 7 : fpTemp = VSIFOpenL( osTempFilename, "w" );
245 7 : 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 7 : return TRUE;
254 : }
255 :
256 : /************************************************************************/
257 : /* CreateLayer() */
258 : /************************************************************************/
259 :
260 8 : OGRLayer *OGRDXFWriterDS::CreateLayer( const char *pszName,
261 : OGRSpatialReference *,
262 : OGRwkbGeometryType,
263 : char ** )
264 :
265 : {
266 8 : if( EQUAL(pszName,"blocks") && poBlocksLayer == NULL )
267 : {
268 1 : poBlocksLayer = new OGRDXFBlocksWriterLayer( this );
269 1 : return poBlocksLayer;
270 : }
271 7 : else if( poLayer == NULL )
272 : {
273 7 : poLayer = new OGRDXFWriterLayer( this, fpTemp );
274 7 : 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 13352 : static int WriteValue( VSILFILE *fp, int nCode, const char *pszLine )
289 :
290 : {
291 : char szLinePair[300];
292 :
293 13352 : snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine );
294 13352 : size_t nLen = strlen(szLinePair);
295 13352 : 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 13352 : return TRUE;
303 : }
304 :
305 : /************************************************************************/
306 : /* WriteValue() */
307 : /************************************************************************/
308 :
309 28 : static int WriteValue( VSILFILE *fp, int nCode, double dfValue )
310 :
311 : {
312 : char szLinePair[64];
313 :
314 28 : snprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue );
315 28 : char* pszComma = strchr(szLinePair, ',');
316 28 : if (pszComma)
317 0 : *pszComma = '.';
318 28 : size_t nLen = strlen(szLinePair);
319 28 : if( VSIFWriteL( szLinePair, 1, nLen, fp ) != nLen )
320 : {
321 : CPLError( CE_Failure, CPLE_FileIO,
322 0 : "Attempt to write line to DXF file failed, disk full?." );
323 0 : return FALSE;
324 : }
325 : else
326 28 : return TRUE;
327 : }
328 : /************************************************************************/
329 : /* TransferUpdateHeader() */
330 : /************************************************************************/
331 :
332 7 : int OGRDXFWriterDS::TransferUpdateHeader( VSILFILE *fpOut )
333 :
334 : {
335 7 : oHeaderDS.ResetReadPointer( 0 );
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Copy header, inserting in new objects as needed. */
339 : /* -------------------------------------------------------------------- */
340 : char szLineBuf[257];
341 : int nCode;
342 7 : CPLString osSection, osTable, osEntity;
343 :
344 5536 : while( (nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1
345 : && osSection != "ENTITIES" )
346 : {
347 5522 : if( nCode == 0 && EQUAL(szLineBuf,"ENDTAB") )
348 : {
349 : // If we are at the end of the LAYER TABLE consider inserting
350 : // missing definitions.
351 63 : if( osTable == "LAYER" )
352 : {
353 7 : if( !WriteNewLayerDefinitions( fp ) )
354 0 : return FALSE;
355 : }
356 :
357 : // If at the end of the BLOCK_RECORD TABLE consider inserting
358 : // missing definitions.
359 63 : if( osTable == "BLOCK_RECORD" && poBlocksLayer )
360 : {
361 1 : if( !WriteNewBlockRecords( fp ) )
362 0 : return FALSE;
363 : }
364 :
365 : // If at the end of the LTYPE TABLE consider inserting
366 : // missing layer type definitions.
367 63 : if( osTable == "LTYPE" )
368 : {
369 7 : if( !WriteNewLineTypeRecords( fp ) )
370 0 : return FALSE;
371 : }
372 :
373 63 : osTable = "";
374 : }
375 :
376 : // If we are at the end of the BLOCKS section, consider inserting
377 : // suplementary blocks.
378 5522 : if( nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf,"ENDSEC")
379 : && poBlocksLayer != NULL )
380 : {
381 1 : if( !WriteNewBlockDefinitions( fp ) )
382 0 : return FALSE;
383 : }
384 :
385 : // We need to keep track of where $HANDSEED is so that we can
386 : // come back and fix it up when we have generated all entity ids.
387 5522 : if( nCode == 9 && EQUAL(szLineBuf,"$HANDSEED") )
388 : {
389 7 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
390 0 : return FALSE;
391 :
392 7 : nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
393 :
394 : // ensure we have room to overwrite with a longer value.
395 56 : while( strlen(szLineBuf) < 8 )
396 : {
397 42 : memmove( szLineBuf+1, szLineBuf, strlen(szLineBuf)+1 );
398 42 : szLineBuf[0] = '0';
399 : }
400 :
401 7 : nHANDSEEDOffset = VSIFTellL( fpOut );
402 : }
403 :
404 : // Patch EXTMIN with minx and miny
405 5522 : if( nCode == 9 && EQUAL(szLineBuf,"$EXTMIN") )
406 : {
407 7 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
408 0 : return FALSE;
409 :
410 7 : nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
411 7 : if (nCode == 10)
412 : {
413 7 : if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinX ) )
414 0 : return FALSE;
415 :
416 7 : nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
417 7 : if (nCode == 20)
418 : {
419 7 : if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinY ) )
420 0 : return FALSE;
421 :
422 7 : continue;
423 : }
424 : }
425 : }
426 :
427 : // Patch EXTMAX with maxx and maxy
428 5515 : if( nCode == 9 && EQUAL(szLineBuf,"$EXTMAX") )
429 : {
430 7 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
431 0 : return FALSE;
432 :
433 7 : nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
434 7 : if (nCode == 10)
435 : {
436 7 : if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxX ) )
437 0 : return FALSE;
438 :
439 7 : nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) );
440 7 : if (nCode == 20)
441 : {
442 7 : if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxY ) )
443 0 : return FALSE;
444 :
445 7 : continue;
446 : }
447 : }
448 : }
449 :
450 : // Copy over the source line.
451 5508 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
452 0 : return FALSE;
453 :
454 : // Track what entity we are in - that is the last "code 0" object.
455 5508 : if( nCode == 0 )
456 349 : osEntity = szLineBuf;
457 :
458 : // Track what section we are in.
459 5508 : if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
460 : {
461 35 : nCode = oHeaderDS.ReadValue( szLineBuf );
462 35 : if( nCode == -1 )
463 0 : break;
464 :
465 35 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
466 0 : return FALSE;
467 :
468 35 : osSection = szLineBuf;
469 : }
470 :
471 : // Track what TABLE we are in.
472 5508 : if( nCode == 0 && EQUAL(szLineBuf,"TABLE") )
473 : {
474 63 : nCode = oHeaderDS.ReadValue( szLineBuf );
475 63 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
476 0 : return FALSE;
477 :
478 63 : osTable = szLineBuf;
479 : }
480 :
481 : // If we are starting the first layer, then capture
482 : // the layer contents while copying so we can duplicate
483 : // it for any new layer definitions.
484 5508 : if( nCode == 0 && EQUAL(szLineBuf,"LAYER")
485 : && osTable == "LAYER" && aosDefaultLayerText.size() == 0 )
486 : {
487 77 : do {
488 77 : anDefaultLayerCode.push_back( nCode );
489 77 : aosDefaultLayerText.push_back( szLineBuf );
490 :
491 77 : if( nCode != 0 && !WriteValue( fpOut, nCode, szLineBuf ) )
492 0 : return FALSE;
493 :
494 77 : nCode = oHeaderDS.ReadValue( szLineBuf );
495 :
496 77 : if( nCode == 2 && !EQUAL(szLineBuf,"0") )
497 : {
498 0 : anDefaultLayerCode.resize(0);
499 0 : aosDefaultLayerText.resize(0);
500 0 : break;
501 : }
502 : } while( nCode != 0 );
503 :
504 7 : oHeaderDS.UnreadValue();
505 : }
506 : }
507 :
508 7 : return TRUE;
509 : }
510 :
511 : /************************************************************************/
512 : /* TransferUpdateTrailer() */
513 : /************************************************************************/
514 :
515 7 : int OGRDXFWriterDS::TransferUpdateTrailer( VSILFILE *fpOut )
516 : {
517 7 : OGRDXFReader oReader;
518 : VSILFILE *fp;
519 :
520 : /* -------------------------------------------------------------------- */
521 : /* Open the file and setup a reader. */
522 : /* -------------------------------------------------------------------- */
523 7 : fp = VSIFOpenL( osTrailerFile, "r" );
524 :
525 7 : if( fp == NULL )
526 0 : return FALSE;
527 :
528 7 : oReader.Initialize( fp );
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* Scan ahead to find the OBJECTS section. */
532 : /* -------------------------------------------------------------------- */
533 : char szLineBuf[257];
534 : int nCode;
535 :
536 21 : while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
537 : {
538 14 : if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
539 : {
540 7 : nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) );
541 7 : if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") )
542 7 : break;
543 : }
544 : }
545 :
546 7 : if( nCode == -1 )
547 : {
548 : CPLError( CE_Failure, CPLE_AppDefined,
549 : "Failed to find OBJECTS section in trailer file '%s'.",
550 0 : osTrailerFile.c_str() );
551 0 : return FALSE;
552 : }
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Insert the "end of section" for ENTITIES, and the start of */
556 : /* the OBJECTS section. */
557 : /* -------------------------------------------------------------------- */
558 7 : WriteValue( fpOut, 0, "ENDSEC" );
559 7 : WriteValue( fpOut, 0, "SECTION" );
560 7 : WriteValue( fpOut, 2, "OBJECTS" );
561 :
562 : /* -------------------------------------------------------------------- */
563 : /* Copy the remainder of the file. */
564 : /* -------------------------------------------------------------------- */
565 7560 : while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
566 : {
567 7546 : if( !WriteValue( fpOut, nCode, szLineBuf ) )
568 : {
569 0 : VSIFCloseL( fp );
570 0 : return FALSE;
571 : }
572 : }
573 :
574 7 : VSIFCloseL( fp );
575 :
576 7 : return TRUE;
577 : }
578 :
579 : /************************************************************************/
580 : /* FixupHANDSEED() */
581 : /* */
582 : /* Fixup the next entity id information in the $HANDSEED header */
583 : /* variable. */
584 : /************************************************************************/
585 :
586 7 : int OGRDXFWriterDS::FixupHANDSEED( VSILFILE *fp )
587 :
588 : {
589 : /* -------------------------------------------------------------------- */
590 : /* What is a good next handle seed? Scan existing values. */
591 : /* -------------------------------------------------------------------- */
592 7 : unsigned int nHighestHandle = 0;
593 7 : std::set<CPLString>::iterator it;
594 :
595 451 : for( it = aosUsedEntities.begin(); it != aosUsedEntities.end(); it++ )
596 : {
597 : unsigned int nHandle;
598 444 : if( sscanf( (*it).c_str(), "%x", &nHandle ) == 1 )
599 : {
600 444 : if( nHandle > nHighestHandle )
601 298 : nHighestHandle = nHandle;
602 : }
603 : }
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Read the existing handseed value, replace it, and write back. */
607 : /* -------------------------------------------------------------------- */
608 : char szWorkBuf[30];
609 7 : int i = 0;
610 :
611 7 : if( nHANDSEEDOffset == 0 )
612 0 : return FALSE;
613 :
614 7 : VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET );
615 7 : VSIFReadL( szWorkBuf, 1, sizeof(szWorkBuf), fp );
616 :
617 35 : while( szWorkBuf[i] != '\n' )
618 21 : i++;
619 :
620 7 : i++;
621 7 : if( szWorkBuf[i] == '\r' )
622 0 : i++;
623 :
624 7 : CPLString osNewValue;
625 :
626 7 : osNewValue.Printf( "%08X", nHighestHandle + 1 );
627 7 : strncpy( szWorkBuf + i, osNewValue.c_str(), osNewValue.size() );
628 :
629 7 : VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET );
630 7 : VSIFWriteL( szWorkBuf, 1, sizeof(szWorkBuf), fp );
631 :
632 7 : return TRUE;
633 : }
634 :
635 : /************************************************************************/
636 : /* WriteNewLayerDefinitions() */
637 : /************************************************************************/
638 :
639 7 : int OGRDXFWriterDS::WriteNewLayerDefinitions( VSILFILE * fpOut )
640 :
641 : {
642 7 : int iLayer, nNewLayers = CSLCount(papszLayersToCreate);
643 :
644 10 : for( iLayer = 0; iLayer < nNewLayers; iLayer++ )
645 : {
646 36 : for( unsigned i = 0; i < aosDefaultLayerText.size(); i++ )
647 : {
648 33 : if( anDefaultLayerCode[i] == 2 )
649 : {
650 3 : if( !WriteValue( fpOut, 2, papszLayersToCreate[iLayer] ) )
651 0 : return FALSE;
652 : }
653 30 : else if( anDefaultLayerCode[i] == 5 )
654 : {
655 3 : WriteEntityID( fpOut );
656 : }
657 : else
658 : {
659 27 : if( !WriteValue( fpOut,
660 : anDefaultLayerCode[i],
661 : aosDefaultLayerText[i] ) )
662 0 : return FALSE;
663 : }
664 : }
665 : }
666 :
667 7 : return TRUE;
668 : }
669 :
670 : /************************************************************************/
671 : /* WriteNewLineTypeRecords() */
672 : /************************************************************************/
673 :
674 7 : int OGRDXFWriterDS::WriteNewLineTypeRecords( VSILFILE *fp )
675 :
676 : {
677 7 : if( poLayer == NULL )
678 0 : return TRUE;
679 :
680 7 : std::map<CPLString,CPLString>::iterator oIt;
681 : std::map<CPLString,CPLString>& oNewLineTypes =
682 7 : poLayer->GetNewLineTypeMap();
683 :
684 9 : for( oIt = oNewLineTypes.begin();
685 : oIt != oNewLineTypes.end(); oIt++ )
686 : {
687 2 : WriteValue( fp, 0, "LTYPE" );
688 2 : WriteEntityID( fp );
689 2 : WriteValue( fp, 100, "AcDbSymbolTableRecord" );
690 2 : WriteValue( fp, 100, "AcDbLinetypeTableRecord" );
691 2 : WriteValue( fp, 2, (*oIt).first );
692 2 : WriteValue( fp, 70, "0" );
693 2 : WriteValue( fp, 3, "" );
694 2 : WriteValue( fp, 72, "65" );
695 2 : VSIFWriteL( (*oIt).second.c_str(), 1, (*oIt).second.size(), fp );
696 :
697 : CPLDebug( "DXF", "Define Line type '%s'.",
698 2 : (*oIt).first.c_str() );
699 : }
700 :
701 7 : return TRUE;
702 : }
703 :
704 : /************************************************************************/
705 : /* WriteNewBlockRecords() */
706 : /************************************************************************/
707 :
708 1 : int OGRDXFWriterDS::WriteNewBlockRecords( VSILFILE * fp )
709 :
710 : {
711 1 : std::set<CPLString> aosAlreadyHandled;
712 :
713 : /* ==================================================================== */
714 : /* Loop over all block objects written via the blocks layer. */
715 : /* ==================================================================== */
716 1 : for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ )
717 : {
718 1 : OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
719 :
720 : /* -------------------------------------------------------------------- */
721 : /* Is this block already defined in the template header? */
722 : /* -------------------------------------------------------------------- */
723 1 : CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName");
724 :
725 1 : if( oHeaderDS.LookupBlock( osBlockName ) != NULL )
726 0 : continue;
727 :
728 : /* -------------------------------------------------------------------- */
729 : /* Have we already written a BLOCK_RECORD for this block? */
730 : /* -------------------------------------------------------------------- */
731 1 : if( aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end() )
732 0 : continue;
733 :
734 1 : aosAlreadyHandled.insert( osBlockName );
735 :
736 : /* -------------------------------------------------------------------- */
737 : /* Write the block record. */
738 : /* -------------------------------------------------------------------- */
739 1 : WriteValue( fp, 0, "BLOCK_RECORD" );
740 1 : WriteEntityID( fp );
741 1 : WriteValue( fp, 100, "AcDbSymbolTableRecord" );
742 1 : WriteValue( fp, 100, "AcDbBlockTableRecord" );
743 1 : WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") );
744 1 : if( !WriteValue( fp, 340, "0" ) )
745 0 : return FALSE;
746 : }
747 :
748 1 : return TRUE;
749 : }
750 :
751 : /************************************************************************/
752 : /* WriteNewBlockDefinitions() */
753 : /************************************************************************/
754 :
755 1 : int OGRDXFWriterDS::WriteNewBlockDefinitions( VSILFILE * fp )
756 :
757 : {
758 1 : poLayer->ResetFP( fp );
759 :
760 : /* ==================================================================== */
761 : /* Loop over all block objects written via the blocks layer. */
762 : /* ==================================================================== */
763 1 : for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ )
764 : {
765 1 : OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
766 :
767 : /* -------------------------------------------------------------------- */
768 : /* Is this block already defined in the template header? */
769 : /* -------------------------------------------------------------------- */
770 1 : CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName");
771 :
772 1 : if( oHeaderDS.LookupBlock( osBlockName ) != NULL )
773 0 : continue;
774 :
775 : /* -------------------------------------------------------------------- */
776 : /* Write the block definition preamble. */
777 : /* -------------------------------------------------------------------- */
778 : CPLDebug( "DXF", "Writing BLOCK definition for '%s'.",
779 1 : poThisBlockFeat->GetFieldAsString("BlockName") );
780 :
781 1 : WriteValue( fp, 0, "BLOCK" );
782 1 : WriteEntityID( fp );
783 1 : WriteValue( fp, 100, "AcDbEntity" );
784 1 : if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 )
785 0 : WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") );
786 : else
787 1 : WriteValue( fp, 8, "0" );
788 1 : WriteValue( fp, 100, "AcDbBlockBegin" );
789 1 : WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") );
790 1 : WriteValue( fp, 70, "0" );
791 :
792 : // Origin
793 1 : WriteValue( fp, 10, "0.0" );
794 1 : WriteValue( fp, 20, "0.0" );
795 1 : WriteValue( fp, 30, "0.0" );
796 :
797 1 : WriteValue( fp, 3, poThisBlockFeat->GetFieldAsString("BlockName") );
798 1 : WriteValue( fp, 1, "" );
799 :
800 : /* -------------------------------------------------------------------- */
801 : /* Write out the feature entities. */
802 : /* -------------------------------------------------------------------- */
803 1 : if( poLayer->CreateFeature( poThisBlockFeat ) != OGRERR_NONE )
804 0 : return FALSE;
805 :
806 : /* -------------------------------------------------------------------- */
807 : /* Write out following features if they are the same block. */
808 : /* -------------------------------------------------------------------- */
809 2 : while( iBlock < poBlocksLayer->apoBlocks.size()-1
810 : && EQUAL(poBlocksLayer->apoBlocks[iBlock+1]->GetFieldAsString("BlockName"),
811 : osBlockName) )
812 : {
813 0 : iBlock++;
814 :
815 0 : if( poLayer->CreateFeature( poBlocksLayer->apoBlocks[iBlock] )
816 : != OGRERR_NONE )
817 0 : return FALSE;
818 : }
819 :
820 : /* -------------------------------------------------------------------- */
821 : /* Write out the block definition postamble. */
822 : /* -------------------------------------------------------------------- */
823 1 : WriteValue( fp, 0, "ENDBLK" );
824 1 : WriteEntityID( fp );
825 1 : WriteValue( fp, 100, "AcDbEntity" );
826 1 : if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 )
827 0 : WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") );
828 : else
829 1 : WriteValue( fp, 8, "0" );
830 1 : WriteValue( fp, 100, "AcDbBlockEnd" );
831 : }
832 :
833 1 : return TRUE;
834 : }
835 :
836 : /************************************************************************/
837 : /* ScanForEntities() */
838 : /* */
839 : /* Scan the indicated file for entity ids ("5" records) and */
840 : /* build them up as a set so we can be careful to avoid */
841 : /* creating new entities with conflicting ids. */
842 : /************************************************************************/
843 :
844 14 : void OGRDXFWriterDS::ScanForEntities( const char *pszFilename,
845 : const char *pszTarget )
846 :
847 : {
848 14 : OGRDXFReader oReader;
849 : VSILFILE *fp;
850 :
851 : /* -------------------------------------------------------------------- */
852 : /* Open the file and setup a reader. */
853 : /* -------------------------------------------------------------------- */
854 14 : fp = VSIFOpenL( pszFilename, "r" );
855 :
856 14 : if( fp == NULL )
857 : return;
858 :
859 14 : oReader.Initialize( fp );
860 :
861 : /* -------------------------------------------------------------------- */
862 : /* Add every code "5" line to our entities list. */
863 : /* -------------------------------------------------------------------- */
864 : char szLineBuf[257];
865 : int nCode;
866 14 : const char *pszPortion = "HEADER";
867 :
868 13278 : while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 )
869 : {
870 13250 : if( (nCode == 5 || nCode == 105) && EQUAL(pszTarget,pszPortion) )
871 : {
872 426 : CPLString osEntity( szLineBuf );
873 :
874 426 : if( CheckEntityID( osEntity ) )
875 : CPLDebug( "DXF", "Encounted entity '%s' multiple times.",
876 6 : osEntity.c_str() );
877 : else
878 420 : aosUsedEntities.insert( osEntity );
879 : }
880 :
881 13250 : if( nCode == 0 && EQUAL(szLineBuf,"SECTION") )
882 : {
883 42 : nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) );
884 42 : if( nCode == 2 && EQUAL(szLineBuf,"ENTITIES") )
885 7 : pszPortion = "BODY";
886 42 : if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") )
887 7 : pszPortion = "TRAILER";
888 : }
889 : }
890 :
891 14 : VSIFCloseL( fp );
892 : }
893 :
894 : /************************************************************************/
895 : /* CheckEntityID() */
896 : /* */
897 : /* Does the mentioned entity already exist? */
898 : /************************************************************************/
899 :
900 451 : int OGRDXFWriterDS::CheckEntityID( const char *pszEntityID )
901 :
902 : {
903 451 : std::set<CPLString>::iterator it;
904 :
905 451 : it = aosUsedEntities.find( pszEntityID );
906 451 : if( it != aosUsedEntities.end() )
907 7 : return TRUE;
908 : else
909 444 : return FALSE;
910 : }
911 :
912 : /************************************************************************/
913 : /* WriteEntityID() */
914 : /************************************************************************/
915 :
916 24 : long OGRDXFWriterDS::WriteEntityID( VSILFILE *fp, long nPreferredFID )
917 :
918 : {
919 24 : CPLString osEntityID;
920 :
921 24 : if( nPreferredFID != OGRNullFID )
922 : {
923 :
924 1 : osEntityID.Printf( "%X", (unsigned int) nPreferredFID );
925 1 : if( !CheckEntityID( osEntityID ) )
926 : {
927 0 : aosUsedEntities.insert( osEntityID );
928 0 : WriteValue( fp, 5, osEntityID );
929 0 : return nPreferredFID;
930 : }
931 : }
932 :
933 24 : do
934 : {
935 24 : osEntityID.Printf( "%X", nNextFID++ );
936 : }
937 : while( CheckEntityID( osEntityID ) );
938 :
939 24 : aosUsedEntities.insert( osEntityID );
940 24 : WriteValue( fp, 5, osEntityID );
941 :
942 24 : return nNextFID - 1;
943 : }
944 :
945 : /************************************************************************/
946 : /* UpdateExtent() */
947 : /************************************************************************/
948 :
949 17 : void OGRDXFWriterDS::UpdateExtent( OGREnvelope* psEnvelope )
950 : {
951 17 : oGlobalEnvelope.Merge(*psEnvelope);
952 17 : }
|