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