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