Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements OGRDXFDataSource class
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_dxf.h"
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 :
18 : #include <algorithm>
19 :
20 : /************************************************************************/
21 : /* OGRDXFDataSource() */
22 : /************************************************************************/
23 :
24 219 : OGRDXFDataSource::OGRDXFDataSource()
25 : : fp(nullptr), iEntitiesOffset(0), iEntitiesLineNumber(0),
26 : bInlineBlocks(false), bMergeBlockGeometries(false),
27 : bTranslateEscapeSequences(false), bIncludeRawCodeValues(false),
28 : b3DExtensibleMode(false), bHaveReadSolidData(false),
29 219 : poReader(std::make_unique<OGRDXFReaderASCII>())
30 : {
31 219 : }
32 :
33 : /************************************************************************/
34 : /* ~OGRDXFDataSource() */
35 : /************************************************************************/
36 :
37 590 : OGRDXFDataSource::~OGRDXFDataSource()
38 :
39 : {
40 : /* -------------------------------------------------------------------- */
41 : /* Destroy layers. */
42 : /* -------------------------------------------------------------------- */
43 443 : while (!apoLayers.empty())
44 : {
45 224 : delete apoLayers.back();
46 224 : apoLayers.pop_back();
47 : }
48 :
49 : /* -------------------------------------------------------------------- */
50 : /* Close file. */
51 : /* -------------------------------------------------------------------- */
52 219 : if (fp != nullptr)
53 : {
54 219 : VSIFCloseL(fp);
55 219 : fp = nullptr;
56 : }
57 371 : }
58 :
59 : /************************************************************************/
60 : /* TestCapability() */
61 : /************************************************************************/
62 :
63 1 : int OGRDXFDataSource::TestCapability(const char *pszCap) const
64 : {
65 1 : if (EQUAL(pszCap, ODsCZGeometries))
66 0 : return true;
67 :
68 1 : return false;
69 : }
70 :
71 : /************************************************************************/
72 : /* GetLayer() */
73 : /************************************************************************/
74 :
75 501 : const OGRLayer *OGRDXFDataSource::GetLayer(int iLayer) const
76 :
77 : {
78 501 : if (iLayer < 0 || iLayer >= (int)apoLayers.size())
79 0 : return nullptr;
80 : else
81 501 : return apoLayers[iLayer];
82 : }
83 :
84 : /************************************************************************/
85 : /* Open() */
86 : /************************************************************************/
87 :
88 219 : bool OGRDXFDataSource::Open(const char *pszFilename, VSILFILE *fpIn,
89 : bool bHeaderOnly, CSLConstList papszOptionsIn)
90 :
91 : {
92 219 : SetDescription(pszFilename);
93 :
94 219 : osEncoding = CPL_ENC_ISO8859_1;
95 :
96 219 : bInlineBlocks = CPLTestBool(
97 : CSLFetchNameValueDef(papszOptionsIn, "INLINE_BLOCKS",
98 : CPLGetConfigOption("DXF_INLINE_BLOCKS", "TRUE")));
99 219 : bMergeBlockGeometries = CPLTestBool(CSLFetchNameValueDef(
100 : papszOptionsIn, "MERGE_BLOCK_GEOMETRIES",
101 : CPLGetConfigOption("DXF_MERGE_BLOCK_GEOMETRIES", "TRUE")));
102 :
103 219 : bTranslateEscapeSequences = CPLTestBool(CSLFetchNameValueDef(
104 : papszOptionsIn, "TRANSLATE_ESCAPE_SEQUENCES",
105 : CPLGetConfigOption("DXF_TRANSLATE_ESCAPE_SEQUENCES", "TRUE")));
106 :
107 219 : bIncludeRawCodeValues = CPLTestBool(CSLFetchNameValueDef(
108 : papszOptionsIn, "INCLUDE_RAW_CODE_VALUES",
109 : CPLGetConfigOption("DXF_INCLUDE_RAW_CODE_VALUES", "FALSE")));
110 :
111 219 : b3DExtensibleMode = CPLTestBool(CSLFetchNameValueDef(
112 : papszOptionsIn, "3D_EXTENSIBLE_MODE",
113 : CPLGetConfigOption("DXF_3D_EXTENSIBLE_MODE", "FALSE")));
114 :
115 219 : m_bClosedLineAsPolygon = CPLTestBool(CSLFetchNameValueDef(
116 : papszOptionsIn, "CLOSED_LINE_AS_POLYGON",
117 : CPLGetConfigOption("DXF_CLOSED_LINE_AS_POLYGON", "FALSE")));
118 :
119 219 : m_dfHatchTolerance = CPLAtof(
120 : CSLFetchNameValueDef(papszOptionsIn, "HATCH_TOLERANCE",
121 : CPLGetConfigOption("DXF_HATCH_TOLERANCE", "-1")));
122 :
123 : // Only for debugging
124 219 : if (CPLTestBool(CPLGetConfigOption("DXF_HEADER_ONLY", "FALSE")))
125 0 : bHeaderOnly = true;
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Open the file. */
129 : /* -------------------------------------------------------------------- */
130 219 : fp = fpIn;
131 219 : if (fp == nullptr)
132 67 : fp = VSIFOpenL(pszFilename, "rb");
133 219 : if (fp == nullptr)
134 0 : return false;
135 :
136 219 : VSIFSeekL(fp, 0, SEEK_SET);
137 438 : std::string osBuffer;
138 219 : constexpr size_t nBinarySignatureLen = AUTOCAD_BINARY_DXF_SIGNATURE.size();
139 219 : osBuffer.resize(nBinarySignatureLen);
140 219 : VSIFReadL(osBuffer.data(), 1, osBuffer.size(), fp);
141 219 : if (memcmp(osBuffer.data(), AUTOCAD_BINARY_DXF_SIGNATURE.data(),
142 219 : nBinarySignatureLen) == 0)
143 : {
144 5 : poReader = std::make_unique<OGRDXFReaderBinary>();
145 : }
146 : else
147 : {
148 214 : VSIFSeekL(fp, 0, SEEK_SET);
149 : }
150 :
151 219 : poReader->Initialize(fp);
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Confirm we have a header section. */
155 : /* -------------------------------------------------------------------- */
156 : char szLineBuf[257];
157 219 : bool bEntitiesOnly = false;
158 :
159 219 : if (ReadValue(szLineBuf) != 0 || !EQUAL(szLineBuf, "SECTION"))
160 0 : return false;
161 :
162 438 : if (ReadValue(szLineBuf) != 2 ||
163 219 : (!EQUAL(szLineBuf, "HEADER") && !EQUAL(szLineBuf, "ENTITIES") &&
164 9 : !EQUAL(szLineBuf, "TABLES")))
165 0 : return false;
166 :
167 219 : if (EQUAL(szLineBuf, "ENTITIES"))
168 19 : bEntitiesOnly = true;
169 :
170 : /* Some files might have no header but begin directly with a TABLES section
171 : */
172 200 : else if (EQUAL(szLineBuf, "TABLES"))
173 : {
174 : osEncoding = CSLFetchNameValueDef(
175 : papszOptionsIn, "ENCODING",
176 9 : CPLGetConfigOption("DXF_ENCODING", osEncoding));
177 :
178 9 : if (!ReadTablesSection())
179 0 : return false;
180 9 : if (ReadValue(szLineBuf) < 0)
181 : {
182 0 : DXF_READER_ERROR();
183 0 : return false;
184 : }
185 : }
186 :
187 : /* -------------------------------------------------------------------- */
188 : /* Process the header, picking up a few useful pieces of */
189 : /* information. */
190 : /* -------------------------------------------------------------------- */
191 : else /* if( EQUAL(szLineBuf,"HEADER") ) */
192 : {
193 191 : if (!ReadHeaderSection(papszOptionsIn))
194 0 : return false;
195 191 : if (ReadValue(szLineBuf) < 0)
196 : {
197 0 : DXF_READER_ERROR();
198 0 : return false;
199 : }
200 :
201 : /* --------------------------------------------------------------------
202 : */
203 : /* Process the CLASSES section, if present. */
204 : /* --------------------------------------------------------------------
205 : */
206 191 : if (EQUAL(szLineBuf, "ENDSEC"))
207 : {
208 0 : if (ReadValue(szLineBuf) < 0)
209 : {
210 0 : DXF_READER_ERROR();
211 0 : return false;
212 : }
213 : }
214 :
215 191 : if (EQUAL(szLineBuf, "SECTION"))
216 : {
217 190 : if (ReadValue(szLineBuf) < 0)
218 : {
219 0 : DXF_READER_ERROR();
220 0 : return false;
221 : }
222 : }
223 :
224 191 : if (EQUAL(szLineBuf, "CLASSES"))
225 : {
226 : // int nCode = 0;
227 2067 : while ((/* nCode = */ ReadValue(szLineBuf, sizeof(szLineBuf))) >
228 4134 : -1 &&
229 2067 : !EQUAL(szLineBuf, "ENDSEC"))
230 : {
231 : // printf("C:%d/%s\n", nCode, szLineBuf );
232 : }
233 : }
234 :
235 : /* --------------------------------------------------------------------
236 : */
237 : /* Process the TABLES section, if present. */
238 : /* --------------------------------------------------------------------
239 : */
240 191 : if (EQUAL(szLineBuf, "ENDSEC"))
241 : {
242 151 : if (ReadValue(szLineBuf) < 0)
243 : {
244 0 : DXF_READER_ERROR();
245 0 : return false;
246 : }
247 : }
248 :
249 191 : if (EQUAL(szLineBuf, "SECTION"))
250 : {
251 151 : if (ReadValue(szLineBuf) < 0)
252 : {
253 0 : DXF_READER_ERROR();
254 0 : return false;
255 : }
256 : }
257 :
258 191 : if (EQUAL(szLineBuf, "TABLES"))
259 : {
260 190 : if (!ReadTablesSection())
261 0 : return false;
262 190 : if (ReadValue(szLineBuf) < 0)
263 : {
264 0 : DXF_READER_ERROR();
265 0 : return false;
266 : }
267 : }
268 : }
269 :
270 : /* -------------------------------------------------------------------- */
271 : /* Create a blocks layer if we are not in inlining mode. */
272 : /* -------------------------------------------------------------------- */
273 219 : if (!bInlineBlocks)
274 5 : apoLayers.push_back(new OGRDXFBlocksLayer(this));
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Create out layer object - we will need it when interpreting */
278 : /* blocks. */
279 : /* -------------------------------------------------------------------- */
280 219 : apoLayers.push_back(new OGRDXFLayer(this));
281 :
282 : /* -------------------------------------------------------------------- */
283 : /* Process the BLOCKS section if present. */
284 : /* -------------------------------------------------------------------- */
285 219 : if (!bEntitiesOnly)
286 : {
287 200 : if (EQUAL(szLineBuf, "ENDSEC"))
288 : {
289 0 : if (ReadValue(szLineBuf) < 0)
290 : {
291 0 : DXF_READER_ERROR();
292 0 : return false;
293 : }
294 : }
295 :
296 200 : if (EQUAL(szLineBuf, "SECTION"))
297 : {
298 197 : if (ReadValue(szLineBuf) < 0)
299 : {
300 0 : DXF_READER_ERROR();
301 0 : return false;
302 : }
303 : }
304 :
305 200 : if (EQUAL(szLineBuf, "BLOCKS"))
306 : {
307 197 : if (!ReadBlocksSection())
308 1 : return false;
309 196 : if (ReadValue(szLineBuf) < 0)
310 : {
311 0 : DXF_READER_ERROR();
312 0 : return false;
313 : }
314 : }
315 : }
316 :
317 218 : if (bHeaderOnly)
318 67 : return true;
319 :
320 : /* -------------------------------------------------------------------- */
321 : /* Now we are at the entities section, hopefully. Confirm. */
322 : /* -------------------------------------------------------------------- */
323 151 : if (EQUAL(szLineBuf, "SECTION"))
324 : {
325 129 : if (ReadValue(szLineBuf) < 0)
326 : {
327 0 : DXF_READER_ERROR();
328 0 : return false;
329 : }
330 : }
331 :
332 151 : if (!EQUAL(szLineBuf, "ENTITIES"))
333 : {
334 0 : DXF_READER_ERROR();
335 0 : return false;
336 : }
337 :
338 151 : iEntitiesOffset = poReader->GetCurrentFilePos();
339 151 : iEntitiesLineNumber = poReader->nLineNumber;
340 151 : apoLayers[0]->ResetReading();
341 :
342 151 : return true;
343 : }
344 :
345 : /************************************************************************/
346 : /* ReadTablesSection() */
347 : /************************************************************************/
348 :
349 199 : bool OGRDXFDataSource::ReadTablesSection()
350 :
351 : {
352 : char szLineBuf[257];
353 199 : int nCode = 0;
354 :
355 3768 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
356 1884 : !EQUAL(szLineBuf, "ENDSEC"))
357 : {
358 : // We are only interested in extracting tables.
359 1685 : if (nCode != 0 || !EQUAL(szLineBuf, "TABLE"))
360 0 : continue;
361 :
362 1685 : nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
363 1685 : if (nCode < 0)
364 : {
365 0 : DXF_READER_ERROR();
366 0 : return false;
367 : }
368 :
369 1685 : if (nCode != 2)
370 0 : continue;
371 :
372 : // CPLDebug( "DXF", "Found table %s.", szLineBuf );
373 :
374 48790 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
375 24395 : !EQUAL(szLineBuf, "ENDTAB"))
376 : {
377 22710 : if (nCode == 0 && EQUAL(szLineBuf, "LAYER"))
378 : {
379 217 : if (!ReadLayerDefinition())
380 0 : return false;
381 : }
382 22710 : if (nCode == 0 && EQUAL(szLineBuf, "LTYPE"))
383 : {
384 749 : if (!ReadLineTypeDefinition())
385 0 : return false;
386 : }
387 22710 : if (nCode == 0 && EQUAL(szLineBuf, "STYLE"))
388 : {
389 209 : if (!ReadTextStyleDefinition())
390 0 : return false;
391 : }
392 22710 : if (nCode == 0 && EQUAL(szLineBuf, "DIMSTYLE"))
393 : {
394 194 : if (!ReadDimStyleDefinition())
395 0 : return false;
396 : }
397 : }
398 : }
399 199 : if (nCode < 0)
400 : {
401 0 : DXF_READER_ERROR();
402 0 : return false;
403 : }
404 :
405 199 : CPLDebug("DXF", "Read %d layer definitions.", (int)oLayerTable.size());
406 199 : return true;
407 : }
408 :
409 : /************************************************************************/
410 : /* ReadLayerDefinition() */
411 : /************************************************************************/
412 :
413 217 : bool OGRDXFDataSource::ReadLayerDefinition()
414 :
415 : {
416 : char szLineBuf[257];
417 217 : int nCode = 0;
418 434 : std::map<CPLString, CPLString> oLayerProperties;
419 434 : CPLString osLayerName = "";
420 :
421 217 : oLayerProperties["Hidden"] = "0";
422 :
423 2297 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
424 : {
425 2080 : switch (nCode)
426 : {
427 217 : case 2:
428 : osLayerName =
429 217 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
430 217 : oLayerProperties["Exists"] = "1";
431 217 : break;
432 :
433 216 : case 6:
434 432 : oLayerProperties["Linetype"] =
435 648 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
436 216 : break;
437 :
438 217 : case 62:
439 217 : oLayerProperties["Color"] = szLineBuf;
440 :
441 : // Is layer off?
442 217 : if (atoi(szLineBuf) < 0 && oLayerProperties["Hidden"] != "2")
443 2 : oLayerProperties["Hidden"] = "1";
444 217 : break;
445 :
446 1 : case 420:
447 1 : oLayerProperties["TrueColor"] = szLineBuf;
448 1 : break;
449 :
450 0 : case 440:
451 0 : oLayerProperties["Transparency"] = szLineBuf;
452 0 : break;
453 :
454 217 : case 70:
455 217 : oLayerProperties["Flags"] = szLineBuf;
456 :
457 : // Is layer frozen?
458 217 : if (atoi(szLineBuf) & 0x01)
459 5 : oLayerProperties["Hidden"] = "2";
460 217 : break;
461 :
462 192 : case 370:
463 : case 39:
464 192 : oLayerProperties["LineWeight"] = szLineBuf;
465 192 : break;
466 :
467 1020 : default:
468 1020 : break;
469 : }
470 : }
471 217 : if (nCode < 0)
472 : {
473 0 : DXF_READER_ERROR();
474 0 : return false;
475 : }
476 :
477 217 : if (!oLayerProperties.empty())
478 217 : oLayerTable[osLayerName] = std::move(oLayerProperties);
479 :
480 217 : UnreadValue();
481 217 : return true;
482 : }
483 :
484 : /************************************************************************/
485 : /* LookupLayerProperty() */
486 : /************************************************************************/
487 :
488 : std::optional<CPLString>
489 13468 : OGRDXFDataSource::LookupLayerProperty(const char *pszLayer,
490 : const char *pszProperty) const
491 :
492 : {
493 13468 : if (pszLayer)
494 : {
495 13468 : const auto oIterLayer = oLayerTable.find(pszLayer);
496 13468 : if (oIterLayer != oLayerTable.end())
497 : {
498 12626 : const auto &oTable = oIterLayer->second;
499 12626 : const auto oIterProperty = oTable.find(pszProperty);
500 12626 : if (oIterProperty != oTable.end())
501 7091 : return oIterProperty->second;
502 : }
503 : }
504 6377 : return std::nullopt;
505 : }
506 :
507 : /************************************************************************/
508 : /* ReadLineTypeDefinition() */
509 : /************************************************************************/
510 :
511 749 : bool OGRDXFDataSource::ReadLineTypeDefinition()
512 :
513 : {
514 : char szLineBuf[257];
515 749 : int nCode = 0;
516 1498 : CPLString osLineTypeName;
517 1498 : std::vector<double> oLineTypeDef;
518 : double dfThisValue;
519 :
520 9613 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
521 : {
522 8864 : switch (nCode)
523 : {
524 749 : case 2:
525 : osLineTypeName =
526 749 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
527 749 : break;
528 :
529 840 : case 49:
530 840 : dfThisValue = CPLAtof(szLineBuf);
531 :
532 : // Same sign as the previous entry? Continue the previous dash
533 : // or gap by appending this length
534 1463 : if (oLineTypeDef.size() > 0 &&
535 623 : (dfThisValue < 0) == (oLineTypeDef.back() < 0))
536 : {
537 1 : oLineTypeDef.back() += dfThisValue;
538 : }
539 : // Otherwise, add a new entry
540 : else
541 : {
542 839 : oLineTypeDef.push_back(dfThisValue);
543 : }
544 :
545 840 : break;
546 :
547 7275 : default:
548 7275 : break;
549 : }
550 : }
551 749 : if (nCode < 0)
552 : {
553 0 : DXF_READER_ERROR();
554 0 : return false;
555 : }
556 :
557 : // Deal with an odd number of elements by adding the last element
558 : // onto the first
559 749 : if (oLineTypeDef.size() % 2 == 1)
560 : {
561 1 : oLineTypeDef.front() += oLineTypeDef.back();
562 1 : oLineTypeDef.pop_back();
563 : }
564 :
565 749 : if (oLineTypeDef.size())
566 : {
567 : // If the first element is a gap, rotate the elements so the first
568 : // element is a dash
569 217 : if (oLineTypeDef.front() < 0)
570 : {
571 2 : std::rotate(oLineTypeDef.begin(), oLineTypeDef.begin() + 1,
572 2 : oLineTypeDef.end());
573 : }
574 :
575 217 : oLineTypeTable[osLineTypeName] = std::move(oLineTypeDef);
576 : }
577 :
578 749 : UnreadValue();
579 749 : return true;
580 : }
581 :
582 : /************************************************************************/
583 : /* LookupLineType() */
584 : /************************************************************************/
585 :
586 2734 : std::vector<double> OGRDXFDataSource::LookupLineType(const char *pszName)
587 :
588 : {
589 2734 : if (pszName && oLineTypeTable.count(pszName) > 0)
590 47 : return oLineTypeTable[pszName];
591 : else
592 2687 : return std::vector<double>(); // empty, represents a continuous line
593 : }
594 :
595 : /************************************************************************/
596 : /* ReadTextStyleDefinition() */
597 : /************************************************************************/
598 :
599 209 : bool OGRDXFDataSource::ReadTextStyleDefinition()
600 :
601 : {
602 : char szLineBuf[257];
603 209 : int nCode = 0;
604 :
605 418 : CPLString osStyleHandle;
606 418 : CPLString osStyleName;
607 209 : bool bInsideAcadSection = false;
608 :
609 2844 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
610 : {
611 2635 : switch (nCode)
612 : {
613 185 : case 5:
614 185 : osStyleHandle = szLineBuf;
615 185 : break;
616 :
617 209 : case 2:
618 418 : osStyleName = CPLString(szLineBuf)
619 209 : .Recode(GetEncoding(), CPL_ENC_UTF8)
620 209 : .toupper();
621 209 : break;
622 :
623 207 : case 70:
624 : // If the LSB is set, this is not a text style
625 207 : if (atoi(szLineBuf) & 1)
626 0 : return true;
627 207 : break;
628 :
629 : // Note: 40 and 41 group codes do not propagate from a text
630 : // style down to TEXT objects. However, 41 does propagate down
631 : // for MTEXT.
632 :
633 209 : case 41:
634 209 : oTextStyleTable[osStyleName]["Width"] = szLineBuf;
635 209 : break;
636 :
637 14 : case 1001:
638 14 : bInsideAcadSection = EQUAL(szLineBuf, "ACAD");
639 14 : break;
640 :
641 14 : case 1000:
642 14 : if (bInsideAcadSection)
643 13 : oTextStyleTable[osStyleName]["Font"] = szLineBuf;
644 14 : break;
645 :
646 13 : case 1071:
647 : // bold and italic are kept in this undocumented bitfield
648 13 : if (bInsideAcadSection)
649 : {
650 13 : const int nFontFlags = atoi(szLineBuf);
651 26 : oTextStyleTable[osStyleName]["Bold"] =
652 39 : (nFontFlags & 0x2000000) ? "1" : "0";
653 26 : oTextStyleTable[osStyleName]["Italic"] =
654 39 : (nFontFlags & 0x1000000) ? "1" : "0";
655 : }
656 13 : break;
657 :
658 1784 : default:
659 1784 : break;
660 : }
661 : }
662 209 : if (nCode < 0)
663 : {
664 0 : DXF_READER_ERROR();
665 0 : return false;
666 : }
667 :
668 209 : UnreadValue();
669 :
670 209 : if (osStyleHandle != "")
671 185 : oTextStyleHandles[osStyleHandle] = std::move(osStyleName);
672 :
673 209 : return true;
674 : }
675 :
676 : /************************************************************************/
677 : /* TextStyleExists() */
678 : /************************************************************************/
679 :
680 1 : bool OGRDXFDataSource::TextStyleExists(const char *pszTextStyle)
681 :
682 : {
683 1 : if (!pszTextStyle)
684 0 : return false;
685 :
686 1 : CPLString osTextStyleUpper = pszTextStyle;
687 1 : osTextStyleUpper.toupper();
688 :
689 1 : return oTextStyleTable.count(osTextStyleUpper) > 0;
690 : }
691 :
692 : /************************************************************************/
693 : /* LookupTextStyleProperty() */
694 : /************************************************************************/
695 :
696 395 : const char *OGRDXFDataSource::LookupTextStyleProperty(const char *pszTextStyle,
697 : const char *pszProperty,
698 : const char *pszDefault)
699 :
700 : {
701 395 : if (!pszTextStyle)
702 0 : return pszDefault;
703 :
704 790 : CPLString osTextStyleUpper = pszTextStyle;
705 395 : osTextStyleUpper.toupper();
706 :
707 653 : if (pszProperty && oTextStyleTable.count(osTextStyleUpper) > 0 &&
708 653 : oTextStyleTable[osTextStyleUpper].count(pszProperty) > 0)
709 : {
710 54 : return (oTextStyleTable[osTextStyleUpper])[pszProperty];
711 : }
712 : else
713 : {
714 341 : return pszDefault;
715 : }
716 : }
717 :
718 : /************************************************************************/
719 : /* GetTextStyleNameByHandle() */
720 : /* */
721 : /* Find the name of the text style with the given STYLE table */
722 : /* handle. If there is no such style, an empty string is returned. */
723 : /************************************************************************/
724 :
725 10 : CPLString OGRDXFDataSource::GetTextStyleNameByHandle(const char *pszID)
726 :
727 : {
728 20 : const CPLString l_osID(pszID);
729 :
730 10 : if (oTextStyleHandles.count(l_osID) == 0)
731 6 : return "";
732 : else
733 4 : return oTextStyleHandles[l_osID];
734 : }
735 :
736 : /************************************************************************/
737 : /* PopulateDefaultDimStyleProperties() */
738 : /************************************************************************/
739 :
740 254 : void OGRDXFDataSource::PopulateDefaultDimStyleProperties(
741 : std::map<CPLString, CPLString> &oDimStyleProperties)
742 :
743 : {
744 254 : const int *piCode = ACGetKnownDimStyleCodes();
745 3048 : do
746 : {
747 3302 : const char *pszProperty = ACGetDimStylePropertyName(*piCode);
748 6604 : oDimStyleProperties[pszProperty] =
749 9906 : ACGetDimStylePropertyDefault(*piCode);
750 3302 : } while (*(++piCode));
751 254 : }
752 :
753 : /************************************************************************/
754 : /* ReadDimStyleDefinition() */
755 : /************************************************************************/
756 :
757 194 : bool OGRDXFDataSource::ReadDimStyleDefinition()
758 :
759 : {
760 : char szLineBuf[257];
761 194 : int nCode = 0;
762 388 : std::map<CPLString, CPLString> oDimStyleProperties;
763 388 : CPLString osDimStyleName = "";
764 :
765 194 : PopulateDefaultDimStyleProperties(oDimStyleProperties);
766 :
767 2510 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
768 : {
769 2316 : switch (nCode)
770 : {
771 194 : case 2:
772 : osDimStyleName =
773 194 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
774 194 : break;
775 :
776 2122 : default:
777 2122 : const char *pszProperty = ACGetDimStylePropertyName(nCode);
778 2122 : if (pszProperty)
779 292 : oDimStyleProperties[pszProperty] = szLineBuf;
780 2122 : break;
781 : }
782 : }
783 194 : if (nCode < 0)
784 : {
785 0 : DXF_READER_ERROR();
786 0 : return false;
787 : }
788 :
789 194 : if (!oDimStyleProperties.empty())
790 194 : oDimStyleTable[osDimStyleName] = std::move(oDimStyleProperties);
791 :
792 194 : UnreadValue();
793 194 : return true;
794 : }
795 :
796 : /************************************************************************/
797 : /* LookupDimStyle() */
798 : /* */
799 : /* If the specified DIMSTYLE does not exist, a default set of */
800 : /* of style properties are copied into oDimStyleProperties and */
801 : /* false is returned. Otherwise true is returned. */
802 : /************************************************************************/
803 :
804 42 : bool OGRDXFDataSource::LookupDimStyle(
805 : const char *pszDimStyle,
806 : std::map<CPLString, CPLString> &oDimStyleProperties)
807 :
808 : {
809 42 : if (pszDimStyle == nullptr || !oDimStyleTable.count(pszDimStyle))
810 : {
811 16 : PopulateDefaultDimStyleProperties(oDimStyleProperties);
812 16 : return false;
813 : }
814 :
815 : // make a copy of the DIMSTYLE properties, so no-one can mess around
816 : // with our original copy
817 26 : oDimStyleProperties = oDimStyleTable[pszDimStyle];
818 26 : return true;
819 : }
820 :
821 : /************************************************************************/
822 : /* ReadHeaderSection() */
823 : /************************************************************************/
824 :
825 191 : bool OGRDXFDataSource::ReadHeaderSection(CSLConstList papszOpenOptionsIn)
826 :
827 : {
828 : char szLineBuf[257];
829 191 : int nCode = 0;
830 :
831 57172 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
832 28586 : !EQUAL(szLineBuf, "ENDSEC"))
833 : {
834 28395 : if (nCode != 9)
835 7768 : continue;
836 :
837 20627 : CPLString l_osName = szLineBuf;
838 :
839 20627 : if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
840 : {
841 0 : DXF_READER_ERROR();
842 0 : return false;
843 : }
844 :
845 20627 : oHeaderVariables[l_osName] = szLineBuf;
846 20627 : GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
847 : "DXF_HEADER_VARIABLES");
848 : }
849 191 : if (nCode < 0)
850 : {
851 0 : DXF_READER_ERROR();
852 0 : return false;
853 : }
854 :
855 191 : nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
856 191 : if (nCode < 0)
857 : {
858 0 : DXF_READER_ERROR();
859 0 : return false;
860 : }
861 191 : UnreadValue();
862 :
863 : /* Unusual DXF files produced by dxflib */
864 : /* such as http://www.ribbonsoft.com/library/architecture/plants/decd5.dxf
865 : */
866 : /* where there is a spurious ENDSEC in the middle of the header variables */
867 191 : if (nCode == 9 && STARTS_WITH_CI(szLineBuf, "$"))
868 : {
869 0 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
870 0 : !EQUAL(szLineBuf, "ENDSEC"))
871 : {
872 0 : if (nCode != 9)
873 0 : continue;
874 :
875 0 : CPLString l_osName = szLineBuf;
876 :
877 0 : if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
878 : {
879 0 : DXF_READER_ERROR();
880 0 : return false;
881 : }
882 :
883 0 : oHeaderVariables[l_osName] = szLineBuf;
884 0 : GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
885 : "DXF_HEADER_VARIABLES");
886 : }
887 0 : if (nCode < 0)
888 : {
889 0 : DXF_READER_ERROR();
890 0 : return false;
891 : }
892 : }
893 :
894 191 : CPLDebug("DXF", "Read %d header variables.", (int)oHeaderVariables.size());
895 :
896 : /* -------------------------------------------------------------------- */
897 : /* Decide on what CPLRecode() name to use for the files */
898 : /* encoding or allow the encoding to be overridden. */
899 : /* -------------------------------------------------------------------- */
900 191 : CPLString osCodepage = GetVariable("$DWGCODEPAGE", "ANSI_1252");
901 :
902 : // not strictly accurate but works even without iconv.
903 191 : if (osCodepage == "ANSI_1252")
904 186 : osEncoding = CPL_ENC_ISO8859_1;
905 5 : else if (STARTS_WITH_CI(osCodepage, "ANSI_"))
906 : {
907 5 : osEncoding = "CP";
908 5 : osEncoding += osCodepage + 5;
909 : }
910 : else
911 : {
912 : // fallback to the default
913 0 : osEncoding = CPL_ENC_ISO8859_1;
914 : }
915 :
916 : const char *pszEncoding =
917 191 : CSLFetchNameValueDef(papszOpenOptionsIn, "ENCODING",
918 : CPLGetConfigOption("DXF_ENCODING", nullptr));
919 191 : if (pszEncoding != nullptr)
920 1 : osEncoding = pszEncoding;
921 :
922 191 : if (osEncoding != CPL_ENC_ISO8859_1)
923 6 : CPLDebug("DXF", "Treating DXF as encoding '%s', $DWGCODEPAGE='%s'",
924 : osEncoding.c_str(), osCodepage.c_str());
925 191 : return true;
926 : }
927 :
928 : /************************************************************************/
929 : /* GetVariable() */
930 : /* */
931 : /* Fetch a variable that came from the HEADER section. */
932 : /************************************************************************/
933 :
934 2918 : const char *OGRDXFDataSource::GetVariable(const char *pszName,
935 : const char *pszDefault)
936 :
937 : {
938 2918 : if (oHeaderVariables.count(pszName) == 0)
939 1117 : return pszDefault;
940 : else
941 1801 : return oHeaderVariables[pszName];
942 : }
943 :
944 : /************************************************************************/
945 : /* AddStandardFields() */
946 : /************************************************************************/
947 :
948 306 : void OGRDXFDataSource::AddStandardFields(OGRFeatureDefn *poFeatureDefn,
949 : const int nFieldModes)
950 : {
951 612 : OGRFieldDefn oLayerField("Layer", OFTString);
952 306 : poFeatureDefn->AddFieldDefn(&oLayerField);
953 :
954 612 : OGRFieldDefn oPaperSpaceField("PaperSpace", OFTInteger);
955 306 : oPaperSpaceField.SetSubType(OFSTBoolean);
956 306 : poFeatureDefn->AddFieldDefn(&oPaperSpaceField);
957 :
958 612 : OGRFieldDefn oClassField("SubClasses", OFTString);
959 306 : poFeatureDefn->AddFieldDefn(&oClassField);
960 :
961 306 : if (nFieldModes & ODFM_IncludeRawCodeValues)
962 : {
963 2 : OGRFieldDefn oRawCodeField("RawCodeValues", OFTStringList);
964 1 : poFeatureDefn->AddFieldDefn(&oRawCodeField);
965 : }
966 :
967 612 : OGRFieldDefn oLinetypeField("Linetype", OFTString);
968 306 : poFeatureDefn->AddFieldDefn(&oLinetypeField);
969 :
970 612 : OGRFieldDefn oEntityHandleField("EntityHandle", OFTString);
971 306 : poFeatureDefn->AddFieldDefn(&oEntityHandleField);
972 :
973 612 : OGRFieldDefn oTextField("Text", OFTString);
974 306 : poFeatureDefn->AddFieldDefn(&oTextField);
975 :
976 306 : if (nFieldModes & ODFM_Include3DModeFields)
977 : {
978 2 : OGRFieldDefn oASMBinaryField("ASMData", OFTBinary);
979 1 : poFeatureDefn->AddFieldDefn(&oASMBinaryField);
980 :
981 2 : OGRFieldDefn oASMTransformField("ASMTransform", OFTRealList);
982 1 : poFeatureDefn->AddFieldDefn(&oASMTransformField);
983 : }
984 :
985 306 : if (nFieldModes & ODFM_IncludeBlockFields)
986 : {
987 184 : OGRFieldDefn oBlockNameField("BlockName", OFTString);
988 92 : poFeatureDefn->AddFieldDefn(&oBlockNameField);
989 :
990 184 : OGRFieldDefn oScaleField("BlockScale", OFTRealList);
991 92 : poFeatureDefn->AddFieldDefn(&oScaleField);
992 :
993 184 : OGRFieldDefn oBlockAngleField("BlockAngle", OFTReal);
994 92 : poFeatureDefn->AddFieldDefn(&oBlockAngleField);
995 :
996 184 : OGRFieldDefn oBlockOCSNormalField("BlockOCSNormal", OFTRealList);
997 92 : poFeatureDefn->AddFieldDefn(&oBlockOCSNormalField);
998 :
999 184 : OGRFieldDefn oBlockOCSCoordsField("BlockOCSCoords", OFTRealList);
1000 92 : poFeatureDefn->AddFieldDefn(&oBlockOCSCoordsField);
1001 :
1002 184 : OGRFieldDefn oBlockAttribsField("BlockAttributes", OFTStringList);
1003 92 : poFeatureDefn->AddFieldDefn(&oBlockAttribsField);
1004 :
1005 : // This field holds the name of the block on which the entity lies.
1006 : // The BlockName field was previously used for this purpose; this
1007 : // was changed because of the ambiguity with the BlockName field
1008 : // used by INSERT entities.
1009 184 : OGRFieldDefn oBlockField("Block", OFTString);
1010 92 : poFeatureDefn->AddFieldDefn(&oBlockField);
1011 :
1012 : // Extra field to use with ATTDEF entities
1013 184 : OGRFieldDefn oAttributeTagField("AttributeTag", OFTString);
1014 92 : poFeatureDefn->AddFieldDefn(&oAttributeTagField);
1015 : }
1016 306 : }
1017 :
1018 : /************************************************************************/
1019 : /* GetEntryFromAcDsDataSection() */
1020 : /************************************************************************/
1021 :
1022 : size_t
1023 2 : OGRDXFDataSource::GetEntryFromAcDsDataSection(const char *pszEntityHandle,
1024 : const GByte **pabyBuffer)
1025 :
1026 : {
1027 2 : if (!pszEntityHandle || !pabyBuffer)
1028 0 : return 0;
1029 :
1030 2 : if (bHaveReadSolidData)
1031 : {
1032 1 : if (oSolidBinaryData.count(pszEntityHandle) > 0)
1033 : {
1034 1 : *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
1035 1 : return oSolidBinaryData[pszEntityHandle].size();
1036 : }
1037 0 : return 0;
1038 : }
1039 :
1040 : // Keep track of our current position and line number in the file so we can
1041 : // return here later
1042 1 : uint64_t iPrevOffset = poReader->GetCurrentFilePos();
1043 1 : int nPrevLineNumber = poReader->nLineNumber;
1044 :
1045 : char szLineBuf[4096];
1046 1 : int nCode = 0;
1047 1 : bool bFound = false;
1048 :
1049 : // Search for the ACDSDATA section
1050 313 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
1051 : {
1052 : // Check whether the ACDSDATA section starts here
1053 313 : if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
1054 : {
1055 3 : if ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) < 0)
1056 : {
1057 0 : break;
1058 : }
1059 :
1060 3 : if (nCode == 2 && EQUAL(szLineBuf, "ACDSDATA"))
1061 : {
1062 1 : bFound = true;
1063 1 : break;
1064 : }
1065 : }
1066 : }
1067 :
1068 1 : if (!bFound)
1069 : {
1070 0 : poReader->ResetReadPointer(iPrevOffset, nPrevLineNumber);
1071 0 : return 0;
1072 : }
1073 :
1074 1 : bool bInAcDsRecord = false;
1075 1 : bool bGotAsmData = false;
1076 2 : CPLString osThisHandle;
1077 :
1078 : // Search for the relevant ACDSRECORD and extract its binary data
1079 129 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
1080 : {
1081 128 : if (nCode == 0 && EQUAL(szLineBuf, "ENDSEC"))
1082 : {
1083 : // We've reached the end of the ACDSDATA section
1084 : break;
1085 : }
1086 128 : else if (nCode == 0)
1087 : {
1088 9 : bInAcDsRecord = EQUAL(szLineBuf, "ACDSRECORD");
1089 9 : bGotAsmData = false;
1090 9 : osThisHandle.clear();
1091 : }
1092 119 : else if (bInAcDsRecord && nCode == 320)
1093 : {
1094 3 : osThisHandle = szLineBuf;
1095 : }
1096 116 : else if (bInAcDsRecord && nCode == 2)
1097 : {
1098 6 : bGotAsmData = EQUAL(szLineBuf, "ASM_Data");
1099 : }
1100 110 : else if (bInAcDsRecord && bGotAsmData && nCode == 94)
1101 : {
1102 : // Group code 94 gives the length of the binary data that follows
1103 2 : int nLen = atoi(szLineBuf);
1104 :
1105 : // Enforce some limits (the upper limit is arbitrary)
1106 2 : if (nLen <= 0 || nLen > 1048576)
1107 : {
1108 0 : CPLError(CE_Warning, CPLE_AppDefined,
1109 : "ACDSRECORD data for entity %s is too long (more than "
1110 : "1MB in size) and was skipped.",
1111 : pszEntityHandle);
1112 0 : continue;
1113 : }
1114 :
1115 2 : oSolidBinaryData[osThisHandle].resize(nLen);
1116 :
1117 : // Read the binary data into the buffer
1118 2 : int nPos = 0;
1119 14 : while (ReadValue(szLineBuf, sizeof(szLineBuf)) == 310)
1120 : {
1121 : int nBytesRead;
1122 : std::unique_ptr<GByte, VSIFreeReleaser> pabyHex(
1123 12 : CPLHexToBinary(szLineBuf, &nBytesRead));
1124 :
1125 12 : if (nPos + nBytesRead > nLen)
1126 : {
1127 0 : CPLError(CE_Warning, CPLE_AppDefined,
1128 : "Too many bytes in ACDSRECORD data for entity %s. "
1129 : "Is the length (group code 94) correct?",
1130 : pszEntityHandle);
1131 0 : break;
1132 : }
1133 : else
1134 : {
1135 : std::copy_n(pabyHex.get(), nBytesRead,
1136 12 : oSolidBinaryData[osThisHandle].begin() + nPos);
1137 12 : nPos += nBytesRead;
1138 : }
1139 : }
1140 : }
1141 : }
1142 :
1143 1 : poReader->ResetReadPointer(iPrevOffset, nPrevLineNumber);
1144 :
1145 1 : bHaveReadSolidData = true;
1146 :
1147 1 : if (oSolidBinaryData.count(pszEntityHandle) > 0)
1148 : {
1149 1 : *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
1150 1 : return oSolidBinaryData[pszEntityHandle].size();
1151 : }
1152 0 : return 0;
1153 : }
|