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