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