Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements OGRDXFWriterDS - the OGRDataSource class used for
5 : * writing a DXF file.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 :
17 : #include <cmath>
18 : #include <cstdlib>
19 : #include <limits>
20 :
21 : #include "ogr_dxf.h"
22 : #include "cpl_conv.h"
23 : #include "cpl_string.h"
24 : #include "cpl_vsi_error.h"
25 :
26 : #ifdef EMBED_RESOURCE_FILES
27 : #include "embedded_resources.h"
28 : #endif
29 :
30 : /************************************************************************/
31 : /* OGRDXFWriterDS() */
32 : /************************************************************************/
33 :
34 57 : OGRDXFWriterDS::OGRDXFWriterDS()
35 : : nNextFID(80), poLayer(nullptr), poBlocksLayer(nullptr), fp(nullptr),
36 57 : fpTemp(nullptr), papszLayersToCreate(nullptr), nHANDSEEDOffset(0)
37 : {
38 57 : }
39 :
40 : /************************************************************************/
41 : /* ~OGRDXFWriterDS() */
42 : /************************************************************************/
43 :
44 114 : OGRDXFWriterDS::~OGRDXFWriterDS()
45 :
46 : {
47 57 : if (fp != nullptr)
48 : {
49 : /* --------------------------------------------------------------------
50 : */
51 : /* Transfer over the header into the destination file with any */
52 : /* adjustments or insertions needed. */
53 : /* --------------------------------------------------------------------
54 : */
55 56 : CPLDebug("DXF", "Compose final DXF file from components.");
56 :
57 56 : if (IsMarkedSuppressOnClose() && fpTemp != nullptr)
58 : {
59 1 : CPLDebug("DXF", "Do not copy final DXF when 'suppress on close'.");
60 1 : VSIFCloseL(fpTemp);
61 1 : VSIUnlink(osTempFilename);
62 1 : fpTemp = nullptr;
63 : }
64 :
65 56 : TransferUpdateHeader(fp);
66 :
67 56 : if (fpTemp != nullptr)
68 : {
69 : /* --------------------------------------------------------------------
70 : */
71 : /* Copy in the temporary file contents. */
72 : /* --------------------------------------------------------------------
73 : */
74 55 : VSIFCloseL(fpTemp);
75 55 : fpTemp = VSIFOpenL(osTempFilename, "r");
76 :
77 55 : const char *pszLine = nullptr;
78 2071 : while ((pszLine = CPLReadLineL(fpTemp)) != nullptr)
79 : {
80 2016 : VSIFWriteL(pszLine, 1, strlen(pszLine), fp);
81 2016 : VSIFWriteL("\n", 1, 1, fp);
82 : }
83 :
84 : /* --------------------------------------------------------------------
85 : */
86 : /* Cleanup temporary file. */
87 : /* --------------------------------------------------------------------
88 : */
89 55 : VSIFCloseL(fpTemp);
90 55 : VSIUnlink(osTempFilename);
91 : }
92 :
93 : /* --------------------------------------------------------------------
94 : */
95 : /* Write trailer. */
96 : /* --------------------------------------------------------------------
97 : */
98 56 : if (osTrailerFile != "")
99 56 : TransferUpdateTrailer(fp);
100 :
101 : /* --------------------------------------------------------------------
102 : */
103 : /* Fixup the HANDSEED value now that we know all the entity ids */
104 : /* created. */
105 : /* --------------------------------------------------------------------
106 : */
107 56 : FixupHANDSEED(fp);
108 :
109 : /* --------------------------------------------------------------------
110 : */
111 : /* Close file. */
112 : /* --------------------------------------------------------------------
113 : */
114 :
115 56 : VSIFCloseL(fp);
116 56 : fp = nullptr;
117 : }
118 :
119 : /* -------------------------------------------------------------------- */
120 : /* Destroy layers. */
121 : /* -------------------------------------------------------------------- */
122 57 : delete poLayer;
123 57 : delete poBlocksLayer;
124 :
125 57 : CSLDestroy(papszLayersToCreate);
126 :
127 57 : if (m_bHeaderFileIsTemp)
128 0 : VSIUnlink(osHeaderFile.c_str());
129 57 : if (m_bTrailerFileIsTemp)
130 0 : VSIUnlink(osTrailerFile.c_str());
131 114 : }
132 :
133 : /************************************************************************/
134 : /* TestCapability() */
135 : /************************************************************************/
136 :
137 48 : int OGRDXFWriterDS::TestCapability(const char *pszCap)
138 :
139 : {
140 48 : if (EQUAL(pszCap, ODsCCreateLayer))
141 : // Unable to have more than one OGR entities layer in a DXF file, with
142 : // one options blocks layer.
143 32 : return poBlocksLayer == nullptr || poLayer == nullptr;
144 : else
145 16 : return FALSE;
146 : }
147 :
148 : /************************************************************************/
149 : /* GetLayer() */
150 : /************************************************************************/
151 :
152 0 : OGRLayer *OGRDXFWriterDS::GetLayer(int iLayer)
153 :
154 : {
155 0 : if (iLayer == 0)
156 0 : return poLayer;
157 : else
158 0 : return nullptr;
159 : }
160 :
161 : /************************************************************************/
162 : /* GetLayerCount() */
163 : /************************************************************************/
164 :
165 0 : int OGRDXFWriterDS::GetLayerCount()
166 :
167 : {
168 0 : if (poLayer)
169 0 : return 1;
170 : else
171 0 : return 0;
172 : }
173 :
174 : /************************************************************************/
175 : /* Open() */
176 : /************************************************************************/
177 :
178 57 : int OGRDXFWriterDS::Open(const char *pszFilename, char **papszOptions)
179 :
180 : {
181 : /* -------------------------------------------------------------------- */
182 : /* Open the standard header, or a user provided header. */
183 : /* -------------------------------------------------------------------- */
184 57 : if (CSLFetchNameValue(papszOptions, "HEADER") != nullptr)
185 3 : osHeaderFile = CSLFetchNameValue(papszOptions, "HEADER");
186 : else
187 : {
188 : #ifdef EMBED_RESOURCE_FILES
189 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
190 : #endif
191 : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
192 : const char *pszValue = nullptr;
193 : #else
194 54 : const char *pszValue = CPLFindFile("gdal", "header.dxf");
195 54 : if (pszValue == nullptr)
196 : #endif
197 : {
198 : #ifdef EMBED_RESOURCE_FILES
199 : static const bool bOnce [[maybe_unused]] = []()
200 : {
201 : CPLDebug("DXF", "Using embedded header.dxf");
202 : return true;
203 : }();
204 : pszValue = VSIMemGenerateHiddenFilename("header.dxf");
205 : VSIFCloseL(VSIFileFromMemBuffer(
206 : pszValue,
207 : const_cast<GByte *>(
208 : reinterpret_cast<const GByte *>(OGRDXFGetHEADER())),
209 : static_cast<int>(strlen(OGRDXFGetHEADER())),
210 : /* bTakeOwnership = */ false));
211 : m_bHeaderFileIsTemp = true;
212 : #else
213 0 : CPLError(CE_Failure, CPLE_OpenFailed,
214 : "Failed to find template header file header.dxf for "
215 : "reading,\nis GDAL_DATA set properly?");
216 0 : return FALSE;
217 : #endif
218 : }
219 54 : osHeaderFile = pszValue;
220 : }
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Establish the name for our trailer file. */
224 : /* -------------------------------------------------------------------- */
225 57 : if (CSLFetchNameValue(papszOptions, "TRAILER") != nullptr)
226 0 : osTrailerFile = CSLFetchNameValue(papszOptions, "TRAILER");
227 : else
228 : {
229 : #ifdef EMBED_RESOURCE_FILES
230 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
231 : #endif
232 : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
233 : const char *pszValue = nullptr;
234 : #else
235 57 : const char *pszValue = CPLFindFile("gdal", "trailer.dxf");
236 57 : if (pszValue != nullptr)
237 57 : osTrailerFile = pszValue;
238 : #endif
239 : #ifdef EMBED_RESOURCE_FILES
240 : if (!pszValue)
241 : {
242 : static const bool bOnce [[maybe_unused]] = []()
243 : {
244 : CPLDebug("DXF", "Using embedded trailer.dxf");
245 : return true;
246 : }();
247 : osTrailerFile = VSIMemGenerateHiddenFilename("trailer.dxf");
248 : m_bTrailerFileIsTemp = false;
249 : VSIFCloseL(VSIFileFromMemBuffer(
250 : osTrailerFile.c_str(),
251 : const_cast<GByte *>(
252 : reinterpret_cast<const GByte *>(OGRDXFGetTRAILER())),
253 : static_cast<int>(strlen(OGRDXFGetTRAILER())),
254 : /* bTakeOwnership = */ false));
255 : }
256 : #endif
257 : }
258 :
259 : /* -------------------------------------------------------------------- */
260 : /* What entity id do we want to start with when writing? Small */
261 : /* values run a risk of overlapping some undetected entity in */
262 : /* the header or trailer despite the prescanning we do. */
263 : /* -------------------------------------------------------------------- */
264 : #ifdef DEBUG
265 57 : nNextFID = 80;
266 : #else
267 : nNextFID = 131072;
268 : #endif
269 :
270 57 : if (CSLFetchNameValue(papszOptions, "FIRST_ENTITY") != nullptr)
271 1 : nNextFID = atoi(CSLFetchNameValue(papszOptions, "FIRST_ENTITY"));
272 :
273 : m_osINSUNITS =
274 57 : CSLFetchNameValueDef(papszOptions, "INSUNITS", m_osINSUNITS.c_str());
275 : m_osMEASUREMENT = CSLFetchNameValueDef(papszOptions, "MEASUREMENT",
276 57 : m_osMEASUREMENT.c_str());
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Prescan the header and trailer for entity codes. */
280 : /* -------------------------------------------------------------------- */
281 57 : ScanForEntities(osHeaderFile, "HEADER");
282 57 : ScanForEntities(osTrailerFile, "TRAILER");
283 :
284 : /* -------------------------------------------------------------------- */
285 : /* Attempt to read the template header file so we have a list */
286 : /* of layers, linestyles and blocks. */
287 : /* -------------------------------------------------------------------- */
288 57 : if (!oHeaderDS.Open(osHeaderFile, true, nullptr))
289 0 : return FALSE;
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Create the output file. */
293 : /* -------------------------------------------------------------------- */
294 57 : fp = VSIFOpenExL(pszFilename, "w+", true);
295 :
296 57 : if (fp == nullptr)
297 : {
298 1 : CPLError(CE_Failure, CPLE_OpenFailed,
299 : "Failed to open '%s' for writing: %s", pszFilename,
300 : VSIGetLastErrorMsg());
301 1 : return FALSE;
302 : }
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Establish the temporary file. */
306 : /* -------------------------------------------------------------------- */
307 56 : osTempFilename = pszFilename;
308 56 : osTempFilename += ".tmp";
309 :
310 56 : fpTemp = VSIFOpenL(osTempFilename, "w");
311 56 : if (fpTemp == nullptr)
312 : {
313 0 : CPLError(CE_Failure, CPLE_OpenFailed,
314 : "Failed to open '%s' for writing.", osTempFilename.c_str());
315 0 : return FALSE;
316 : }
317 :
318 56 : return TRUE;
319 : }
320 :
321 : /************************************************************************/
322 : /* ICreateLayer() */
323 : /************************************************************************/
324 :
325 66 : OGRLayer *OGRDXFWriterDS::ICreateLayer(const char *pszName,
326 : const OGRGeomFieldDefn *poGeomFieldDefn,
327 : CSLConstList /*papszOptions*/)
328 :
329 : {
330 66 : if (poGeomFieldDefn)
331 : {
332 63 : const auto poSRS = poGeomFieldDefn->GetSpatialRef();
333 63 : if (poSRS)
334 5 : m_oSRS = *poSRS;
335 : }
336 66 : if (EQUAL(pszName, "blocks") && poBlocksLayer == nullptr)
337 : {
338 2 : poBlocksLayer = new OGRDXFBlocksWriterLayer(this);
339 2 : return poBlocksLayer;
340 : }
341 64 : else if (poLayer == nullptr)
342 : {
343 48 : poLayer = new OGRDXFWriterLayer(this, fpTemp);
344 48 : return poLayer;
345 : }
346 : else
347 : {
348 16 : CPLError(CE_Failure, CPLE_AppDefined,
349 : "Unable to have more than one OGR entities layer in a DXF "
350 : "file, with one options blocks layer.");
351 16 : return nullptr;
352 : }
353 : }
354 :
355 : /************************************************************************/
356 : /* WriteValue() */
357 : /************************************************************************/
358 :
359 44676 : static bool WriteValue(VSILFILE *fp, int nCode, const char *pszLine)
360 :
361 : {
362 : char szLinePair[300];
363 :
364 44676 : snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine);
365 44676 : size_t nLen = strlen(szLinePair);
366 44676 : if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
367 : {
368 0 : CPLError(CE_Failure, CPLE_FileIO,
369 : "Attempt to write line to DXF file failed, disk full?.");
370 0 : return false;
371 : }
372 :
373 44676 : return true;
374 : }
375 :
376 : /************************************************************************/
377 : /* WriteValue() */
378 : /************************************************************************/
379 :
380 232 : static bool WriteValue(VSILFILE *fp, int nCode, double dfValue)
381 :
382 : {
383 : char szLinePair[64];
384 :
385 232 : CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
386 232 : size_t nLen = strlen(szLinePair);
387 232 : if (VSIFWriteL(szLinePair, 1, nLen, fp) != nLen)
388 : {
389 0 : CPLError(CE_Failure, CPLE_FileIO,
390 : "Attempt to write line to DXF file failed, disk full?.");
391 0 : return false;
392 : }
393 :
394 232 : return true;
395 : }
396 :
397 : /************************************************************************/
398 : /* TransferUpdateHeader() */
399 : /************************************************************************/
400 :
401 56 : bool OGRDXFWriterDS::TransferUpdateHeader(VSILFILE *fpOut)
402 :
403 : {
404 56 : oHeaderDS.ResetReadPointer(0);
405 :
406 : // We don't like non-finite extents. In this case, just write a generic
407 : // bounding box. Most CAD programs probably ignore this anyway.
408 56 : if (!std::isfinite(oGlobalEnvelope.MinX) ||
409 26 : !std::isfinite(oGlobalEnvelope.MinY) ||
410 108 : !std::isfinite(oGlobalEnvelope.MaxX) ||
411 26 : !std::isfinite(oGlobalEnvelope.MaxY))
412 : {
413 30 : oGlobalEnvelope.MinX = 0.0;
414 30 : oGlobalEnvelope.MinY = 0.0;
415 30 : oGlobalEnvelope.MaxX = 10.0;
416 30 : oGlobalEnvelope.MaxY = 10.0;
417 : }
418 :
419 : /* -------------------------------------------------------------------- */
420 : /* Copy header, inserting in new objects as needed. */
421 : /* -------------------------------------------------------------------- */
422 : char szLineBuf[257];
423 56 : int nCode = 0;
424 112 : CPLString osSection;
425 112 : CPLString osTable;
426 112 : CPLString osEntity;
427 :
428 61536 : while ((nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1 &&
429 30740 : osSection != "ENTITIES")
430 : {
431 30740 : if (nCode == 0 && EQUAL(szLineBuf, "ENDTAB"))
432 : {
433 : // If we are at the end of the LAYER TABLE consider inserting
434 : // missing definitions.
435 504 : if (osTable == "LAYER")
436 : {
437 56 : if (!WriteNewLayerDefinitions(fp))
438 0 : return false;
439 : }
440 :
441 : // If at the end of the BLOCK_RECORD TABLE consider inserting
442 : // missing definitions.
443 504 : if (osTable == "BLOCK_RECORD" && poBlocksLayer)
444 : {
445 2 : if (!WriteNewBlockRecords(fp))
446 0 : return false;
447 : }
448 :
449 : // If at the end of the LTYPE TABLE consider inserting
450 : // missing layer type definitions.
451 504 : if (osTable == "LTYPE")
452 : {
453 56 : if (!WriteNewLineTypeRecords(fp))
454 0 : return false;
455 : }
456 :
457 : // If at the end of the STYLE TABLE consider inserting
458 : // missing layer type definitions.
459 504 : if (osTable == "STYLE")
460 : {
461 56 : if (!WriteNewTextStyleRecords(fp))
462 0 : return false;
463 : }
464 :
465 504 : osTable = "";
466 : }
467 :
468 : // If we are at the end of the BLOCKS section, consider inserting
469 : // supplementary blocks.
470 30796 : if (nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf, "ENDSEC") &&
471 56 : poBlocksLayer != nullptr)
472 : {
473 2 : if (!WriteNewBlockDefinitions(fp))
474 0 : return false;
475 : }
476 :
477 : // We need to keep track of where $HANDSEED is so that we can
478 : // come back and fix it up when we have generated all entity ids.
479 30740 : if (nCode == 9 && EQUAL(szLineBuf, "$HANDSEED"))
480 : {
481 56 : if (!WriteValue(fpOut, nCode, szLineBuf))
482 0 : return false;
483 :
484 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
485 :
486 : // ensure we have room to overwrite with a longer value.
487 392 : while (strlen(szLineBuf) < 8)
488 : {
489 336 : memmove(szLineBuf + 1, szLineBuf, strlen(szLineBuf) + 1);
490 336 : szLineBuf[0] = '0';
491 : }
492 :
493 56 : nHANDSEEDOffset = VSIFTellL(fpOut);
494 : }
495 :
496 : // Patch EXTMIN with minx and miny
497 30740 : if (nCode == 9 && EQUAL(szLineBuf, "$EXTMIN"))
498 : {
499 56 : if (!WriteValue(fpOut, nCode, szLineBuf))
500 0 : return false;
501 :
502 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
503 56 : if (nCode == 10)
504 : {
505 56 : if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinX))
506 0 : return false;
507 :
508 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
509 56 : if (nCode == 20)
510 : {
511 56 : if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MinY))
512 0 : return false;
513 :
514 56 : continue;
515 : }
516 : }
517 : }
518 :
519 : // Patch EXTMAX with maxx and maxy
520 30684 : if (nCode == 9 && EQUAL(szLineBuf, "$EXTMAX"))
521 : {
522 56 : if (!WriteValue(fpOut, nCode, szLineBuf))
523 0 : return false;
524 :
525 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
526 56 : if (nCode == 10)
527 : {
528 56 : if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxX))
529 0 : return false;
530 :
531 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
532 56 : if (nCode == 20)
533 : {
534 56 : if (!WriteValue(fpOut, nCode, oGlobalEnvelope.MaxY))
535 0 : return false;
536 :
537 56 : continue;
538 : }
539 : }
540 : }
541 :
542 : // Patch INSUNITS
543 30684 : if (nCode == 9 && EQUAL(szLineBuf, "$INSUNITS") &&
544 56 : m_osINSUNITS != "HEADER_VALUE")
545 : {
546 56 : if (!WriteValue(fpOut, nCode, szLineBuf))
547 0 : return false;
548 56 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
549 56 : if (nCode == 70)
550 : {
551 56 : int nVal = -1;
552 56 : if (m_osINSUNITS == "AUTO" && m_oSRS.IsProjected())
553 : {
554 4 : const char *pszUnits = nullptr;
555 4 : const double dfUnits = m_oSRS.GetLinearUnits(&pszUnits);
556 9 : const auto IsAlmostEqual = [](double x, double y)
557 9 : { return std::fabs(x - y) <= 1e-10; };
558 4 : if (IsAlmostEqual(dfUnits, 1)) /* METERS */
559 : {
560 1 : nVal = 6;
561 : }
562 3 : else if (IsAlmostEqual(dfUnits, CPLAtof(SRS_UL_FOOT_CONV)))
563 : {
564 1 : nVal = 2;
565 : }
566 2 : else if (IsAlmostEqual(dfUnits,
567 : CPLAtof(SRS_UL_US_FOOT_CONV)))
568 : {
569 1 : nVal = 21;
570 : }
571 : else
572 : {
573 1 : CPLError(CE_Warning, CPLE_AppDefined,
574 : "Could not translate CRS unit %s to "
575 : "$INSUNIT. Using default value from "
576 : "template header file",
577 : pszUnits);
578 : }
579 : }
580 52 : else if (m_osINSUNITS != "AUTO")
581 : {
582 : static const struct
583 : {
584 : const char *pszValue;
585 : int nValue;
586 : } INSUNITSMap[] = {
587 : {"UNITLESS", 0},
588 : {"INCHES", 1},
589 : {"FEET", 2},
590 : {"MILLIMETERS", 4},
591 : {"CENTIMETERS", 5},
592 : {"METERS", 6},
593 : {"US_SURVEY_FEET", 21},
594 : };
595 :
596 21 : for (const auto &sTuple : INSUNITSMap)
597 : {
598 39 : if (m_osINSUNITS == sTuple.pszValue ||
599 19 : m_osINSUNITS == CPLSPrintf("%d", sTuple.nValue))
600 : {
601 2 : nVal = sTuple.nValue;
602 2 : break;
603 : }
604 : }
605 3 : if (nVal < 0)
606 : {
607 1 : CPLError(CE_Warning, CPLE_AppDefined,
608 : "Could not translate $INSUNITS=%s. "
609 : "Using default value from template header "
610 : "file",
611 : m_osINSUNITS.c_str());
612 : }
613 : }
614 :
615 56 : if (nVal >= 0)
616 : {
617 5 : if (!WriteValue(fpOut, nCode, CPLSPrintf("%d", nVal)))
618 0 : return false;
619 :
620 5 : continue;
621 : }
622 : }
623 : }
624 :
625 : // Patch MEASUREMENT
626 30679 : if (nCode == 9 && EQUAL(szLineBuf, "$MEASUREMENT") &&
627 56 : m_osMEASUREMENT != "HEADER_VALUE")
628 : {
629 3 : if (!WriteValue(fpOut, nCode, szLineBuf))
630 0 : return false;
631 3 : nCode = oHeaderDS.ReadValue(szLineBuf, sizeof(szLineBuf));
632 3 : if (nCode == 70)
633 : {
634 3 : int nVal = -1;
635 :
636 : static const struct
637 : {
638 : const char *pszValue;
639 : int nValue;
640 : } MEASUREMENTMap[] = {
641 : {"IMPERIAL", 0},
642 : {"METRIC", 1},
643 : };
644 :
645 7 : for (const auto &sTuple : MEASUREMENTMap)
646 : {
647 11 : if (m_osMEASUREMENT == sTuple.pszValue ||
648 5 : m_osMEASUREMENT == CPLSPrintf("%d", sTuple.nValue))
649 : {
650 2 : nVal = sTuple.nValue;
651 2 : break;
652 : }
653 : }
654 3 : if (nVal < 0)
655 : {
656 1 : CPLError(CE_Warning, CPLE_AppDefined,
657 : "Could not translate $MEASUREMENT=%s. "
658 : "Using default value from template header file",
659 : m_osMEASUREMENT.c_str());
660 : }
661 : else
662 : {
663 2 : if (!WriteValue(fpOut, nCode, CPLSPrintf("%d", nVal)))
664 0 : return false;
665 :
666 2 : continue;
667 : }
668 : }
669 : }
670 :
671 : // Copy over the source line.
672 30621 : if (!WriteValue(fpOut, nCode, szLineBuf))
673 0 : return false;
674 :
675 : // Track what entity we are in - that is the last "code 0" object.
676 30621 : if (nCode == 0)
677 2391 : osEntity = szLineBuf;
678 :
679 : // Track what section we are in.
680 30621 : if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
681 : {
682 280 : nCode = oHeaderDS.ReadValue(szLineBuf);
683 280 : if (nCode == -1)
684 0 : break;
685 :
686 280 : if (!WriteValue(fpOut, nCode, szLineBuf))
687 0 : return false;
688 :
689 280 : osSection = szLineBuf;
690 : }
691 :
692 : // Track what TABLE we are in.
693 30621 : if (nCode == 0 && EQUAL(szLineBuf, "TABLE"))
694 : {
695 504 : nCode = oHeaderDS.ReadValue(szLineBuf);
696 504 : if (!WriteValue(fpOut, nCode, szLineBuf))
697 0 : return false;
698 :
699 504 : osTable = szLineBuf;
700 : }
701 :
702 : // If we are starting the first layer, then capture
703 : // the layer contents while copying so we can duplicate
704 : // it for any new layer definitions.
705 30677 : if (nCode == 0 && EQUAL(szLineBuf, "LAYER") && osTable == "LAYER" &&
706 56 : aosDefaultLayerText.empty())
707 : {
708 560 : do
709 : {
710 616 : anDefaultLayerCode.push_back(nCode);
711 616 : aosDefaultLayerText.push_back(szLineBuf);
712 :
713 616 : if (nCode != 0 && !WriteValue(fpOut, nCode, szLineBuf))
714 0 : return false;
715 :
716 616 : nCode = oHeaderDS.ReadValue(szLineBuf);
717 :
718 616 : if (nCode == 2 && !EQUAL(szLineBuf, "0"))
719 : {
720 0 : anDefaultLayerCode.resize(0);
721 0 : aosDefaultLayerText.resize(0);
722 0 : break;
723 : }
724 616 : } while (nCode != 0);
725 :
726 56 : oHeaderDS.UnreadValue();
727 : }
728 : }
729 :
730 56 : return true;
731 : }
732 :
733 : /************************************************************************/
734 : /* TransferUpdateTrailer() */
735 : /************************************************************************/
736 :
737 56 : bool OGRDXFWriterDS::TransferUpdateTrailer(VSILFILE *fpOut)
738 : {
739 : /* -------------------------------------------------------------------- */
740 : /* Open the file and setup a reader. */
741 : /* -------------------------------------------------------------------- */
742 56 : VSILFILE *l_fp = VSIFOpenL(osTrailerFile, "r");
743 :
744 56 : if (l_fp == nullptr)
745 0 : return false;
746 :
747 112 : OGRDXFReader oReader;
748 56 : oReader.Initialize(l_fp);
749 :
750 : /* -------------------------------------------------------------------- */
751 : /* Scan ahead to find the OBJECTS section. */
752 : /* -------------------------------------------------------------------- */
753 : char szLineBuf[257];
754 56 : int nCode = 0;
755 :
756 112 : while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
757 : {
758 112 : if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
759 : {
760 56 : nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
761 56 : if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
762 56 : break;
763 : }
764 : }
765 :
766 56 : if (nCode == -1)
767 : {
768 0 : CPLError(CE_Failure, CPLE_AppDefined,
769 : "Failed to find OBJECTS section in trailer file '%s'.",
770 : osTrailerFile.c_str());
771 0 : return false;
772 : }
773 :
774 : /* -------------------------------------------------------------------- */
775 : /* Insert the "end of section" for ENTITIES, and the start of */
776 : /* the OBJECTS section. */
777 : /* -------------------------------------------------------------------- */
778 56 : WriteValue(fpOut, 0, "ENDSEC");
779 56 : WriteValue(fpOut, 0, "SECTION");
780 56 : WriteValue(fpOut, 2, "OBJECTS");
781 :
782 : /* -------------------------------------------------------------------- */
783 : /* Copy the remainder of the file. */
784 : /* -------------------------------------------------------------------- */
785 56 : bool ret = true;
786 12040 : while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
787 : {
788 11984 : if (!WriteValue(fpOut, nCode, szLineBuf))
789 : {
790 0 : ret = false;
791 0 : break;
792 : }
793 : }
794 :
795 56 : VSIFCloseL(l_fp);
796 :
797 56 : return ret;
798 : }
799 :
800 : /************************************************************************/
801 : /* FixupHANDSEED() */
802 : /* */
803 : /* Fixup the next entity id information in the $HANDSEED header */
804 : /* variable. */
805 : /************************************************************************/
806 :
807 56 : bool OGRDXFWriterDS::FixupHANDSEED(VSILFILE *fpIn)
808 :
809 : {
810 : /* -------------------------------------------------------------------- */
811 : /* What is a good next handle seed? Scan existing values. */
812 : /* -------------------------------------------------------------------- */
813 56 : unsigned int nHighestHandle = 0;
814 56 : std::set<CPLString>::iterator it;
815 :
816 2175 : for (it = aosUsedEntities.begin(); it != aosUsedEntities.end(); ++it)
817 : {
818 2119 : unsigned int nHandle = 0;
819 2119 : if (sscanf((*it).c_str(), "%x", &nHandle) == 1)
820 : {
821 2119 : if (nHandle > nHighestHandle)
822 1389 : nHighestHandle = nHandle;
823 : }
824 : }
825 :
826 : /* -------------------------------------------------------------------- */
827 : /* Read the existing handseed value, replace it, and write back. */
828 : /* -------------------------------------------------------------------- */
829 56 : if (nHANDSEEDOffset == 0)
830 0 : return false;
831 :
832 : char szWorkBuf[30];
833 56 : VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
834 56 : VSIFReadL(szWorkBuf, 1, sizeof(szWorkBuf), fpIn);
835 :
836 56 : int i = 0;
837 224 : while (szWorkBuf[i] != '\n')
838 168 : i++;
839 :
840 56 : i++;
841 56 : if (szWorkBuf[i] == '\r')
842 0 : i++;
843 :
844 56 : CPLString osNewValue;
845 :
846 56 : osNewValue.Printf("%08X", nHighestHandle + 1);
847 56 : strncpy(szWorkBuf + i, osNewValue.c_str(), osNewValue.size());
848 :
849 56 : VSIFSeekL(fpIn, nHANDSEEDOffset, SEEK_SET);
850 56 : VSIFWriteL(szWorkBuf, 1, sizeof(szWorkBuf), fp);
851 :
852 56 : return true;
853 : }
854 :
855 : /************************************************************************/
856 : /* WriteNewLayerDefinitions() */
857 : /************************************************************************/
858 :
859 56 : bool OGRDXFWriterDS::WriteNewLayerDefinitions(VSILFILE *fpOut)
860 :
861 : {
862 56 : const int nNewLayers = CSLCount(papszLayersToCreate);
863 :
864 59 : for (int iLayer = 0; iLayer < nNewLayers; iLayer++)
865 : {
866 3 : bool bIsDefPoints = false;
867 3 : bool bWrote290 = false;
868 36 : for (unsigned i = 0; i < aosDefaultLayerText.size(); i++)
869 : {
870 33 : if (anDefaultLayerCode[i] == 2)
871 : {
872 3 : if (EQUAL(papszLayersToCreate[iLayer], "DEFPOINTS"))
873 0 : bIsDefPoints = true;
874 :
875 3 : if (!WriteValue(fpOut, 2, papszLayersToCreate[iLayer]))
876 0 : return false;
877 : }
878 30 : else if (anDefaultLayerCode[i] == 5)
879 : {
880 : unsigned int nIgnored;
881 3 : if (!WriteEntityID(fpOut, nIgnored))
882 0 : return false;
883 : }
884 : else
885 : {
886 27 : if (anDefaultLayerCode[i] == 290)
887 0 : bWrote290 = true;
888 :
889 27 : if (!WriteValue(fpOut, anDefaultLayerCode[i],
890 27 : aosDefaultLayerText[i]))
891 0 : return false;
892 : }
893 : }
894 3 : if (bIsDefPoints && !bWrote290)
895 : {
896 : // The Defpoints layer must be explicitly set to not plotted to
897 : // please Autocad. See https://trac.osgeo.org/gdal/ticket/7078
898 0 : if (!WriteValue(fpOut, 290, "0"))
899 0 : return false;
900 : }
901 : }
902 :
903 56 : return true;
904 : }
905 :
906 : /************************************************************************/
907 : /* WriteNewLineTypeRecords() */
908 : /************************************************************************/
909 :
910 56 : bool OGRDXFWriterDS::WriteNewLineTypeRecords(VSILFILE *fpIn)
911 :
912 : {
913 56 : if (poLayer == nullptr)
914 8 : return true;
915 :
916 : const std::map<CPLString, std::vector<double>> &oNewLineTypes =
917 48 : poLayer->GetNewLineTypeMap();
918 :
919 48 : bool bRet = true;
920 50 : for (const auto &oPair : oNewLineTypes)
921 : {
922 2 : bRet &= WriteValue(fpIn, 0, "LTYPE");
923 : unsigned int nIgnored;
924 2 : bRet &= WriteEntityID(fpIn, nIgnored);
925 2 : bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
926 2 : bRet &= WriteValue(fpIn, 100, "AcDbLinetypeTableRecord");
927 2 : bRet &= WriteValue(fpIn, 2, oPair.first);
928 2 : bRet &= WriteValue(fpIn, 70, "0");
929 2 : bRet &= WriteValue(fpIn, 3, "");
930 2 : bRet &= WriteValue(fpIn, 72, "65");
931 2 : bRet &= WriteValue(fpIn, 73, static_cast<int>(oPair.second.size()));
932 :
933 2 : double dfTotalLength = 0.0;
934 6 : for (const double &dfSegment : oPair.second)
935 4 : dfTotalLength += fabs(dfSegment);
936 2 : bRet &= WriteValue(fpIn, 40, dfTotalLength);
937 :
938 6 : for (const double &dfSegment : oPair.second)
939 : {
940 4 : bRet &= WriteValue(fpIn, 49, dfSegment);
941 4 : bRet &= WriteValue(fpIn, 74, "0");
942 : }
943 : }
944 :
945 48 : return bRet;
946 : }
947 :
948 : /************************************************************************/
949 : /* WriteNewTextStyleRecords() */
950 : /************************************************************************/
951 :
952 56 : bool OGRDXFWriterDS::WriteNewTextStyleRecords(VSILFILE *fpIn)
953 :
954 : {
955 56 : if (poLayer == nullptr)
956 8 : return true;
957 :
958 48 : auto &oNewTextStyles = poLayer->GetNewTextStyleMap();
959 :
960 48 : bool bRet = true;
961 49 : for (auto &oPair : oNewTextStyles)
962 : {
963 1 : bRet &= WriteValue(fpIn, 0, "STYLE");
964 : unsigned int nIgnored;
965 1 : bRet &= WriteEntityID(fpIn, nIgnored);
966 1 : bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
967 1 : bRet &= WriteValue(fpIn, 100, "AcDbTextStyleTableRecord");
968 1 : bRet &= WriteValue(fpIn, 2, oPair.first);
969 1 : bRet &= WriteValue(fpIn, 70, "0");
970 1 : bRet &= WriteValue(fpIn, 40, "0.0");
971 :
972 1 : if (oPair.second.count("Width"))
973 1 : bRet &= WriteValue(fpIn, 41, oPair.second["Width"]);
974 : else
975 0 : bRet &= WriteValue(fpIn, 41, "1.0");
976 :
977 1 : bRet &= WriteValue(fpIn, 50, "0.0");
978 1 : bRet &= WriteValue(fpIn, 71, "0");
979 1 : bRet &= WriteValue(fpIn, 1001, "ACAD");
980 :
981 1 : if (oPair.second.count("Font"))
982 1 : bRet &= WriteValue(fpIn, 1000, oPair.second["Font"]);
983 :
984 1 : int nStyleValue = 0;
985 1 : if (oPair.second.count("Italic") && oPair.second["Italic"] == "1")
986 0 : nStyleValue |= 0x1000000;
987 1 : if (oPair.second.count("Bold") && oPair.second["Bold"] == "1")
988 1 : nStyleValue |= 0x2000000;
989 1 : bRet &= WriteValue(fpIn, 1071,
990 2 : CPLString().Printf("%d", nStyleValue).c_str());
991 : }
992 :
993 48 : return bRet;
994 : }
995 :
996 : /************************************************************************/
997 : /* WriteNewBlockRecords() */
998 : /************************************************************************/
999 :
1000 2 : bool OGRDXFWriterDS::WriteNewBlockRecords(VSILFILE *fpIn)
1001 :
1002 : {
1003 2 : std::set<CPLString> aosAlreadyHandled;
1004 :
1005 : /* ==================================================================== */
1006 : /* Loop over all block objects written via the blocks layer. */
1007 : /* ==================================================================== */
1008 2 : bool bRet = true;
1009 6 : for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
1010 : {
1011 4 : OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
1012 :
1013 : /* --------------------------------------------------------------------
1014 : */
1015 : /* Is this block already defined in the template header? */
1016 : /* --------------------------------------------------------------------
1017 : */
1018 4 : CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
1019 :
1020 4 : if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
1021 0 : continue;
1022 :
1023 : /* --------------------------------------------------------------------
1024 : */
1025 : /* Have we already written a BLOCK_RECORD for this block? */
1026 : /* --------------------------------------------------------------------
1027 : */
1028 4 : if (aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end())
1029 0 : continue;
1030 :
1031 4 : aosAlreadyHandled.insert(osBlockName);
1032 :
1033 : /* --------------------------------------------------------------------
1034 : */
1035 : /* Write the block record. */
1036 : /* --------------------------------------------------------------------
1037 : */
1038 4 : bRet &= WriteValue(fpIn, 0, "BLOCK_RECORD");
1039 : unsigned int nIgnored;
1040 4 : bRet &= WriteEntityID(fpIn, nIgnored);
1041 4 : bRet &= WriteValue(fpIn, 100, "AcDbSymbolTableRecord");
1042 4 : bRet &= WriteValue(fpIn, 100, "AcDbBlockTableRecord");
1043 4 : bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
1044 4 : bRet &= WriteValue(fpIn, 340, "0");
1045 : }
1046 :
1047 4 : return bRet;
1048 : }
1049 :
1050 : /************************************************************************/
1051 : /* WriteNewBlockDefinitions() */
1052 : /************************************************************************/
1053 :
1054 2 : bool OGRDXFWriterDS::WriteNewBlockDefinitions(VSILFILE *fpIn)
1055 :
1056 : {
1057 2 : if (poLayer == nullptr)
1058 1 : poLayer = new OGRDXFWriterLayer(this, fpTemp);
1059 2 : poLayer->ResetFP(fpIn);
1060 :
1061 : /* ==================================================================== */
1062 : /* Loop over all block objects written via the blocks layer. */
1063 : /* ==================================================================== */
1064 2 : bool bRet = true;
1065 6 : for (size_t iBlock = 0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++)
1066 : {
1067 4 : OGRFeature *poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock];
1068 :
1069 : /* --------------------------------------------------------------------
1070 : */
1071 : /* Is this block already defined in the template header? */
1072 : /* --------------------------------------------------------------------
1073 : */
1074 4 : CPLString osBlockName = poThisBlockFeat->GetFieldAsString("Block");
1075 :
1076 4 : if (oHeaderDS.LookupBlock(osBlockName) != nullptr)
1077 0 : continue;
1078 :
1079 : /* --------------------------------------------------------------------
1080 : */
1081 : /* Write the block definition preamble. */
1082 : /* --------------------------------------------------------------------
1083 : */
1084 4 : CPLDebug("DXF", "Writing BLOCK definition for '%s'.",
1085 : poThisBlockFeat->GetFieldAsString("Block"));
1086 :
1087 4 : bRet &= WriteValue(fpIn, 0, "BLOCK");
1088 : unsigned int nIgnored;
1089 4 : bRet &= WriteEntityID(fpIn, nIgnored);
1090 4 : bRet &= WriteValue(fpIn, 100, "AcDbEntity");
1091 4 : if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
1092 0 : bRet &=
1093 0 : WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
1094 : else
1095 4 : bRet &= WriteValue(fpIn, 8, "0");
1096 4 : bRet &= WriteValue(fpIn, 100, "AcDbBlockBegin");
1097 4 : bRet &= WriteValue(fpIn, 2, poThisBlockFeat->GetFieldAsString("Block"));
1098 4 : bRet &= WriteValue(fpIn, 70, "0");
1099 :
1100 : // Origin
1101 4 : bRet &= WriteValue(fpIn, 10, "0.0");
1102 4 : bRet &= WriteValue(fpIn, 20, "0.0");
1103 4 : bRet &= WriteValue(fpIn, 30, "0.0");
1104 :
1105 4 : bRet &= WriteValue(fpIn, 3, poThisBlockFeat->GetFieldAsString("Block"));
1106 4 : bRet &= WriteValue(fpIn, 1, "");
1107 :
1108 : /* --------------------------------------------------------------------
1109 : */
1110 : /* Write out the feature entities. */
1111 : /* --------------------------------------------------------------------
1112 : */
1113 4 : if (poLayer->CreateFeature(poThisBlockFeat) != OGRERR_NONE)
1114 0 : return false;
1115 :
1116 : /* --------------------------------------------------------------------
1117 : */
1118 : /* Write out following features if they are the same block. */
1119 : /* --------------------------------------------------------------------
1120 : */
1121 6 : while (iBlock < poBlocksLayer->apoBlocks.size() - 1 &&
1122 2 : EQUAL(poBlocksLayer->apoBlocks[iBlock + 1]->GetFieldAsString(
1123 : "Block"),
1124 : osBlockName))
1125 : {
1126 0 : iBlock++;
1127 :
1128 0 : if (poLayer->CreateFeature(poBlocksLayer->apoBlocks[iBlock]) !=
1129 : OGRERR_NONE)
1130 0 : return false;
1131 : }
1132 :
1133 : /* --------------------------------------------------------------------
1134 : */
1135 : /* Write out the block definition postamble. */
1136 : /* --------------------------------------------------------------------
1137 : */
1138 4 : bRet &= WriteValue(fpIn, 0, "ENDBLK");
1139 4 : bRet &= WriteEntityID(fpIn, nIgnored);
1140 4 : bRet &= WriteValue(fpIn, 100, "AcDbEntity");
1141 4 : if (strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0)
1142 0 : bRet &=
1143 0 : WriteValue(fpIn, 8, poThisBlockFeat->GetFieldAsString("Layer"));
1144 : else
1145 4 : bRet &= WriteValue(fpIn, 8, "0");
1146 4 : bRet &= WriteValue(fpIn, 100, "AcDbBlockEnd");
1147 : }
1148 :
1149 2 : return bRet;
1150 : }
1151 :
1152 : /************************************************************************/
1153 : /* ScanForEntities() */
1154 : /* */
1155 : /* Scan the indicated file for entity ids ("5" records) and */
1156 : /* build them up as a set so we can be careful to avoid */
1157 : /* creating new entities with conflicting ids. */
1158 : /************************************************************************/
1159 :
1160 114 : void OGRDXFWriterDS::ScanForEntities(const char *pszFilename,
1161 : const char *pszTarget)
1162 :
1163 : {
1164 : /* -------------------------------------------------------------------- */
1165 : /* Open the file and setup a reader. */
1166 : /* -------------------------------------------------------------------- */
1167 114 : VSILFILE *l_fp = VSIFOpenL(pszFilename, "r");
1168 :
1169 114 : if (l_fp == nullptr)
1170 0 : return;
1171 :
1172 228 : OGRDXFReader oReader;
1173 114 : oReader.Initialize(l_fp);
1174 :
1175 : /* -------------------------------------------------------------------- */
1176 : /* Add every code "5" line to our entities list. */
1177 : /* -------------------------------------------------------------------- */
1178 : char szLineBuf[257];
1179 114 : int nCode = 0;
1180 114 : const char *pszPortion = "HEADER";
1181 :
1182 45126 : while ((nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf))) != -1)
1183 : {
1184 45012 : if ((nCode == 5 || nCode == 105) && EQUAL(pszTarget, pszPortion))
1185 : {
1186 3948 : CPLString osEntity(szLineBuf);
1187 :
1188 1974 : if (CheckEntityID(osEntity))
1189 6 : CPLDebug("DXF", "Encountered entity '%s' multiple times.",
1190 : osEntity.c_str());
1191 : else
1192 1968 : aosUsedEntities.insert(osEntity);
1193 : }
1194 :
1195 45012 : if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
1196 : {
1197 342 : nCode = oReader.ReadValue(szLineBuf, sizeof(szLineBuf));
1198 342 : if (nCode == 2 && EQUAL(szLineBuf, "ENTITIES"))
1199 57 : pszPortion = "BODY";
1200 342 : if (nCode == 2 && EQUAL(szLineBuf, "OBJECTS"))
1201 57 : pszPortion = "TRAILER";
1202 : }
1203 : }
1204 :
1205 114 : VSIFCloseL(l_fp);
1206 : }
1207 :
1208 : /************************************************************************/
1209 : /* CheckEntityID() */
1210 : /* */
1211 : /* Does the mentioned entity already exist? */
1212 : /************************************************************************/
1213 :
1214 2174 : bool OGRDXFWriterDS::CheckEntityID(const char *pszEntityID)
1215 :
1216 : {
1217 2174 : std::set<CPLString>::iterator it;
1218 :
1219 2174 : it = aosUsedEntities.find(pszEntityID);
1220 2174 : return it != aosUsedEntities.end();
1221 : }
1222 :
1223 : /************************************************************************/
1224 : /* WriteEntityID() */
1225 : /************************************************************************/
1226 :
1227 185 : bool OGRDXFWriterDS::WriteEntityID(VSILFILE *fpIn, unsigned int &nAssignedFID,
1228 : GIntBig nPreferredFID)
1229 :
1230 : {
1231 370 : CPLString osEntityID;
1232 :
1233 : // From https://github.com/OSGeo/gdal/issues/11299 it seems that 0 is an
1234 : // invalid handle value.
1235 201 : if (nPreferredFID > 0 &&
1236 : nPreferredFID <=
1237 16 : static_cast<GIntBig>(std::numeric_limits<unsigned int>::max()))
1238 : {
1239 :
1240 16 : osEntityID.Printf("%X", static_cast<unsigned int>(nPreferredFID));
1241 16 : if (!CheckEntityID(osEntityID))
1242 : {
1243 1 : aosUsedEntities.insert(osEntityID);
1244 1 : if (!WriteValue(fpIn, 5, osEntityID))
1245 0 : return false;
1246 1 : nAssignedFID = static_cast<unsigned int>(nPreferredFID);
1247 1 : return true;
1248 : }
1249 : }
1250 :
1251 0 : do
1252 : {
1253 184 : osEntityID.Printf("%X", nNextFID++);
1254 184 : } while (CheckEntityID(osEntityID));
1255 :
1256 184 : aosUsedEntities.insert(osEntityID);
1257 184 : if (!WriteValue(fpIn, 5, osEntityID))
1258 0 : return false;
1259 :
1260 184 : nAssignedFID = nNextFID - 1;
1261 184 : return true;
1262 : }
1263 :
1264 : /************************************************************************/
1265 : /* UpdateExtent() */
1266 : /************************************************************************/
1267 :
1268 175 : void OGRDXFWriterDS::UpdateExtent(OGREnvelope *psEnvelope)
1269 : {
1270 175 : oGlobalEnvelope.Merge(*psEnvelope);
1271 175 : }
|