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 181 : OGRDXFDataSource::OGRDXFDataSource()
25 : : fp(nullptr), iEntitiesOffset(0), iEntitiesLineNumber(0),
26 : bInlineBlocks(false), bMergeBlockGeometries(false),
27 : bTranslateEscapeSequences(false), bIncludeRawCodeValues(false),
28 181 : b3DExtensibleMode(false), bHaveReadSolidData(false)
29 : {
30 181 : }
31 :
32 : /************************************************************************/
33 : /* ~OGRDXFDataSource() */
34 : /************************************************************************/
35 :
36 486 : OGRDXFDataSource::~OGRDXFDataSource()
37 :
38 : {
39 : /* -------------------------------------------------------------------- */
40 : /* Destroy layers. */
41 : /* -------------------------------------------------------------------- */
42 366 : while (!apoLayers.empty())
43 : {
44 185 : delete apoLayers.back();
45 185 : apoLayers.pop_back();
46 : }
47 :
48 : /* -------------------------------------------------------------------- */
49 : /* Close file. */
50 : /* -------------------------------------------------------------------- */
51 181 : if (fp != nullptr)
52 : {
53 181 : VSIFCloseL(fp);
54 181 : fp = nullptr;
55 : }
56 305 : }
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 399 : OGRLayer *OGRDXFDataSource::GetLayer(int iLayer)
75 :
76 : {
77 399 : if (iLayer < 0 || iLayer >= (int)apoLayers.size())
78 0 : return nullptr;
79 : else
80 399 : return apoLayers[iLayer];
81 : }
82 :
83 : /************************************************************************/
84 : /* Open() */
85 : /************************************************************************/
86 :
87 181 : int OGRDXFDataSource::Open(const char *pszFilename, bool bHeaderOnly,
88 : CSLConstList papszOptionsIn)
89 :
90 : {
91 181 : osEncoding = CPL_ENC_ISO8859_1;
92 :
93 181 : bInlineBlocks = CPLTestBool(
94 : CSLFetchNameValueDef(papszOptionsIn, "INLINE_BLOCKS",
95 : CPLGetConfigOption("DXF_INLINE_BLOCKS", "TRUE")));
96 181 : bMergeBlockGeometries = CPLTestBool(CSLFetchNameValueDef(
97 : papszOptionsIn, "MERGE_BLOCK_GEOMETRIES",
98 : CPLGetConfigOption("DXF_MERGE_BLOCK_GEOMETRIES", "TRUE")));
99 :
100 181 : bTranslateEscapeSequences = CPLTestBool(CSLFetchNameValueDef(
101 : papszOptionsIn, "TRANSLATE_ESCAPE_SEQUENCES",
102 : CPLGetConfigOption("DXF_TRANSLATE_ESCAPE_SEQUENCES", "TRUE")));
103 :
104 181 : bIncludeRawCodeValues = CPLTestBool(CSLFetchNameValueDef(
105 : papszOptionsIn, "INCLUDE_RAW_CODE_VALUES",
106 : CPLGetConfigOption("DXF_INCLUDE_RAW_CODE_VALUES", "FALSE")));
107 :
108 181 : b3DExtensibleMode = CPLTestBool(CSLFetchNameValueDef(
109 : papszOptionsIn, "3D_EXTENSIBLE_MODE",
110 : CPLGetConfigOption("DXF_3D_EXTENSIBLE_MODE", "FALSE")));
111 :
112 181 : m_bClosedLineAsPolygon = CPLTestBool(CSLFetchNameValueDef(
113 : papszOptionsIn, "CLOSED_LINE_AS_POLYGON",
114 : CPLGetConfigOption("DXF_CLOSED_LINE_AS_POLYGON", "FALSE")));
115 :
116 181 : m_dfHatchTolerance = CPLAtof(
117 : CSLFetchNameValueDef(papszOptionsIn, "HATCH_TOLERANCE",
118 : CPLGetConfigOption("DXF_HATCH_TOLERANCE", "-1")));
119 :
120 : // Only for debugging
121 181 : if (CPLTestBool(CPLGetConfigOption("DXF_HEADER_ONLY", "FALSE")))
122 0 : bHeaderOnly = true;
123 :
124 : /* -------------------------------------------------------------------- */
125 : /* Open the file. */
126 : /* -------------------------------------------------------------------- */
127 181 : fp = VSIFOpenL(pszFilename, "r");
128 181 : if (fp == nullptr)
129 0 : return FALSE;
130 :
131 181 : oReader.Initialize(fp);
132 :
133 : /* -------------------------------------------------------------------- */
134 : /* Confirm we have a header section. */
135 : /* -------------------------------------------------------------------- */
136 : char szLineBuf[257];
137 181 : bool bEntitiesOnly = false;
138 :
139 181 : if (ReadValue(szLineBuf) != 0 || !EQUAL(szLineBuf, "SECTION"))
140 0 : return FALSE;
141 :
142 362 : if (ReadValue(szLineBuf) != 2 ||
143 181 : (!EQUAL(szLineBuf, "HEADER") && !EQUAL(szLineBuf, "ENTITIES") &&
144 7 : !EQUAL(szLineBuf, "TABLES")))
145 0 : return FALSE;
146 :
147 181 : 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 162 : 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 155 : if (!ReadHeaderSection())
174 0 : return FALSE;
175 155 : 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 155 : 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 155 : if (EQUAL(szLineBuf, "SECTION"))
196 : {
197 154 : if (ReadValue(szLineBuf) < 0)
198 : {
199 0 : DXF_READER_ERROR();
200 0 : return FALSE;
201 : }
202 : }
203 :
204 155 : if (EQUAL(szLineBuf, "CLASSES"))
205 : {
206 : // int nCode = 0;
207 1137 : while ((/* nCode = */ ReadValue(szLineBuf, sizeof(szLineBuf))) >
208 2274 : -1 &&
209 1137 : !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 155 : if (EQUAL(szLineBuf, "ENDSEC"))
221 : {
222 121 : if (ReadValue(szLineBuf) < 0)
223 : {
224 0 : DXF_READER_ERROR();
225 0 : return FALSE;
226 : }
227 : }
228 :
229 155 : if (EQUAL(szLineBuf, "SECTION"))
230 : {
231 121 : if (ReadValue(szLineBuf) < 0)
232 : {
233 0 : DXF_READER_ERROR();
234 0 : return FALSE;
235 : }
236 : }
237 :
238 155 : if (EQUAL(szLineBuf, "TABLES"))
239 : {
240 154 : if (!ReadTablesSection())
241 0 : return FALSE;
242 154 : 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 181 : 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 181 : apoLayers.push_back(new OGRDXFLayer(this));
261 :
262 : /* -------------------------------------------------------------------- */
263 : /* Process the BLOCKS section if present. */
264 : /* -------------------------------------------------------------------- */
265 181 : if (!bEntitiesOnly)
266 : {
267 162 : 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 162 : if (EQUAL(szLineBuf, "SECTION"))
277 : {
278 159 : if (ReadValue(szLineBuf) < 0)
279 : {
280 0 : DXF_READER_ERROR();
281 0 : return FALSE;
282 : }
283 : }
284 :
285 162 : if (EQUAL(szLineBuf, "BLOCKS"))
286 : {
287 159 : if (!ReadBlocksSection())
288 1 : return FALSE;
289 158 : if (ReadValue(szLineBuf) < 0)
290 : {
291 0 : DXF_READER_ERROR();
292 0 : return FALSE;
293 : }
294 : }
295 : }
296 :
297 180 : if (bHeaderOnly)
298 57 : return TRUE;
299 :
300 : /* -------------------------------------------------------------------- */
301 : /* Now we are at the entities section, hopefully. Confirm. */
302 : /* -------------------------------------------------------------------- */
303 123 : if (EQUAL(szLineBuf, "SECTION"))
304 : {
305 101 : if (ReadValue(szLineBuf) < 0)
306 : {
307 0 : DXF_READER_ERROR();
308 0 : return FALSE;
309 : }
310 : }
311 :
312 123 : if (!EQUAL(szLineBuf, "ENTITIES"))
313 : {
314 0 : DXF_READER_ERROR();
315 0 : return FALSE;
316 : }
317 :
318 123 : iEntitiesOffset = oReader.iSrcBufferFileOffset + oReader.iSrcBufferOffset;
319 123 : iEntitiesLineNumber = oReader.nLineNumber;
320 123 : apoLayers[0]->ResetReading();
321 :
322 123 : return TRUE;
323 : }
324 :
325 : /************************************************************************/
326 : /* ReadTablesSection() */
327 : /************************************************************************/
328 :
329 161 : bool OGRDXFDataSource::ReadTablesSection()
330 :
331 : {
332 : char szLineBuf[257];
333 161 : int nCode = 0;
334 :
335 3056 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
336 1528 : !EQUAL(szLineBuf, "ENDSEC"))
337 : {
338 : // We are only interested in extracting tables.
339 1367 : if (nCode != 0 || !EQUAL(szLineBuf, "TABLE"))
340 0 : continue;
341 :
342 1367 : nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
343 1367 : if (nCode < 0)
344 : {
345 0 : DXF_READER_ERROR();
346 0 : return false;
347 : }
348 :
349 1367 : if (nCode != 2)
350 0 : continue;
351 :
352 : // CPLDebug( "DXF", "Found table %s.", szLineBuf );
353 :
354 37750 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
355 18875 : !EQUAL(szLineBuf, "ENDTAB"))
356 : {
357 17508 : if (nCode == 0 && EQUAL(szLineBuf, "LAYER"))
358 : {
359 167 : if (!ReadLayerDefinition())
360 0 : return false;
361 : }
362 17508 : if (nCode == 0 && EQUAL(szLineBuf, "LTYPE"))
363 : {
364 651 : if (!ReadLineTypeDefinition())
365 0 : return false;
366 : }
367 17508 : if (nCode == 0 && EQUAL(szLineBuf, "STYLE"))
368 : {
369 168 : if (!ReadTextStyleDefinition())
370 0 : return false;
371 : }
372 17508 : if (nCode == 0 && EQUAL(szLineBuf, "DIMSTYLE"))
373 : {
374 156 : if (!ReadDimStyleDefinition())
375 0 : return false;
376 : }
377 : }
378 : }
379 161 : if (nCode < 0)
380 : {
381 0 : DXF_READER_ERROR();
382 0 : return false;
383 : }
384 :
385 161 : CPLDebug("DXF", "Read %d layer definitions.", (int)oLayerTable.size());
386 161 : return true;
387 : }
388 :
389 : /************************************************************************/
390 : /* ReadLayerDefinition() */
391 : /************************************************************************/
392 :
393 167 : bool OGRDXFDataSource::ReadLayerDefinition()
394 :
395 : {
396 : char szLineBuf[257];
397 167 : int nCode = 0;
398 334 : std::map<CPLString, CPLString> oLayerProperties;
399 334 : CPLString osLayerName = "";
400 :
401 167 : oLayerProperties["Hidden"] = "0";
402 :
403 1786 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
404 : {
405 1619 : switch (nCode)
406 : {
407 167 : case 2:
408 : osLayerName =
409 167 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
410 167 : oLayerProperties["Exists"] = "1";
411 167 : break;
412 :
413 166 : case 6:
414 332 : oLayerProperties["Linetype"] =
415 498 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
416 166 : break;
417 :
418 167 : case 62:
419 167 : oLayerProperties["Color"] = szLineBuf;
420 :
421 : // Is layer off?
422 167 : if (atoi(szLineBuf) < 0 && oLayerProperties["Hidden"] != "2")
423 1 : oLayerProperties["Hidden"] = "1";
424 167 : break;
425 :
426 1 : case 420:
427 1 : oLayerProperties["TrueColor"] = szLineBuf;
428 1 : break;
429 :
430 167 : case 70:
431 167 : oLayerProperties["Flags"] = szLineBuf;
432 :
433 : // Is layer frozen?
434 167 : if (atoi(szLineBuf) & 0x01)
435 2 : oLayerProperties["Hidden"] = "2";
436 167 : break;
437 :
438 154 : case 370:
439 : case 39:
440 154 : oLayerProperties["LineWeight"] = szLineBuf;
441 154 : break;
442 :
443 797 : default:
444 797 : break;
445 : }
446 : }
447 167 : if (nCode < 0)
448 : {
449 0 : DXF_READER_ERROR();
450 0 : return false;
451 : }
452 :
453 167 : if (!oLayerProperties.empty())
454 167 : oLayerTable[osLayerName] = std::move(oLayerProperties);
455 :
456 167 : if (nCode == 0)
457 167 : UnreadValue();
458 167 : 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 651 : bool OGRDXFDataSource::ReadLineTypeDefinition()
487 :
488 : {
489 : char szLineBuf[257];
490 651 : int nCode = 0;
491 1302 : CPLString osLineTypeName;
492 1302 : std::vector<double> oLineTypeDef;
493 : double dfThisValue;
494 :
495 8535 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
496 : {
497 7884 : switch (nCode)
498 : {
499 651 : case 2:
500 : osLineTypeName =
501 651 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
502 651 : 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 6405 : default:
523 6405 : break;
524 : }
525 : }
526 651 : 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 651 : if (oLineTypeDef.size() % 2 == 1)
535 : {
536 1 : oLineTypeDef.front() += oLineTypeDef.back();
537 1 : oLineTypeDef.pop_back();
538 : }
539 :
540 651 : 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 651 : if (nCode == 0)
554 651 : UnreadValue();
555 651 : 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 168 : bool OGRDXFDataSource::ReadTextStyleDefinition()
576 :
577 : {
578 : char szLineBuf[257];
579 168 : int nCode = 0;
580 :
581 336 : CPLString osStyleHandle;
582 336 : CPLString osStyleName;
583 168 : bool bInsideAcadSection = false;
584 :
585 2284 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
586 : {
587 2116 : switch (nCode)
588 : {
589 152 : case 5:
590 152 : osStyleHandle = szLineBuf;
591 152 : break;
592 :
593 168 : case 2:
594 336 : osStyleName = CPLString(szLineBuf)
595 168 : .Recode(GetEncoding(), CPL_ENC_UTF8)
596 168 : .toupper();
597 168 : break;
598 :
599 166 : case 70:
600 : // If the LSB is set, this is not a text style
601 166 : if (atoi(szLineBuf) & 1)
602 0 : return true;
603 166 : 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 168 : case 41:
610 168 : oTextStyleTable[osStyleName]["Width"] = szLineBuf;
611 168 : 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 1435 : default:
635 1435 : break;
636 : }
637 : }
638 168 : if (nCode < 0)
639 : {
640 0 : DXF_READER_ERROR();
641 0 : return false;
642 : }
643 :
644 168 : if (nCode == 0)
645 168 : UnreadValue();
646 :
647 168 : if (osStyleHandle != "")
648 152 : oTextStyleHandles[osStyleHandle] = std::move(osStyleName);
649 :
650 168 : 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 216 : void OGRDXFDataSource::PopulateDefaultDimStyleProperties(
718 : std::map<CPLString, CPLString> &oDimStyleProperties)
719 :
720 : {
721 216 : const int *piCode = ACGetKnownDimStyleCodes();
722 2592 : do
723 : {
724 2808 : const char *pszProperty = ACGetDimStylePropertyName(*piCode);
725 5616 : oDimStyleProperties[pszProperty] =
726 8424 : ACGetDimStylePropertyDefault(*piCode);
727 2808 : } while (*(++piCode));
728 216 : }
729 :
730 : /************************************************************************/
731 : /* ReadDimStyleDefinition() */
732 : /************************************************************************/
733 :
734 156 : bool OGRDXFDataSource::ReadDimStyleDefinition()
735 :
736 : {
737 : char szLineBuf[257];
738 156 : int nCode = 0;
739 312 : std::map<CPLString, CPLString> oDimStyleProperties;
740 312 : CPLString osDimStyleName = "";
741 :
742 156 : PopulateDefaultDimStyleProperties(oDimStyleProperties);
743 :
744 1713 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
745 : {
746 1557 : switch (nCode)
747 : {
748 156 : case 2:
749 : osDimStyleName =
750 156 : CPLString(szLineBuf).Recode(GetEncoding(), CPL_ENC_UTF8);
751 156 : break;
752 :
753 1401 : default:
754 1401 : const char *pszProperty = ACGetDimStylePropertyName(nCode);
755 1401 : if (pszProperty)
756 161 : oDimStyleProperties[pszProperty] = szLineBuf;
757 1401 : break;
758 : }
759 : }
760 156 : if (nCode < 0)
761 : {
762 0 : DXF_READER_ERROR();
763 0 : return false;
764 : }
765 :
766 156 : if (!oDimStyleProperties.empty())
767 156 : oDimStyleTable[osDimStyleName] = std::move(oDimStyleProperties);
768 :
769 156 : if (nCode == 0)
770 156 : UnreadValue();
771 156 : 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 155 : bool OGRDXFDataSource::ReadHeaderSection()
804 :
805 : {
806 : char szLineBuf[257];
807 155 : int nCode = 0;
808 :
809 43694 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
810 21847 : !EQUAL(szLineBuf, "ENDSEC"))
811 : {
812 21692 : if (nCode != 9)
813 6116 : continue;
814 :
815 15576 : CPLString l_osName = szLineBuf;
816 :
817 15576 : if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
818 : {
819 0 : DXF_READER_ERROR();
820 0 : return false;
821 : }
822 :
823 15576 : oHeaderVariables[l_osName] = szLineBuf;
824 15576 : GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
825 : "DXF_HEADER_VARIABLES");
826 : }
827 155 : if (nCode < 0)
828 : {
829 0 : DXF_READER_ERROR();
830 0 : return false;
831 : }
832 :
833 155 : nCode = ReadValue(szLineBuf, sizeof(szLineBuf));
834 155 : if (nCode < 0)
835 : {
836 0 : DXF_READER_ERROR();
837 0 : return false;
838 : }
839 155 : UnreadValue();
840 :
841 : /* Unusual DXF files produced by dxflib */
842 : /* such as http://www.ribbonsoft.com/library/architecture/plants/decd5.dxf
843 : */
844 : /* where there is a spurious ENDSEC in the middle of the header variables */
845 155 : if (nCode == 9 && STARTS_WITH_CI(szLineBuf, "$"))
846 : {
847 0 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
848 0 : !EQUAL(szLineBuf, "ENDSEC"))
849 : {
850 0 : if (nCode != 9)
851 0 : continue;
852 :
853 0 : CPLString l_osName = szLineBuf;
854 :
855 0 : if (ReadValue(szLineBuf, sizeof(szLineBuf)) < 0)
856 : {
857 0 : DXF_READER_ERROR();
858 0 : return false;
859 : }
860 :
861 0 : oHeaderVariables[l_osName] = szLineBuf;
862 0 : GDALDataset::SetMetadataItem(l_osName.c_str(), szLineBuf,
863 : "DXF_HEADER_VARIABLES");
864 : }
865 0 : if (nCode < 0)
866 : {
867 0 : DXF_READER_ERROR();
868 0 : return false;
869 : }
870 : }
871 :
872 155 : CPLDebug("DXF", "Read %d header variables.", (int)oHeaderVariables.size());
873 :
874 : /* -------------------------------------------------------------------- */
875 : /* Decide on what CPLRecode() name to use for the files */
876 : /* encoding or allow the encoding to be overridden. */
877 : /* -------------------------------------------------------------------- */
878 155 : CPLString osCodepage = GetVariable("$DWGCODEPAGE", "ANSI_1252");
879 :
880 : // not strictly accurate but works even without iconv.
881 155 : if (osCodepage == "ANSI_1252")
882 155 : osEncoding = CPL_ENC_ISO8859_1;
883 0 : else if (STARTS_WITH_CI(osCodepage, "ANSI_"))
884 : {
885 0 : osEncoding = "CP";
886 0 : osEncoding += osCodepage + 5;
887 : }
888 : else
889 : {
890 : // fallback to the default
891 0 : osEncoding = CPL_ENC_ISO8859_1;
892 : }
893 :
894 155 : const char *pszEncoding = CPLGetConfigOption("DXF_ENCODING", nullptr);
895 155 : if (pszEncoding != nullptr)
896 0 : osEncoding = pszEncoding;
897 :
898 155 : if (osEncoding != CPL_ENC_ISO8859_1)
899 0 : CPLDebug("DXF", "Treating DXF as encoding '%s', $DWGCODEPAGE='%s'",
900 : osEncoding.c_str(), osCodepage.c_str());
901 155 : return true;
902 : }
903 :
904 : /************************************************************************/
905 : /* GetVariable() */
906 : /* */
907 : /* Fetch a variable that came from the HEADER section. */
908 : /************************************************************************/
909 :
910 1085 : const char *OGRDXFDataSource::GetVariable(const char *pszName,
911 : const char *pszDefault)
912 :
913 : {
914 1085 : if (oHeaderVariables.count(pszName) == 0)
915 909 : return pszDefault;
916 : else
917 176 : return oHeaderVariables[pszName];
918 : }
919 :
920 : /************************************************************************/
921 : /* AddStandardFields() */
922 : /************************************************************************/
923 :
924 256 : void OGRDXFDataSource::AddStandardFields(OGRFeatureDefn *poFeatureDefn,
925 : const int nFieldModes)
926 : {
927 512 : OGRFieldDefn oLayerField("Layer", OFTString);
928 256 : poFeatureDefn->AddFieldDefn(&oLayerField);
929 :
930 512 : OGRFieldDefn oPaperSpaceField("PaperSpace", OFTInteger);
931 256 : oPaperSpaceField.SetSubType(OFSTBoolean);
932 256 : poFeatureDefn->AddFieldDefn(&oPaperSpaceField);
933 :
934 512 : OGRFieldDefn oClassField("SubClasses", OFTString);
935 256 : poFeatureDefn->AddFieldDefn(&oClassField);
936 :
937 256 : if (nFieldModes & ODFM_IncludeRawCodeValues)
938 : {
939 2 : OGRFieldDefn oRawCodeField("RawCodeValues", OFTStringList);
940 1 : poFeatureDefn->AddFieldDefn(&oRawCodeField);
941 : }
942 :
943 512 : OGRFieldDefn oLinetypeField("Linetype", OFTString);
944 256 : poFeatureDefn->AddFieldDefn(&oLinetypeField);
945 :
946 512 : OGRFieldDefn oEntityHandleField("EntityHandle", OFTString);
947 256 : poFeatureDefn->AddFieldDefn(&oEntityHandleField);
948 :
949 512 : OGRFieldDefn oTextField("Text", OFTString);
950 256 : poFeatureDefn->AddFieldDefn(&oTextField);
951 :
952 256 : if (nFieldModes & ODFM_Include3DModeFields)
953 : {
954 2 : OGRFieldDefn oASMBinaryField("ASMData", OFTBinary);
955 1 : poFeatureDefn->AddFieldDefn(&oASMBinaryField);
956 :
957 2 : OGRFieldDefn oASMTransformField("ASMTransform", OFTRealList);
958 1 : poFeatureDefn->AddFieldDefn(&oASMTransformField);
959 : }
960 :
961 256 : if (nFieldModes & ODFM_IncludeBlockFields)
962 : {
963 158 : OGRFieldDefn oBlockNameField("BlockName", OFTString);
964 79 : poFeatureDefn->AddFieldDefn(&oBlockNameField);
965 :
966 158 : OGRFieldDefn oScaleField("BlockScale", OFTRealList);
967 79 : poFeatureDefn->AddFieldDefn(&oScaleField);
968 :
969 158 : OGRFieldDefn oBlockAngleField("BlockAngle", OFTReal);
970 79 : poFeatureDefn->AddFieldDefn(&oBlockAngleField);
971 :
972 158 : OGRFieldDefn oBlockOCSNormalField("BlockOCSNormal", OFTRealList);
973 79 : poFeatureDefn->AddFieldDefn(&oBlockOCSNormalField);
974 :
975 158 : OGRFieldDefn oBlockOCSCoordsField("BlockOCSCoords", OFTRealList);
976 79 : poFeatureDefn->AddFieldDefn(&oBlockOCSCoordsField);
977 :
978 158 : OGRFieldDefn oBlockAttribsField("BlockAttributes", OFTStringList);
979 79 : poFeatureDefn->AddFieldDefn(&oBlockAttribsField);
980 :
981 : // This field holds the name of the block on which the entity lies.
982 : // The BlockName field was previously used for this purpose; this
983 : // was changed because of the ambiguity with the BlockName field
984 : // used by INSERT entities.
985 158 : OGRFieldDefn oBlockField("Block", OFTString);
986 79 : poFeatureDefn->AddFieldDefn(&oBlockField);
987 :
988 : // Extra field to use with ATTDEF entities
989 158 : OGRFieldDefn oAttributeTagField("AttributeTag", OFTString);
990 79 : poFeatureDefn->AddFieldDefn(&oAttributeTagField);
991 : }
992 256 : }
993 :
994 : /************************************************************************/
995 : /* GetEntryFromAcDsDataSection() */
996 : /************************************************************************/
997 :
998 : size_t
999 2 : OGRDXFDataSource::GetEntryFromAcDsDataSection(const char *pszEntityHandle,
1000 : const GByte **pabyBuffer)
1001 :
1002 : {
1003 2 : if (!pszEntityHandle || !pabyBuffer)
1004 0 : return 0;
1005 :
1006 2 : if (bHaveReadSolidData)
1007 : {
1008 1 : if (oSolidBinaryData.count(pszEntityHandle) > 0)
1009 : {
1010 1 : *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
1011 1 : return oSolidBinaryData[pszEntityHandle].size();
1012 : }
1013 0 : return 0;
1014 : }
1015 :
1016 : // Keep track of our current position and line number in the file so we can
1017 : // return here later
1018 1 : unsigned int iPrevOffset =
1019 1 : oReader.iSrcBufferFileOffset + oReader.iSrcBufferOffset;
1020 1 : int nPrevLineNumber = oReader.nLineNumber;
1021 :
1022 : char szLineBuf[4096];
1023 1 : int nCode = 0;
1024 1 : bool bFound = false;
1025 :
1026 : // Search for the ACDSDATA section
1027 313 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
1028 : {
1029 : // Check whether the ACDSDATA section starts here
1030 313 : if (nCode == 0 && EQUAL(szLineBuf, "SECTION"))
1031 : {
1032 3 : if ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) < 0)
1033 : {
1034 0 : break;
1035 : }
1036 :
1037 3 : if (nCode == 2 && EQUAL(szLineBuf, "ACDSDATA"))
1038 : {
1039 1 : bFound = true;
1040 1 : break;
1041 : }
1042 : }
1043 : }
1044 :
1045 1 : if (!bFound)
1046 : {
1047 0 : oReader.ResetReadPointer(iPrevOffset, nPrevLineNumber);
1048 0 : return 0;
1049 : }
1050 :
1051 1 : bool bInAcDsRecord = false;
1052 1 : bool bGotAsmData = false;
1053 2 : CPLString osThisHandle;
1054 :
1055 : // Search for the relevant ACDSRECORD and extract its binary data
1056 129 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) >= 0)
1057 : {
1058 128 : if (nCode == 0 && EQUAL(szLineBuf, "ENDSEC"))
1059 : {
1060 : // We've reached the end of the ACDSDATA section
1061 : break;
1062 : }
1063 128 : else if (nCode == 0)
1064 : {
1065 9 : bInAcDsRecord = EQUAL(szLineBuf, "ACDSRECORD");
1066 9 : bGotAsmData = false;
1067 9 : osThisHandle.clear();
1068 : }
1069 119 : else if (bInAcDsRecord && nCode == 320)
1070 : {
1071 3 : osThisHandle = szLineBuf;
1072 : }
1073 116 : else if (bInAcDsRecord && nCode == 2)
1074 : {
1075 6 : bGotAsmData = EQUAL(szLineBuf, "ASM_Data");
1076 : }
1077 110 : else if (bInAcDsRecord && bGotAsmData && nCode == 94)
1078 : {
1079 : // Group code 94 gives the length of the binary data that follows
1080 2 : int nLen = atoi(szLineBuf);
1081 :
1082 : // Enforce some limits (the upper limit is arbitrary)
1083 2 : if (nLen <= 0 || nLen > 1048576)
1084 : {
1085 0 : CPLError(CE_Warning, CPLE_AppDefined,
1086 : "ACDSRECORD data for entity %s is too long (more than "
1087 : "1MB in size) and was skipped.",
1088 : pszEntityHandle);
1089 0 : continue;
1090 : }
1091 :
1092 2 : oSolidBinaryData[osThisHandle].resize(nLen);
1093 :
1094 : // Read the binary data into the buffer
1095 2 : int nPos = 0;
1096 14 : while (ReadValue(szLineBuf, sizeof(szLineBuf)) == 310)
1097 : {
1098 : int nBytesRead;
1099 12 : GByte *pabyHex = CPLHexToBinary(szLineBuf, &nBytesRead);
1100 :
1101 12 : if (nPos + nBytesRead > nLen)
1102 : {
1103 0 : CPLError(CE_Warning, CPLE_AppDefined,
1104 : "Too many bytes in ACDSRECORD data for entity %s. "
1105 : "Is the length (group code 94) correct?",
1106 : pszEntityHandle);
1107 0 : break;
1108 : }
1109 : else
1110 : {
1111 : std::copy_n(pabyHex, nBytesRead,
1112 12 : oSolidBinaryData[osThisHandle].begin() + nPos);
1113 12 : nPos += nBytesRead;
1114 : }
1115 :
1116 12 : CPLFree(pabyHex);
1117 : }
1118 : }
1119 : }
1120 :
1121 1 : oReader.ResetReadPointer(iPrevOffset, nPrevLineNumber);
1122 :
1123 1 : bHaveReadSolidData = true;
1124 :
1125 1 : if (oSolidBinaryData.count(pszEntityHandle) > 0)
1126 : {
1127 1 : *pabyBuffer = oSolidBinaryData[pszEntityHandle].data();
1128 1 : return oSolidBinaryData[pszEntityHandle].size();
1129 : }
1130 0 : return 0;
1131 : }
|