Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements OGRDXFLayer class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10 : * Copyright (c) 2017-2020, Alan Thomas <alant@outlook.com.au>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ogr_dxf.h"
16 : #include "cpl_conv.h"
17 : #include "ogrdxf_polyline_smooth.h"
18 : #include "ogr_api.h"
19 :
20 : #include <cmath>
21 : #include <algorithm>
22 : #include <limits>
23 : #include <stdexcept>
24 : #include <memory>
25 :
26 : /************************************************************************/
27 : /* push() */
28 : /************************************************************************/
29 :
30 1736 : void OGRDXFFeatureQueue::push(OGRDXFFeature *poFeature)
31 : {
32 1736 : apoFeatures.push(poFeature);
33 1736 : }
34 :
35 : /************************************************************************/
36 : /* pop() */
37 : /************************************************************************/
38 :
39 1736 : void OGRDXFFeatureQueue::pop()
40 : {
41 1736 : CPLAssert(!apoFeatures.empty());
42 1736 : apoFeatures.pop();
43 1736 : }
44 :
45 : /************************************************************************/
46 : /* OGRDXFLayer() */
47 : /************************************************************************/
48 :
49 167 : OGRDXFLayer::OGRDXFLayer(OGRDXFDataSource *poDSIn)
50 167 : : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("entities")), iNextFID(0)
51 : {
52 167 : poFeatureDefn->Reference();
53 :
54 167 : int nModes = ODFM_None;
55 167 : if (!poDS->InlineBlocks())
56 24 : nModes |= ODFM_IncludeBlockFields;
57 167 : if (poDS->ShouldIncludeRawCodeValues())
58 1 : nModes |= ODFM_IncludeRawCodeValues;
59 167 : if (poDS->In3DExtensibleMode())
60 1 : nModes |= ODFM_Include3DModeFields;
61 167 : OGRDXFDataSource::AddStandardFields(poFeatureDefn, nModes);
62 :
63 167 : SetDescription(poFeatureDefn->GetName());
64 167 : }
65 :
66 : /************************************************************************/
67 : /* ~OGRDXFLayer() */
68 : /************************************************************************/
69 :
70 314 : OGRDXFLayer::~OGRDXFLayer()
71 :
72 : {
73 167 : ClearPendingFeatures();
74 167 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
75 : {
76 70 : CPLDebug("DXF", "%d features read on layer '%s'.", (int)m_nFeaturesRead,
77 70 : poFeatureDefn->GetName());
78 : }
79 :
80 167 : if (poFeatureDefn)
81 167 : poFeatureDefn->Release();
82 314 : }
83 :
84 : /************************************************************************/
85 : /* ClearPendingFeatures() */
86 : /************************************************************************/
87 :
88 379 : void OGRDXFLayer::ClearPendingFeatures()
89 :
90 : {
91 379 : while (!apoPendingFeatures.empty())
92 : {
93 55 : OGRDXFFeature *poFeature = apoPendingFeatures.front();
94 55 : apoPendingFeatures.pop();
95 55 : delete poFeature;
96 : }
97 324 : }
98 :
99 : /************************************************************************/
100 : /* ResetReading() */
101 : /************************************************************************/
102 :
103 157 : void OGRDXFLayer::ResetReading()
104 :
105 : {
106 157 : iNextFID = 0;
107 157 : ClearPendingFeatures();
108 157 : m_oInsertState.m_nRowCount = 0;
109 157 : m_oInsertState.m_nColumnCount = 0;
110 157 : poDS->RestartEntities();
111 157 : }
112 :
113 : /************************************************************************/
114 : /* TranslateGenericProperty() */
115 : /* */
116 : /* Try and convert entity properties handled similarly for most */
117 : /* or all entity types. */
118 : /************************************************************************/
119 :
120 6555 : void OGRDXFLayer::TranslateGenericProperty(OGRDXFFeature *poFeature, int nCode,
121 : char *pszValue)
122 :
123 : {
124 6555 : switch (nCode)
125 : {
126 698 : case 8:
127 698 : poFeature->SetField("Layer", TextRecode(pszValue));
128 698 : break;
129 :
130 1324 : case 100:
131 : {
132 2648 : CPLString osSubClass = poFeature->GetFieldAsString("SubClasses");
133 1324 : if (!osSubClass.empty())
134 717 : osSubClass += ":";
135 1324 : osSubClass += pszValue;
136 1324 : poFeature->SetField("SubClasses", osSubClass.c_str());
137 : }
138 1324 : break;
139 :
140 38 : case 101:
141 : // Embedded objects mark the end of meaningful DXF data
142 : // See
143 : // http://docs.autodesk.com/ACDMAC/2016/ENU/ObjectARX_Dev_Guide/files/GUID-C953866F-A335-4FFD-AE8C-256A76065552.htm
144 : {
145 : char szLineBuf[257];
146 : // Eat the rest of this entity
147 38 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) >
148 : 0)
149 : {
150 : }
151 :
152 2 : if (nCode < 0)
153 : {
154 : // Let the entity reader function discover this error for
155 : // itself
156 0 : return;
157 : }
158 :
159 2 : CPLAssert(nCode == 0);
160 2 : poDS->UnreadValue();
161 : }
162 2 : break;
163 :
164 5 : case 60:
165 5 : if (atoi(pszValue))
166 5 : poFeature->oStyleProperties["Hidden"] = "1";
167 5 : break;
168 :
169 12 : case 67:
170 12 : if (atoi(pszValue))
171 12 : poFeature->SetField("PaperSpace", 1);
172 12 : break;
173 :
174 318 : case 62:
175 318 : poFeature->oStyleProperties["Color"] = pszValue;
176 318 : break;
177 :
178 12 : case 420:
179 12 : poFeature->oStyleProperties["TrueColor"] = pszValue;
180 12 : break;
181 :
182 256 : case 6:
183 256 : poFeature->SetField("Linetype", TextRecode(pszValue));
184 256 : break;
185 :
186 7 : case 48:
187 7 : poFeature->oStyleProperties["LinetypeScale"] = pszValue;
188 7 : break;
189 :
190 202 : case 370:
191 : case 39:
192 202 : poFeature->oStyleProperties["LineWeight"] = pszValue;
193 202 : break;
194 :
195 689 : case 5:
196 689 : poFeature->SetField("EntityHandle", pszValue);
197 689 : break;
198 :
199 : // OCS vector.
200 145 : case 210:
201 145 : poFeature->oOCS.dfX = CPLAtof(pszValue);
202 145 : break;
203 :
204 145 : case 220:
205 145 : poFeature->oOCS.dfY = CPLAtof(pszValue);
206 145 : break;
207 :
208 145 : case 230:
209 145 : poFeature->oOCS.dfZ = CPLAtof(pszValue);
210 145 : break;
211 :
212 2595 : default:
213 2595 : if (poDS->ShouldIncludeRawCodeValues())
214 : {
215 : char **papszRawCodeValues =
216 479 : poFeature->GetFieldAsStringList("RawCodeValues");
217 :
218 479 : papszRawCodeValues = CSLDuplicate(papszRawCodeValues);
219 :
220 479 : papszRawCodeValues = CSLAddString(
221 : papszRawCodeValues,
222 479 : CPLString()
223 958 : .Printf("%d %s", nCode, TextRecode(pszValue).c_str())
224 : .c_str());
225 :
226 479 : poFeature->SetField("RawCodeValues", papszRawCodeValues);
227 :
228 479 : CSLDestroy(papszRawCodeValues);
229 : }
230 2595 : break;
231 : }
232 : }
233 :
234 : /************************************************************************/
235 : /* PrepareFeatureStyle() */
236 : /* */
237 : /* - poBlockFeature: If this is not NULL, style properties on */
238 : /* poFeature with ByBlock values will be replaced with the */
239 : /* corresponding property from poBlockFeature. If this */
240 : /* parameter is supplied it is assumed that poFeature is a */
241 : /* clone, not an "original" feature object. */
242 : /************************************************************************/
243 :
244 568 : void OGRDXFLayer::PrepareFeatureStyle(
245 : OGRDXFFeature *const poFeature,
246 : OGRDXFFeature *const poBlockFeature /* = NULL */)
247 :
248 : {
249 568 : const char *pszStyleString = poFeature->GetStyleString();
250 :
251 568 : if (pszStyleString && STARTS_WITH_CI(pszStyleString, "BRUSH("))
252 : {
253 51 : PrepareBrushStyle(poFeature, poBlockFeature);
254 : }
255 517 : else if (pszStyleString && STARTS_WITH_CI(pszStyleString, "LABEL("))
256 : {
257 : // Find the new color of this feature, and replace it into
258 : // the style string
259 128 : const CPLString osNewColor = poFeature->GetColor(poDS, poBlockFeature);
260 :
261 128 : CPLString osNewStyle = pszStyleString;
262 64 : const size_t nColorStartPos = osNewStyle.rfind(",c:");
263 64 : if (nColorStartPos != std::string::npos)
264 : {
265 : const size_t nColorEndPos =
266 64 : osNewStyle.find_first_of(",)", nColorStartPos + 3);
267 :
268 64 : if (nColorEndPos != std::string::npos)
269 : {
270 : osNewStyle.replace(nColorStartPos + 3,
271 64 : nColorEndPos - (nColorStartPos + 3),
272 64 : osNewColor);
273 64 : poFeature->SetStyleString(osNewStyle);
274 : }
275 64 : }
276 : }
277 : else
278 : {
279 453 : PrepareLineStyle(poFeature, poBlockFeature);
280 : }
281 568 : }
282 :
283 : /************************************************************************/
284 : /* PrepareBrushStyle() */
285 : /************************************************************************/
286 :
287 147 : void OGRDXFLayer::PrepareBrushStyle(
288 : OGRDXFFeature *const poFeature,
289 : OGRDXFFeature *const poBlockFeature /* = NULL */)
290 :
291 : {
292 294 : CPLString osStyle = "BRUSH(fc:";
293 147 : osStyle += poFeature->GetColor(poDS, poBlockFeature);
294 147 : osStyle += ")";
295 :
296 147 : poFeature->SetStyleString(osStyle);
297 147 : }
298 :
299 : /************************************************************************/
300 : /* PrepareLineStyle() */
301 : /************************************************************************/
302 :
303 930 : void OGRDXFLayer::PrepareLineStyle(
304 : OGRDXFFeature *const poFeature,
305 : OGRDXFFeature *const poBlockFeature /* = NULL */)
306 :
307 : {
308 1860 : const CPLString osLayer = poFeature->GetFieldAsString("Layer");
309 :
310 : /* -------------------------------------------------------------------- */
311 : /* Get line weight if available. */
312 : /* -------------------------------------------------------------------- */
313 930 : double dfWeight = 0.0;
314 1860 : CPLString osWeight = "-1";
315 :
316 930 : if (poFeature->oStyleProperties.count("LineWeight") > 0)
317 268 : osWeight = poFeature->oStyleProperties["LineWeight"];
318 :
319 : // Use ByBlock lineweight?
320 930 : if (CPLAtof(osWeight) == -2 && poBlockFeature)
321 : {
322 97 : if (poBlockFeature->oStyleProperties.count("LineWeight") > 0)
323 : {
324 : // Inherit lineweight from the owning block
325 14 : osWeight = poBlockFeature->oStyleProperties["LineWeight"];
326 :
327 : // Use the inherited lineweight if we regenerate the style
328 : // string again during block insertion
329 14 : poFeature->oStyleProperties["LineWeight"] = osWeight;
330 : }
331 : else
332 : {
333 : // If the owning block has no explicit lineweight,
334 : // assume ByLayer
335 83 : osWeight = "-1";
336 : }
337 : }
338 :
339 : // Use layer lineweight?
340 930 : if (CPLAtof(osWeight) == -1)
341 : {
342 881 : osWeight = poDS->LookupLayerProperty(osLayer, "LineWeight");
343 : }
344 :
345 : // Will be zero in the case of an invalid value
346 930 : dfWeight = CPLAtof(osWeight) / 100.0;
347 :
348 : /* -------------------------------------------------------------------- */
349 : /* Do we have a dash/dot line style? */
350 : /* -------------------------------------------------------------------- */
351 930 : const char *pszLinetype = poFeature->GetFieldAsString("Linetype");
352 :
353 : // Use ByBlock line style?
354 930 : if (pszLinetype && EQUAL(pszLinetype, "ByBlock") && poBlockFeature)
355 : {
356 27 : pszLinetype = poBlockFeature->GetFieldAsString("Linetype");
357 :
358 : // Use the inherited line style if we regenerate the style string
359 : // again during block insertion
360 27 : if (pszLinetype)
361 27 : poFeature->SetField("Linetype", pszLinetype);
362 : }
363 :
364 : // Use layer line style?
365 930 : if (pszLinetype && EQUAL(pszLinetype, ""))
366 : {
367 655 : pszLinetype = poDS->LookupLayerProperty(osLayer, "Linetype");
368 : }
369 :
370 1860 : const std::vector<double> oLineType = poDS->LookupLineType(pszLinetype);
371 :
372 : // Linetype scale is not inherited from the block feature
373 930 : double dfLineTypeScale = CPLAtof(poDS->GetVariable("$LTSCALE", "1.0"));
374 930 : if (poFeature->oStyleProperties.count("LinetypeScale") > 0)
375 16 : dfLineTypeScale *=
376 16 : CPLAtof(poFeature->oStyleProperties["LinetypeScale"]);
377 :
378 1860 : CPLString osPattern;
379 1020 : for (std::vector<double>::const_iterator oIt = oLineType.begin();
380 1020 : oIt != oLineType.end(); ++oIt)
381 : {
382 : // this is the format specifier %g followed by a literal 'g'
383 : osPattern +=
384 90 : CPLString().Printf("%.11gg ", fabs(*oIt) * dfLineTypeScale);
385 : }
386 :
387 930 : if (osPattern.length() > 0)
388 45 : osPattern.erase(osPattern.end() - 1);
389 :
390 : /* -------------------------------------------------------------------- */
391 : /* Format the style string. */
392 : /* -------------------------------------------------------------------- */
393 :
394 1860 : CPLString osStyle = "PEN(c:";
395 930 : osStyle += poFeature->GetColor(poDS, poBlockFeature);
396 :
397 930 : if (dfWeight > 0.0)
398 : {
399 : char szBuffer[64];
400 13 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight);
401 13 : osStyle += CPLString().Printf(",w:%sg", szBuffer);
402 : }
403 :
404 930 : if (osPattern != "")
405 : {
406 45 : osStyle += ",p:\"";
407 45 : osStyle += osPattern;
408 45 : osStyle += "\"";
409 : }
410 :
411 930 : osStyle += ")";
412 :
413 930 : poFeature->SetStyleString(osStyle);
414 930 : }
415 :
416 : /************************************************************************/
417 : /* TextRecode() */
418 : /************************************************************************/
419 :
420 1463 : CPLString OGRDXFLayer::TextRecode(const char *pszInput)
421 :
422 : {
423 2926 : return CPLString(pszInput).Recode(poDS->GetEncoding(), CPL_ENC_UTF8);
424 : }
425 :
426 : /************************************************************************/
427 : /* TextUnescape() */
428 : /* */
429 : /* Unexcape DXF style escape sequences such as \P for newline */
430 : /* and \~ for space, and do the recoding to UTF8. */
431 : /************************************************************************/
432 :
433 148 : CPLString OGRDXFLayer::TextUnescape(const char *pszInput, bool bIsMText)
434 :
435 : {
436 148 : if (poDS->ShouldTranslateEscapes())
437 147 : return ACTextUnescape(pszInput, poDS->GetEncoding(), bIsMText);
438 :
439 1 : return TextRecode(pszInput);
440 : }
441 :
442 : /************************************************************************/
443 : /* TranslateMTEXT() */
444 : /************************************************************************/
445 :
446 40 : OGRDXFFeature *OGRDXFLayer::TranslateMTEXT()
447 :
448 : {
449 : char szLineBuf[512];
450 40 : int nCode = 0;
451 80 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
452 40 : double dfX = 0.0;
453 40 : double dfY = 0.0;
454 40 : double dfZ = 0.0;
455 40 : double dfAngle = 0.0;
456 40 : double dfHeight = 0.0;
457 40 : double dfXDirection = 0.0;
458 40 : double dfYDirection = 0.0;
459 40 : bool bHaveZ = false;
460 40 : int nAttachmentPoint = -1;
461 80 : CPLString osText;
462 80 : CPLString osStyleName = "STANDARD";
463 :
464 802 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
465 : {
466 762 : switch (nCode)
467 : {
468 40 : case 10:
469 40 : dfX = CPLAtof(szLineBuf);
470 40 : break;
471 :
472 40 : case 20:
473 40 : dfY = CPLAtof(szLineBuf);
474 40 : break;
475 :
476 34 : case 30:
477 34 : dfZ = CPLAtof(szLineBuf);
478 34 : bHaveZ = true;
479 34 : break;
480 :
481 40 : case 40:
482 40 : dfHeight = CPLAtof(szLineBuf);
483 40 : break;
484 :
485 37 : case 71:
486 37 : nAttachmentPoint = atoi(szLineBuf);
487 37 : break;
488 :
489 0 : case 11:
490 0 : dfXDirection = CPLAtof(szLineBuf);
491 0 : break;
492 :
493 0 : case 21:
494 0 : dfYDirection = CPLAtof(szLineBuf);
495 0 : dfAngle = atan2(dfYDirection, dfXDirection) * 180.0 / M_PI;
496 0 : break;
497 :
498 42 : case 1:
499 : case 3:
500 42 : osText += TextUnescape(szLineBuf, true);
501 42 : break;
502 :
503 29 : case 50:
504 29 : dfAngle = CPLAtof(szLineBuf);
505 29 : break;
506 :
507 29 : case 7:
508 29 : osStyleName = TextRecode(szLineBuf);
509 29 : break;
510 :
511 471 : default:
512 471 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
513 471 : break;
514 : }
515 : }
516 40 : if (nCode < 0)
517 : {
518 0 : DXF_LAYER_READER_ERROR();
519 0 : return nullptr;
520 : }
521 :
522 40 : if (nCode == 0)
523 40 : poDS->UnreadValue();
524 :
525 40 : OGRPoint *poGeom = nullptr;
526 40 : if (bHaveZ)
527 34 : poGeom = new OGRPoint(dfX, dfY, dfZ);
528 : else
529 6 : poGeom = new OGRPoint(dfX, dfY);
530 :
531 : /* We do NOT apply the OCS for MTEXT. See
532 : * https://trac.osgeo.org/gdal/ticket/7049 */
533 : /* ApplyOCSTransformer( poGeom ); */
534 :
535 40 : poFeature->SetGeometryDirectly(poGeom);
536 :
537 : /* -------------------------------------------------------------------- */
538 : /* Apply text after stripping off any extra terminating newline. */
539 : /* -------------------------------------------------------------------- */
540 40 : if (!osText.empty() && osText.back() == '\n')
541 0 : osText.pop_back();
542 :
543 40 : poFeature->SetField("Text", osText);
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* We need to escape double quotes with backslashes before they */
547 : /* can be inserted in the style string. */
548 : /* -------------------------------------------------------------------- */
549 40 : if (strchr(osText, '"') != nullptr)
550 : {
551 10 : std::string osEscaped;
552 :
553 230 : for (size_t iC = 0; iC < osText.size(); iC++)
554 : {
555 220 : if (osText[iC] == '"')
556 20 : osEscaped += "\\\"";
557 : else
558 200 : osEscaped += osText[iC];
559 : }
560 10 : osText = std::move(osEscaped);
561 : }
562 :
563 : /* -------------------------------------------------------------------- */
564 : /* Prepare style string. */
565 : /* -------------------------------------------------------------------- */
566 80 : CPLString osStyle;
567 : char szBuffer[64];
568 :
569 : // Font name
570 40 : osStyle.Printf("LABEL(f:\"");
571 :
572 : // Preserve legacy behavior of specifying "Arial" as a default font name.
573 40 : osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
574 :
575 40 : osStyle += "\"";
576 :
577 : // Bold, italic
578 40 : if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
579 : {
580 4 : osStyle += ",bo:1";
581 : }
582 40 : if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
583 : {
584 1 : osStyle += ",it:1";
585 : }
586 :
587 : // Text string itself
588 40 : osStyle += ",t:\"";
589 40 : osStyle += osText;
590 40 : osStyle += "\"";
591 :
592 40 : if (dfAngle != 0.0)
593 : {
594 19 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
595 19 : osStyle += CPLString().Printf(",a:%s", szBuffer);
596 : }
597 :
598 40 : if (dfHeight != 0.0)
599 : {
600 40 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
601 40 : osStyle += CPLString().Printf(",s:%sg", szBuffer);
602 : }
603 :
604 : const char *pszWidthFactor =
605 40 : poDS->LookupTextStyleProperty(osStyleName, "Width", "1");
606 40 : if (pszWidthFactor && CPLAtof(pszWidthFactor) != 1.0)
607 : {
608 4 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g",
609 4 : CPLAtof(pszWidthFactor) * 100.0);
610 4 : osStyle += CPLString().Printf(",w:%s", szBuffer);
611 : }
612 :
613 40 : if (nAttachmentPoint >= 0 && nAttachmentPoint <= 9)
614 : {
615 : const static int anAttachmentMap[10] = {-1, 7, 8, 9, 4, 5, 6, 1, 2, 3};
616 :
617 : osStyle +=
618 37 : CPLString().Printf(",p:%d", anAttachmentMap[nAttachmentPoint]);
619 : }
620 :
621 : // Color
622 40 : osStyle += ",c:";
623 40 : osStyle += poFeature->GetColor(poDS);
624 :
625 40 : osStyle += ")";
626 :
627 40 : poFeature->SetStyleString(osStyle);
628 :
629 40 : return poFeature.release();
630 : }
631 :
632 : /************************************************************************/
633 : /* TranslateTEXT() */
634 : /* */
635 : /* This function translates TEXT and ATTRIB entities, as well as */
636 : /* ATTDEF entities when we are not inlining blocks. */
637 : /************************************************************************/
638 :
639 63 : OGRDXFFeature *OGRDXFLayer::TranslateTEXT(const bool bIsAttribOrAttdef)
640 :
641 : {
642 : char szLineBuf[257];
643 63 : int nCode = 0;
644 126 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
645 :
646 63 : double dfX = 0.0;
647 63 : double dfY = 0.0;
648 63 : double dfZ = 0.0;
649 63 : bool bHaveZ = false;
650 :
651 63 : double dfAngle = 0.0;
652 63 : double dfHeight = 0.0;
653 63 : double dfWidthFactor = 1.0;
654 63 : bool bHasAlignmentPoint = false;
655 63 : double dfAlignmentPointX = 0.0;
656 63 : double dfAlignmentPointY = 0.0;
657 :
658 126 : CPLString osText;
659 126 : CPLString osStyleName = "STANDARD";
660 :
661 63 : int nAnchorPosition = 1;
662 63 : int nHorizontalAlignment = 0;
663 63 : int nVerticalAlignment = 0;
664 :
665 958 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
666 : {
667 895 : switch (nCode)
668 : {
669 62 : case 10:
670 62 : dfX = CPLAtof(szLineBuf);
671 62 : break;
672 :
673 62 : case 20:
674 62 : dfY = CPLAtof(szLineBuf);
675 62 : break;
676 :
677 25 : case 11:
678 25 : dfAlignmentPointX = CPLAtof(szLineBuf);
679 25 : break;
680 :
681 25 : case 21:
682 25 : dfAlignmentPointY = CPLAtof(szLineBuf);
683 25 : bHasAlignmentPoint = true;
684 25 : break;
685 :
686 62 : case 30:
687 62 : dfZ = CPLAtof(szLineBuf);
688 62 : bHaveZ = true;
689 62 : break;
690 :
691 62 : case 40:
692 62 : dfHeight = CPLAtof(szLineBuf);
693 62 : break;
694 :
695 12 : case 41:
696 12 : dfWidthFactor = CPLAtof(szLineBuf);
697 12 : break;
698 :
699 62 : case 1:
700 62 : osText += TextUnescape(szLineBuf, false);
701 62 : break;
702 :
703 11 : case 50:
704 11 : dfAngle = CPLAtof(szLineBuf);
705 11 : break;
706 :
707 25 : case 72:
708 25 : nHorizontalAlignment = atoi(szLineBuf);
709 25 : break;
710 :
711 0 : case 73:
712 0 : if (!bIsAttribOrAttdef)
713 0 : nVerticalAlignment = atoi(szLineBuf);
714 0 : break;
715 :
716 8 : case 74:
717 8 : if (bIsAttribOrAttdef)
718 8 : nVerticalAlignment = atoi(szLineBuf);
719 8 : break;
720 :
721 0 : case 7:
722 0 : osStyleName = TextRecode(szLineBuf);
723 0 : break;
724 :
725 : // 2 and 70 are for ATTRIB and ATTDEF entities only
726 37 : case 2:
727 37 : if (bIsAttribOrAttdef)
728 : {
729 : // Attribute tags are not supposed to contain spaces (but
730 : // sometimes they do)
731 37 : while (char *pchSpace = strchr(szLineBuf, ' '))
732 0 : *pchSpace = '_';
733 :
734 37 : poFeature->osAttributeTag = szLineBuf;
735 : }
736 37 : break;
737 :
738 37 : case 70:
739 37 : if (bIsAttribOrAttdef)
740 : {
741 : // When the LSB is set, this ATTRIB is "invisible"
742 37 : if (atoi(szLineBuf) & 1)
743 0 : poFeature->oStyleProperties["Hidden"] = "1";
744 : // If the next bit is set, this ATTDEF is to be preserved
745 : // and treated as constant TEXT
746 37 : else if (atoi(szLineBuf) & 2)
747 2 : poFeature->osAttributeTag.Clear();
748 : }
749 37 : break;
750 :
751 405 : default:
752 405 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
753 405 : break;
754 : }
755 : }
756 63 : if (nCode < 0)
757 : {
758 0 : DXF_LAYER_READER_ERROR();
759 0 : return nullptr;
760 : }
761 :
762 63 : if (nCode == 0)
763 63 : poDS->UnreadValue();
764 :
765 63 : OGRPoint *poGeom = nullptr;
766 63 : if (bHaveZ)
767 62 : poGeom = new OGRPoint(dfX, dfY, dfZ);
768 : else
769 1 : poGeom = new OGRPoint(dfX, dfY);
770 63 : poFeature->ApplyOCSTransformer(poGeom);
771 63 : poFeature->SetGeometryDirectly(poGeom);
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Determine anchor position. */
775 : /* -------------------------------------------------------------------- */
776 63 : if (nHorizontalAlignment > 0 || nVerticalAlignment > 0)
777 : {
778 25 : switch (nVerticalAlignment)
779 : {
780 0 : case 1: // bottom
781 0 : nAnchorPosition = 10;
782 0 : break;
783 :
784 2 : case 2: // middle
785 2 : nAnchorPosition = 4;
786 2 : break;
787 :
788 6 : case 3: // top
789 6 : nAnchorPosition = 7;
790 6 : break;
791 :
792 17 : default:
793 : // Handle "Middle" alignment approximately (this is rather like
794 : // MTEXT alignment in that it uses the actual height of the text
795 : // string to position the text, and thus requires knowledge of
796 : // text metrics)
797 17 : if (nHorizontalAlignment == 4)
798 1 : nAnchorPosition = 5;
799 17 : break;
800 : }
801 25 : if (nHorizontalAlignment < 3)
802 24 : nAnchorPosition += nHorizontalAlignment;
803 : // TODO other alignment options
804 : }
805 :
806 63 : poFeature->SetField("Text", osText);
807 :
808 : /* -------------------------------------------------------------------- */
809 : /* We need to escape double quotes with backslashes before they */
810 : /* can be inserted in the style string. */
811 : /* -------------------------------------------------------------------- */
812 63 : if (strchr(osText, '"') != nullptr)
813 : {
814 0 : CPLString osEscaped;
815 :
816 0 : for (size_t iC = 0; iC < osText.size(); iC++)
817 : {
818 0 : if (osText[iC] == '"')
819 0 : osEscaped += "\\\"";
820 : else
821 0 : osEscaped += osText[iC];
822 : }
823 0 : osText = std::move(osEscaped);
824 : }
825 :
826 : /* -------------------------------------------------------------------- */
827 : /* Prepare style string. */
828 : /* -------------------------------------------------------------------- */
829 126 : CPLString osStyle;
830 : char szBuffer[64];
831 :
832 : // Font name
833 63 : osStyle.Printf("LABEL(f:\"");
834 :
835 : // Preserve legacy behavior of specifying "Arial" as a default font name.
836 63 : osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
837 :
838 63 : osStyle += "\"";
839 :
840 : // Bold, italic
841 63 : if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
842 : {
843 6 : osStyle += ",bo:1";
844 : }
845 63 : if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
846 : {
847 6 : osStyle += ",it:1";
848 : }
849 :
850 : // Text string itself
851 63 : osStyle += ",t:\"";
852 63 : osStyle += osText;
853 63 : osStyle += "\"";
854 :
855 : // Other attributes
856 63 : osStyle += CPLString().Printf(",p:%d", nAnchorPosition);
857 :
858 63 : if (dfAngle != 0.0)
859 : {
860 11 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
861 11 : osStyle += CPLString().Printf(",a:%s", szBuffer);
862 : }
863 :
864 63 : if (dfHeight != 0.0)
865 : {
866 62 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
867 62 : osStyle += CPLString().Printf(",s:%sg", szBuffer);
868 : }
869 :
870 63 : if (dfWidthFactor != 1.0)
871 : {
872 12 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g", dfWidthFactor * 100.0);
873 12 : osStyle += CPLString().Printf(",w:%s", szBuffer);
874 : }
875 :
876 63 : if (bHasAlignmentPoint && dfAlignmentPointX != dfX)
877 : {
878 23 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
879 : dfAlignmentPointX - dfX);
880 23 : osStyle += CPLString().Printf(",dx:%sg", szBuffer);
881 : }
882 :
883 63 : if (bHasAlignmentPoint && dfAlignmentPointY != dfY)
884 : {
885 14 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
886 : dfAlignmentPointY - dfY);
887 14 : osStyle += CPLString().Printf(",dy:%sg", szBuffer);
888 : }
889 :
890 : // Color
891 63 : osStyle += ",c:";
892 63 : osStyle += poFeature->GetColor(poDS);
893 :
894 63 : osStyle += ")";
895 :
896 63 : poFeature->SetStyleString(osStyle);
897 :
898 63 : return poFeature.release();
899 : }
900 :
901 : /************************************************************************/
902 : /* TranslatePOINT() */
903 : /************************************************************************/
904 :
905 33 : OGRDXFFeature *OGRDXFLayer::TranslatePOINT()
906 :
907 : {
908 : char szLineBuf[257];
909 33 : int nCode = 0;
910 66 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
911 33 : double dfX = 0.0;
912 33 : double dfY = 0.0;
913 33 : double dfZ = 0.0;
914 33 : bool bHaveZ = false;
915 :
916 349 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
917 : {
918 316 : switch (nCode)
919 : {
920 33 : case 10:
921 33 : dfX = CPLAtof(szLineBuf);
922 33 : break;
923 :
924 33 : case 20:
925 33 : dfY = CPLAtof(szLineBuf);
926 33 : break;
927 :
928 31 : case 30:
929 31 : dfZ = CPLAtof(szLineBuf);
930 31 : bHaveZ = true;
931 31 : break;
932 :
933 219 : default:
934 219 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
935 219 : break;
936 : }
937 : }
938 33 : if (nCode < 0)
939 : {
940 0 : DXF_LAYER_READER_ERROR();
941 0 : return nullptr;
942 : }
943 :
944 33 : if (nCode == 0)
945 33 : poDS->UnreadValue();
946 :
947 33 : OGRPoint *poGeom = nullptr;
948 33 : if (bHaveZ)
949 31 : poGeom = new OGRPoint(dfX, dfY, dfZ);
950 : else
951 2 : poGeom = new OGRPoint(dfX, dfY);
952 :
953 33 : poFeature->SetGeometryDirectly(poGeom);
954 :
955 : // Set style pen color
956 33 : PrepareLineStyle(poFeature.get());
957 :
958 33 : return poFeature.release();
959 : }
960 :
961 : /************************************************************************/
962 : /* TranslateLINE() */
963 : /************************************************************************/
964 :
965 163 : OGRDXFFeature *OGRDXFLayer::TranslateLINE()
966 :
967 : {
968 : char szLineBuf[257];
969 163 : int nCode = 0;
970 326 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
971 163 : double dfX1 = 0.0;
972 163 : double dfY1 = 0.0;
973 163 : double dfZ1 = 0.0;
974 163 : double dfX2 = 0.0;
975 163 : double dfY2 = 0.0;
976 163 : double dfZ2 = 0.0;
977 163 : bool bHaveZ = false;
978 :
979 : /* -------------------------------------------------------------------- */
980 : /* Process values. */
981 : /* -------------------------------------------------------------------- */
982 2209 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
983 : {
984 2046 : switch (nCode)
985 : {
986 163 : case 10:
987 163 : dfX1 = CPLAtof(szLineBuf);
988 163 : break;
989 :
990 163 : case 11:
991 163 : dfX2 = CPLAtof(szLineBuf);
992 163 : break;
993 :
994 163 : case 20:
995 163 : dfY1 = CPLAtof(szLineBuf);
996 163 : break;
997 :
998 163 : case 21:
999 163 : dfY2 = CPLAtof(szLineBuf);
1000 163 : break;
1001 :
1002 163 : case 30:
1003 163 : dfZ1 = CPLAtof(szLineBuf);
1004 163 : bHaveZ = true;
1005 163 : break;
1006 :
1007 163 : case 31:
1008 163 : dfZ2 = CPLAtof(szLineBuf);
1009 163 : bHaveZ = true;
1010 163 : break;
1011 :
1012 1068 : default:
1013 1068 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1014 1068 : break;
1015 : }
1016 : }
1017 163 : if (nCode < 0)
1018 : {
1019 0 : DXF_LAYER_READER_ERROR();
1020 0 : return nullptr;
1021 : }
1022 :
1023 163 : if (nCode == 0)
1024 163 : poDS->UnreadValue();
1025 :
1026 : /* -------------------------------------------------------------------- */
1027 : /* Create geometry */
1028 : /* -------------------------------------------------------------------- */
1029 326 : auto poLS = std::make_unique<OGRLineString>();
1030 163 : if (bHaveZ)
1031 : {
1032 163 : poLS->addPoint(dfX1, dfY1, dfZ1);
1033 163 : poLS->addPoint(dfX2, dfY2, dfZ2);
1034 : }
1035 : else
1036 : {
1037 0 : poLS->addPoint(dfX1, dfY1);
1038 0 : poLS->addPoint(dfX2, dfY2);
1039 : }
1040 :
1041 163 : poFeature->SetGeometryDirectly(poLS.release());
1042 :
1043 163 : PrepareLineStyle(poFeature.get());
1044 :
1045 163 : return poFeature.release();
1046 : }
1047 :
1048 : /************************************************************************/
1049 : /* TranslateLWPOLYLINE() */
1050 : /************************************************************************/
1051 37 : OGRDXFFeature *OGRDXFLayer::TranslateLWPOLYLINE()
1052 :
1053 : {
1054 : // Collect vertices and attributes into a smooth polyline.
1055 : // If there are no bulges, then we are a straight-line polyline.
1056 : // Single-vertex polylines become points.
1057 : // Group code 30 (vertex Z) is not part of this entity.
1058 : char szLineBuf[257];
1059 37 : int nCode = 0;
1060 37 : int nPolylineFlag = 0;
1061 :
1062 74 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1063 37 : double dfX = 0.0;
1064 37 : double dfY = 0.0;
1065 37 : double dfZ = 0.0;
1066 37 : bool bHaveX = false;
1067 37 : bool bHaveY = false;
1068 :
1069 37 : int nNumVertices = 1; // use 1 based index
1070 37 : int npolyarcVertexCount = 1;
1071 37 : double dfBulge = 0.0;
1072 74 : DXFSmoothPolyline smoothPolyline;
1073 :
1074 37 : smoothPolyline.setCoordinateDimension(2);
1075 :
1076 : /* -------------------------------------------------------------------- */
1077 : /* Collect information from the LWPOLYLINE object itself. */
1078 : /* -------------------------------------------------------------------- */
1079 675 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1080 : {
1081 638 : if (npolyarcVertexCount > nNumVertices)
1082 : {
1083 0 : CPLError(CE_Failure, CPLE_AppDefined,
1084 : "Too many vertices found in LWPOLYLINE.");
1085 0 : return nullptr;
1086 : }
1087 :
1088 638 : switch (nCode)
1089 : {
1090 8 : case 38:
1091 : // Constant elevation.
1092 8 : dfZ = CPLAtof(szLineBuf);
1093 8 : smoothPolyline.setCoordinateDimension(3);
1094 8 : break;
1095 :
1096 37 : case 90:
1097 37 : nNumVertices = atoi(szLineBuf);
1098 37 : break;
1099 :
1100 37 : case 70:
1101 37 : nPolylineFlag = atoi(szLineBuf);
1102 37 : break;
1103 :
1104 128 : case 10:
1105 128 : if (bHaveX && bHaveY)
1106 : {
1107 91 : smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1108 91 : npolyarcVertexCount++;
1109 91 : dfBulge = 0.0;
1110 91 : bHaveY = false;
1111 : }
1112 128 : dfX = CPLAtof(szLineBuf);
1113 128 : bHaveX = true;
1114 128 : break;
1115 :
1116 128 : case 20:
1117 128 : if (bHaveX && bHaveY)
1118 : {
1119 0 : smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1120 0 : npolyarcVertexCount++;
1121 0 : dfBulge = 0.0;
1122 0 : bHaveX = false;
1123 : }
1124 128 : dfY = CPLAtof(szLineBuf);
1125 128 : bHaveY = true;
1126 128 : break;
1127 :
1128 14 : case 42:
1129 14 : dfBulge = CPLAtof(szLineBuf);
1130 14 : break;
1131 :
1132 286 : default:
1133 286 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1134 286 : break;
1135 : }
1136 : }
1137 37 : if (nCode < 0)
1138 : {
1139 0 : DXF_LAYER_READER_ERROR();
1140 0 : return nullptr;
1141 : }
1142 :
1143 37 : if (nCode == 0)
1144 37 : poDS->UnreadValue();
1145 :
1146 37 : if (bHaveX && bHaveY)
1147 37 : smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1148 :
1149 37 : if (smoothPolyline.IsEmpty())
1150 : {
1151 0 : return nullptr;
1152 : }
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* Close polyline if necessary. */
1156 : /* -------------------------------------------------------------------- */
1157 37 : const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
1158 37 : if (bIsClosed)
1159 15 : smoothPolyline.Close();
1160 :
1161 37 : const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
1162 :
1163 37 : smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
1164 : auto poGeom =
1165 74 : std::unique_ptr<OGRGeometry>(smoothPolyline.Tessellate(bAsPolygon));
1166 37 : poFeature->ApplyOCSTransformer(poGeom.get());
1167 37 : poFeature->SetGeometryDirectly(poGeom.release());
1168 :
1169 37 : PrepareLineStyle(poFeature.get());
1170 :
1171 37 : return poFeature.release();
1172 : }
1173 :
1174 : /************************************************************************/
1175 : /* SafeAbs() */
1176 : /************************************************************************/
1177 :
1178 24 : static inline int SafeAbs(int x)
1179 : {
1180 24 : if (x == std::numeric_limits<int>::min())
1181 0 : return std::numeric_limits<int>::max();
1182 24 : return abs(x);
1183 : }
1184 :
1185 : /************************************************************************/
1186 : /* TranslatePOLYLINE() */
1187 : /* */
1188 : /* We also capture the following vertices. */
1189 : /************************************************************************/
1190 :
1191 18 : OGRDXFFeature *OGRDXFLayer::TranslatePOLYLINE()
1192 :
1193 : {
1194 : char szLineBuf[257];
1195 18 : int nCode = 0;
1196 18 : int nPolylineFlag = 0;
1197 36 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1198 :
1199 : /* -------------------------------------------------------------------- */
1200 : /* Collect information from the POLYLINE object itself. */
1201 : /* -------------------------------------------------------------------- */
1202 208 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1203 : {
1204 190 : switch (nCode)
1205 : {
1206 18 : case 70:
1207 18 : nPolylineFlag = atoi(szLineBuf);
1208 18 : break;
1209 :
1210 172 : default:
1211 172 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1212 172 : break;
1213 : }
1214 : }
1215 18 : if (nCode < 0)
1216 : {
1217 0 : DXF_LAYER_READER_ERROR();
1218 0 : return nullptr;
1219 : }
1220 :
1221 18 : if ((nPolylineFlag & 16) != 0)
1222 : {
1223 0 : CPLDebug("DXF", "Polygon mesh not supported.");
1224 0 : return nullptr;
1225 : }
1226 :
1227 : /* -------------------------------------------------------------------- */
1228 : /* Collect vertices as a smooth polyline. */
1229 : /* -------------------------------------------------------------------- */
1230 18 : double dfX = 0.0;
1231 18 : double dfY = 0.0;
1232 18 : double dfZ = 0.0;
1233 18 : double dfBulge = 0.0;
1234 18 : int nVertexFlag = 0;
1235 36 : DXFSmoothPolyline smoothPolyline;
1236 18 : unsigned int vertexIndex71 = 0;
1237 18 : unsigned int vertexIndex72 = 0;
1238 18 : unsigned int vertexIndex73 = 0;
1239 18 : unsigned int vertexIndex74 = 0;
1240 36 : std::vector<OGRPoint> aoPoints;
1241 36 : auto poPS = std::make_unique<OGRPolyhedralSurface>();
1242 :
1243 18 : smoothPolyline.setCoordinateDimension(2);
1244 :
1245 93 : while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
1246 : {
1247 : // Eat non-vertex objects.
1248 75 : if (!EQUAL(szLineBuf, "VERTEX"))
1249 : {
1250 0 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1251 : {
1252 : }
1253 0 : if (nCode < 0)
1254 : {
1255 0 : DXF_LAYER_READER_ERROR();
1256 0 : return nullptr;
1257 : }
1258 :
1259 0 : continue;
1260 : }
1261 :
1262 : // process a Vertex
1263 874 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1264 : {
1265 799 : switch (nCode)
1266 : {
1267 75 : case 10:
1268 75 : dfX = CPLAtof(szLineBuf);
1269 75 : break;
1270 :
1271 75 : case 20:
1272 75 : dfY = CPLAtof(szLineBuf);
1273 75 : break;
1274 :
1275 75 : case 30:
1276 75 : dfZ = CPLAtof(szLineBuf);
1277 75 : smoothPolyline.setCoordinateDimension(3);
1278 75 : break;
1279 :
1280 4 : case 42:
1281 4 : dfBulge = CPLAtof(szLineBuf);
1282 4 : break;
1283 :
1284 64 : case 70:
1285 64 : nVertexFlag = atoi(szLineBuf);
1286 64 : break;
1287 :
1288 6 : case 71:
1289 : // See comment below about negative values for 71, 72, 73,
1290 : // 74
1291 6 : vertexIndex71 = SafeAbs(atoi(szLineBuf));
1292 6 : break;
1293 :
1294 6 : case 72:
1295 6 : vertexIndex72 = SafeAbs(atoi(szLineBuf));
1296 6 : break;
1297 :
1298 6 : case 73:
1299 6 : vertexIndex73 = SafeAbs(atoi(szLineBuf));
1300 6 : break;
1301 :
1302 6 : case 74:
1303 6 : vertexIndex74 = SafeAbs(atoi(szLineBuf));
1304 6 : break;
1305 :
1306 482 : default:
1307 482 : break;
1308 : }
1309 : }
1310 :
1311 75 : if (((nVertexFlag & 64) != 0) && ((nVertexFlag & 128) != 0))
1312 : {
1313 : // add the point to the list of points
1314 : try
1315 : {
1316 8 : aoPoints.emplace_back(dfX, dfY, dfZ);
1317 : }
1318 0 : catch (const std::exception &e)
1319 : {
1320 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1321 0 : return nullptr;
1322 : }
1323 : }
1324 :
1325 : // Note - If any index out of vertexIndex71, vertexIndex72,
1326 : // vertexIndex73 or vertexIndex74 is negative, it means that the line
1327 : // starting from that vertex is invisible. However, it still needs to be
1328 : // constructed as part of the resultant polyhedral surface; there is no
1329 : // way to specify the visibility of individual edges in a polyhedral
1330 : // surface at present
1331 :
1332 75 : if (nVertexFlag == 128)
1333 : {
1334 : // create a polygon and add it to the Polyhedral Surface
1335 12 : auto poLR = std::make_unique<OGRLinearRing>();
1336 6 : int iPoint = 0;
1337 6 : int startPoint = -1;
1338 6 : poLR->set3D(TRUE);
1339 6 : if (vertexIndex71 != 0 && vertexIndex71 <= aoPoints.size())
1340 : {
1341 : // if (startPoint == -1)
1342 6 : startPoint = vertexIndex71 - 1;
1343 6 : poLR->setPoint(iPoint, &aoPoints[vertexIndex71 - 1]);
1344 6 : iPoint++;
1345 6 : vertexIndex71 = 0;
1346 : }
1347 6 : if (vertexIndex72 != 0 && vertexIndex72 <= aoPoints.size())
1348 : {
1349 6 : if (startPoint == -1)
1350 0 : startPoint = vertexIndex72 - 1;
1351 6 : poLR->setPoint(iPoint, &aoPoints[vertexIndex72 - 1]);
1352 6 : iPoint++;
1353 6 : vertexIndex72 = 0;
1354 : }
1355 6 : if (vertexIndex73 != 0 && vertexIndex73 <= aoPoints.size())
1356 : {
1357 6 : if (startPoint == -1)
1358 0 : startPoint = vertexIndex73 - 1;
1359 6 : poLR->setPoint(iPoint, &aoPoints[vertexIndex73 - 1]);
1360 6 : iPoint++;
1361 6 : vertexIndex73 = 0;
1362 : }
1363 6 : if (vertexIndex74 != 0 && vertexIndex74 <= aoPoints.size())
1364 : {
1365 6 : if (startPoint == -1)
1366 0 : startPoint = vertexIndex74 - 1;
1367 6 : poLR->setPoint(iPoint, &aoPoints[vertexIndex74 - 1]);
1368 6 : iPoint++;
1369 6 : vertexIndex74 = 0;
1370 : }
1371 6 : if (startPoint >= 0)
1372 : {
1373 : // complete the ring
1374 6 : poLR->setPoint(iPoint, &aoPoints[startPoint]);
1375 :
1376 6 : OGRPolygon *poPolygon = new OGRPolygon();
1377 6 : poPolygon->addRingDirectly(poLR.release());
1378 :
1379 6 : poPS->addGeometryDirectly(poPolygon);
1380 : }
1381 : }
1382 :
1383 75 : if (nCode < 0)
1384 : {
1385 0 : DXF_LAYER_READER_ERROR();
1386 0 : return nullptr;
1387 : }
1388 :
1389 : // Ignore Spline frame control points ( see #4683 )
1390 75 : if ((nVertexFlag & 16) == 0)
1391 75 : smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1392 75 : dfBulge = 0.0;
1393 : }
1394 :
1395 18 : if (smoothPolyline.IsEmpty())
1396 : {
1397 0 : return nullptr;
1398 : }
1399 :
1400 18 : if (poPS->getNumGeometries() > 0)
1401 : {
1402 1 : poFeature->SetGeometryDirectly(poPS.release());
1403 1 : PrepareBrushStyle(poFeature.get());
1404 1 : return poFeature.release();
1405 : }
1406 :
1407 : /* -------------------------------------------------------------------- */
1408 : /* Close polyline if necessary. */
1409 : /* -------------------------------------------------------------------- */
1410 17 : const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
1411 17 : if (bIsClosed)
1412 10 : smoothPolyline.Close();
1413 :
1414 17 : const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
1415 :
1416 17 : smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
1417 17 : OGRGeometry *poGeom = smoothPolyline.Tessellate(bAsPolygon);
1418 :
1419 17 : if ((nPolylineFlag & 8) == 0)
1420 2 : poFeature->ApplyOCSTransformer(poGeom);
1421 17 : poFeature->SetGeometryDirectly(poGeom);
1422 :
1423 17 : PrepareLineStyle(poFeature.get());
1424 :
1425 17 : return poFeature.release();
1426 : }
1427 :
1428 : /************************************************************************/
1429 : /* TranslateMLINE() */
1430 : /************************************************************************/
1431 :
1432 3 : OGRDXFFeature *OGRDXFLayer::TranslateMLINE()
1433 :
1434 : {
1435 : char szLineBuf[257];
1436 3 : int nCode = 0;
1437 :
1438 6 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1439 :
1440 3 : bool bIsClosed = false;
1441 3 : int nNumVertices = 0;
1442 3 : int nNumElements = 0;
1443 :
1444 57 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0 &&
1445 : nCode != 11)
1446 : {
1447 54 : switch (nCode)
1448 : {
1449 3 : case 71:
1450 3 : bIsClosed = (atoi(szLineBuf) & 2) == 2;
1451 3 : break;
1452 :
1453 3 : case 72:
1454 3 : nNumVertices = atoi(szLineBuf);
1455 3 : break;
1456 :
1457 3 : case 73:
1458 3 : nNumElements = atoi(szLineBuf);
1459 : // No-one should ever need more than 1000 elements!
1460 3 : if (nNumElements <= 0 || nNumElements > 1000)
1461 : {
1462 0 : CPLDebug("DXF", "Invalid number of MLINE elements (73): %s",
1463 : szLineBuf);
1464 0 : DXF_LAYER_READER_ERROR();
1465 0 : return nullptr;
1466 : }
1467 3 : break;
1468 :
1469 45 : default:
1470 45 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1471 45 : break;
1472 : }
1473 : }
1474 3 : if (nCode < 0)
1475 : {
1476 0 : DXF_LAYER_READER_ERROR();
1477 0 : return nullptr;
1478 : }
1479 :
1480 3 : if (nCode == 0 || nCode == 11)
1481 3 : poDS->UnreadValue();
1482 :
1483 : /* -------------------------------------------------------------------- */
1484 : /* Read in the position and parameters for each vertex, and */
1485 : /* translate these values into line geometries. */
1486 : /* -------------------------------------------------------------------- */
1487 :
1488 6 : auto poMLS = std::make_unique<OGRMultiLineString>();
1489 6 : std::vector<std::unique_ptr<OGRLineString>> apoCurrentLines(nNumElements);
1490 :
1491 : // For use when bIsClosed is true
1492 6 : std::vector<DXFTriple> aoInitialVertices(nNumElements);
1493 :
1494 : #define EXPECT_CODE(code) \
1495 : if (poDS->ReadValue(szLineBuf, sizeof(szLineBuf)) != (code)) \
1496 : { \
1497 : DXF_LAYER_READER_ERROR(); \
1498 : return nullptr; \
1499 : }
1500 :
1501 13 : for (int iVertex = 0; iVertex < nNumVertices; iVertex++)
1502 : {
1503 10 : EXPECT_CODE(11);
1504 10 : const double dfVertexX = CPLAtof(szLineBuf);
1505 10 : EXPECT_CODE(21);
1506 10 : const double dfVertexY = CPLAtof(szLineBuf);
1507 10 : EXPECT_CODE(31);
1508 10 : const double dfVertexZ = CPLAtof(szLineBuf);
1509 :
1510 10 : EXPECT_CODE(12);
1511 10 : const double dfSegmentDirectionX = CPLAtof(szLineBuf);
1512 10 : EXPECT_CODE(22);
1513 10 : const double dfSegmentDirectionY = CPLAtof(szLineBuf);
1514 10 : EXPECT_CODE(32);
1515 10 : const double dfSegmentDirectionZ = CPLAtof(szLineBuf);
1516 :
1517 10 : EXPECT_CODE(13);
1518 10 : const double dfMiterDirectionX = CPLAtof(szLineBuf);
1519 10 : EXPECT_CODE(23);
1520 10 : const double dfMiterDirectionY = CPLAtof(szLineBuf);
1521 10 : EXPECT_CODE(33);
1522 10 : const double dfMiterDirectionZ = CPLAtof(szLineBuf);
1523 :
1524 34 : for (int iElement = 0; iElement < nNumElements; iElement++)
1525 : {
1526 24 : double dfStartSegmentX = 0.0;
1527 24 : double dfStartSegmentY = 0.0;
1528 24 : double dfStartSegmentZ = 0.0;
1529 :
1530 24 : EXPECT_CODE(74);
1531 24 : const int nNumParameters = atoi(szLineBuf);
1532 :
1533 : // The first parameter is special: it is a distance along the
1534 : // miter vector from the initial vertex to the start of the
1535 : // element line.
1536 24 : if (nNumParameters > 0)
1537 : {
1538 24 : EXPECT_CODE(41);
1539 24 : const double dfDistance = CPLAtof(szLineBuf);
1540 :
1541 24 : dfStartSegmentX = dfVertexX + dfMiterDirectionX * dfDistance;
1542 24 : dfStartSegmentY = dfVertexY + dfMiterDirectionY * dfDistance;
1543 24 : dfStartSegmentZ = dfVertexZ + dfMiterDirectionZ * dfDistance;
1544 :
1545 24 : if (bIsClosed && iVertex == 0)
1546 : {
1547 3 : aoInitialVertices[iElement] = DXFTriple(
1548 : dfStartSegmentX, dfStartSegmentY, dfStartSegmentZ);
1549 : }
1550 :
1551 : // If we have an unfinished line for this element, we need
1552 : // to close it off.
1553 24 : if (apoCurrentLines[iElement])
1554 : {
1555 16 : apoCurrentLines[iElement]->addPoint(
1556 : dfStartSegmentX, dfStartSegmentY, dfStartSegmentZ);
1557 32 : poMLS->addGeometryDirectly(
1558 16 : apoCurrentLines[iElement].release());
1559 : }
1560 : }
1561 :
1562 : // Parameters with an odd index give pen-up distances (breaks),
1563 : // while even indexes are pen-down distances (line segments).
1564 61 : for (int iParameter = 1; iParameter < nNumParameters; iParameter++)
1565 : {
1566 37 : EXPECT_CODE(41);
1567 37 : const double dfDistance = CPLAtof(szLineBuf);
1568 :
1569 37 : const double dfCurrentX =
1570 37 : dfStartSegmentX + dfSegmentDirectionX * dfDistance;
1571 37 : const double dfCurrentY =
1572 37 : dfStartSegmentY + dfSegmentDirectionY * dfDistance;
1573 37 : const double dfCurrentZ =
1574 37 : dfStartSegmentZ + dfSegmentDirectionZ * dfDistance;
1575 :
1576 37 : if (iParameter % 2 == 0)
1577 : {
1578 : // The dfCurrent(X,Y,Z) point is the end of a line segment
1579 7 : CPLAssert(apoCurrentLines[iElement]);
1580 7 : apoCurrentLines[iElement]->addPoint(dfCurrentX, dfCurrentY,
1581 : dfCurrentZ);
1582 14 : poMLS->addGeometryDirectly(
1583 7 : apoCurrentLines[iElement].release());
1584 : }
1585 : else
1586 : {
1587 : // The dfCurrent(X,Y,Z) point is the end of a break
1588 30 : apoCurrentLines[iElement] =
1589 60 : std::make_unique<OGRLineString>();
1590 30 : apoCurrentLines[iElement]->addPoint(dfCurrentX, dfCurrentY,
1591 : dfCurrentZ);
1592 : }
1593 : }
1594 :
1595 24 : EXPECT_CODE(75);
1596 24 : const int nNumAreaFillParams = atoi(szLineBuf);
1597 :
1598 24 : for (int iParameter = 0; iParameter < nNumAreaFillParams;
1599 : iParameter++)
1600 : {
1601 0 : EXPECT_CODE(42);
1602 : }
1603 : }
1604 : }
1605 :
1606 : #undef EXPECT_CODE
1607 :
1608 : // Close the MLINE if required.
1609 3 : if (bIsClosed)
1610 : {
1611 4 : for (int iElement = 0; iElement < nNumElements; iElement++)
1612 : {
1613 3 : if (apoCurrentLines[iElement])
1614 : {
1615 6 : apoCurrentLines[iElement]->addPoint(
1616 3 : aoInitialVertices[iElement].dfX,
1617 3 : aoInitialVertices[iElement].dfY,
1618 3 : aoInitialVertices[iElement].dfZ);
1619 3 : poMLS->addGeometryDirectly(apoCurrentLines[iElement].release());
1620 : }
1621 : }
1622 : }
1623 :
1624 : // Apparently extrusions are ignored for MLINE entities.
1625 : // poFeature->ApplyOCSTransformer( poMLS );
1626 3 : poFeature->SetGeometryDirectly(poMLS.release());
1627 :
1628 3 : PrepareLineStyle(poFeature.get());
1629 :
1630 3 : return poFeature.release();
1631 : }
1632 :
1633 : /************************************************************************/
1634 : /* TranslateCIRCLE() */
1635 : /************************************************************************/
1636 :
1637 23 : OGRDXFFeature *OGRDXFLayer::TranslateCIRCLE()
1638 :
1639 : {
1640 : char szLineBuf[257];
1641 23 : int nCode = 0;
1642 46 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1643 23 : double dfX1 = 0.0;
1644 23 : double dfY1 = 0.0;
1645 23 : double dfZ1 = 0.0;
1646 23 : double dfRadius = 0.0;
1647 23 : double dfThickness = 0.0;
1648 23 : bool bHaveZ = false;
1649 :
1650 : /* -------------------------------------------------------------------- */
1651 : /* Process values. */
1652 : /* -------------------------------------------------------------------- */
1653 236 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1654 : {
1655 213 : switch (nCode)
1656 : {
1657 23 : case 10:
1658 23 : dfX1 = CPLAtof(szLineBuf);
1659 23 : break;
1660 :
1661 23 : case 20:
1662 23 : dfY1 = CPLAtof(szLineBuf);
1663 23 : break;
1664 :
1665 23 : case 30:
1666 23 : dfZ1 = CPLAtof(szLineBuf);
1667 23 : bHaveZ = true;
1668 23 : break;
1669 :
1670 1 : case 39:
1671 1 : dfThickness = CPLAtof(szLineBuf);
1672 1 : break;
1673 :
1674 23 : case 40:
1675 23 : dfRadius = CPLAtof(szLineBuf);
1676 23 : break;
1677 :
1678 120 : default:
1679 120 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1680 120 : break;
1681 : }
1682 : }
1683 23 : if (nCode < 0)
1684 : {
1685 0 : DXF_LAYER_READER_ERROR();
1686 0 : return nullptr;
1687 : }
1688 :
1689 23 : if (nCode == 0)
1690 23 : poDS->UnreadValue();
1691 :
1692 : /* -------------------------------------------------------------------- */
1693 : /* Create geometry */
1694 : /* -------------------------------------------------------------------- */
1695 : auto poCircle = std::unique_ptr<OGRLineString>(
1696 : OGRGeometryFactory::approximateArcAngles(dfX1, dfY1, dfZ1, dfRadius,
1697 : dfRadius, 0.0, 0.0, 360.0, 0.0,
1698 23 : poDS->InlineBlocks())
1699 46 : ->toLineString());
1700 :
1701 23 : const int nPoints = poCircle->getNumPoints();
1702 :
1703 : // If dfThickness is nonzero, we need to extrude a cylinder of height
1704 : // dfThickness in the Z axis.
1705 23 : if (dfThickness != 0.0 && nPoints > 1)
1706 : {
1707 1 : OGRPolyhedralSurface *poSurface = new OGRPolyhedralSurface();
1708 :
1709 : // Add the bottom base as a polygon
1710 1 : OGRLinearRing *poRing1 = new OGRLinearRing();
1711 1 : poRing1->addSubLineString(poCircle.get());
1712 :
1713 1 : OGRPolygon *poBase1 = new OGRPolygon();
1714 1 : poBase1->addRingDirectly(poRing1);
1715 1 : poSurface->addGeometryDirectly(poBase1);
1716 :
1717 : // Create and add the top base
1718 1 : OGRLinearRing *poRing2 = poRing1->clone();
1719 :
1720 1 : OGRDXFInsertTransformer oTransformer;
1721 1 : oTransformer.dfZOffset = dfThickness;
1722 1 : poRing2->transform(&oTransformer);
1723 :
1724 1 : OGRPolygon *poBase2 = new OGRPolygon();
1725 1 : poBase2->addRingDirectly(poRing2);
1726 1 : poSurface->addGeometryDirectly(poBase2);
1727 :
1728 : // Add the side of the cylinder as two "semicylindrical" polygons
1729 2 : auto poRect = std::make_unique<OGRLinearRing>();
1730 2 : OGRPoint oPoint;
1731 :
1732 47 : for (int iPoint = nPoints / 2; iPoint >= 0; iPoint--)
1733 : {
1734 46 : poRing1->getPoint(iPoint, &oPoint);
1735 46 : poRect->addPoint(&oPoint);
1736 : }
1737 47 : for (int iPoint = 0; iPoint <= nPoints / 2; iPoint++)
1738 : {
1739 46 : poRing2->getPoint(iPoint, &oPoint);
1740 46 : poRect->addPoint(&oPoint);
1741 : }
1742 :
1743 1 : poRect->closeRings();
1744 :
1745 1 : OGRPolygon *poRectPolygon = new OGRPolygon();
1746 1 : poRectPolygon->addRingDirectly(poRect.release());
1747 1 : poSurface->addGeometryDirectly(poRectPolygon);
1748 :
1749 1 : poRect = std::make_unique<OGRLinearRing>();
1750 :
1751 47 : for (int iPoint = nPoints - 1; iPoint >= nPoints / 2; iPoint--)
1752 : {
1753 46 : poRing1->getPoint(iPoint, &oPoint);
1754 46 : poRect->addPoint(&oPoint);
1755 : }
1756 47 : for (int iPoint = nPoints / 2; iPoint < nPoints; iPoint++)
1757 : {
1758 46 : poRing2->getPoint(iPoint, &oPoint);
1759 46 : poRect->addPoint(&oPoint);
1760 : }
1761 :
1762 1 : poRect->closeRings();
1763 :
1764 1 : poRectPolygon = new OGRPolygon();
1765 1 : poRectPolygon->addRingDirectly(poRect.release());
1766 1 : poSurface->addGeometryDirectly(poRectPolygon);
1767 :
1768 : // That's your cylinder, folks
1769 1 : poFeature->ApplyOCSTransformer(poSurface);
1770 2 : poFeature->SetGeometryDirectly(poSurface);
1771 : }
1772 : else
1773 : {
1774 22 : if (!bHaveZ)
1775 0 : poCircle->flattenTo2D();
1776 :
1777 22 : poFeature->ApplyOCSTransformer(poCircle.get());
1778 22 : poFeature->SetGeometryDirectly(poCircle.release());
1779 : }
1780 :
1781 23 : PrepareLineStyle(poFeature.get());
1782 :
1783 23 : return poFeature.release();
1784 : }
1785 :
1786 : /************************************************************************/
1787 : /* TranslateELLIPSE() */
1788 : /************************************************************************/
1789 :
1790 29 : OGRDXFFeature *OGRDXFLayer::TranslateELLIPSE()
1791 :
1792 : {
1793 : char szLineBuf[257];
1794 29 : int nCode = 0;
1795 58 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1796 29 : double dfX1 = 0.0;
1797 29 : double dfY1 = 0.0;
1798 29 : double dfZ1 = 0.0;
1799 29 : double dfRatio = 0.0;
1800 29 : double dfStartAngle = 0.0;
1801 29 : double dfEndAngle = 360.0;
1802 29 : double dfAxisX = 0.0;
1803 29 : double dfAxisY = 0.0;
1804 29 : double dfAxisZ = 0.0;
1805 29 : bool bHaveZ = false;
1806 29 : bool bApplyOCSTransform = false;
1807 :
1808 : /* -------------------------------------------------------------------- */
1809 : /* Process values. */
1810 : /* -------------------------------------------------------------------- */
1811 555 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1812 : {
1813 526 : switch (nCode)
1814 : {
1815 29 : case 10:
1816 29 : dfX1 = CPLAtof(szLineBuf);
1817 29 : break;
1818 :
1819 29 : case 20:
1820 29 : dfY1 = CPLAtof(szLineBuf);
1821 29 : break;
1822 :
1823 29 : case 30:
1824 29 : dfZ1 = CPLAtof(szLineBuf);
1825 29 : bHaveZ = true;
1826 29 : break;
1827 :
1828 29 : case 11:
1829 29 : dfAxisX = CPLAtof(szLineBuf);
1830 29 : break;
1831 :
1832 29 : case 21:
1833 29 : dfAxisY = CPLAtof(szLineBuf);
1834 29 : break;
1835 :
1836 29 : case 31:
1837 29 : dfAxisZ = CPLAtof(szLineBuf);
1838 29 : break;
1839 :
1840 29 : case 40:
1841 29 : dfRatio = CPLAtof(szLineBuf);
1842 29 : break;
1843 :
1844 29 : case 41:
1845 : // These *seem* to always be in radians regardless of $AUNITS
1846 29 : dfEndAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
1847 29 : break;
1848 :
1849 29 : case 42:
1850 : // These *seem* to always be in radians regardless of $AUNITS
1851 29 : dfStartAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
1852 29 : break;
1853 :
1854 265 : default:
1855 265 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1856 265 : break;
1857 : }
1858 : }
1859 29 : if (nCode < 0)
1860 : {
1861 0 : DXF_LAYER_READER_ERROR();
1862 0 : return nullptr;
1863 : }
1864 :
1865 29 : if (nCode == 0)
1866 29 : poDS->UnreadValue();
1867 :
1868 : /* -------------------------------------------------------------------- */
1869 : /* Setup coordinate system */
1870 : /* -------------------------------------------------------------------- */
1871 : double adfN[3];
1872 29 : poFeature->oOCS.ToArray(adfN);
1873 :
1874 29 : if ((adfN[0] == 0.0 && adfN[1] == 0.0 && adfN[2] == 1.0) == false)
1875 : {
1876 14 : OGRDXFOCSTransformer oTransformer(adfN, true);
1877 :
1878 7 : bApplyOCSTransform = true;
1879 :
1880 7 : double *x = &dfX1;
1881 7 : double *y = &dfY1;
1882 7 : double *z = &dfZ1;
1883 7 : oTransformer.InverseTransform(1, x, y, z);
1884 :
1885 7 : x = &dfAxisX;
1886 7 : y = &dfAxisY;
1887 7 : z = &dfAxisZ;
1888 7 : oTransformer.InverseTransform(1, x, y, z);
1889 : }
1890 :
1891 : /* -------------------------------------------------------------------- */
1892 : /* Compute primary and secondary axis lengths, and the angle of */
1893 : /* rotation for the ellipse. */
1894 : /* -------------------------------------------------------------------- */
1895 : double dfPrimaryRadius =
1896 29 : sqrt(dfAxisX * dfAxisX + dfAxisY * dfAxisY + dfAxisZ * dfAxisZ);
1897 :
1898 29 : double dfSecondaryRadius = dfRatio * dfPrimaryRadius;
1899 :
1900 29 : double dfRotation = -1 * atan2(dfAxisY, dfAxisX) * 180 / M_PI;
1901 :
1902 : /* -------------------------------------------------------------------- */
1903 : /* Create geometry */
1904 : /* -------------------------------------------------------------------- */
1905 29 : if (dfStartAngle > dfEndAngle)
1906 0 : dfEndAngle += 360.0;
1907 :
1908 29 : if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
1909 : {
1910 : // Only honor OGR_DXF_MAX_GAP if this geometry isn't at risk of
1911 : // being enlarged or shrunk as part of a block insertion.
1912 : auto poEllipse = std::unique_ptr<OGRGeometry>(
1913 : OGRGeometryFactory::approximateArcAngles(
1914 : dfX1, dfY1, dfZ1, dfPrimaryRadius, dfSecondaryRadius,
1915 : dfRotation, dfStartAngle, dfEndAngle, 0.0,
1916 58 : poDS->InlineBlocks()));
1917 :
1918 29 : if (!bHaveZ)
1919 0 : poEllipse->flattenTo2D();
1920 :
1921 29 : if (bApplyOCSTransform == true)
1922 7 : poFeature->ApplyOCSTransformer(poEllipse.get());
1923 29 : poFeature->SetGeometryDirectly(poEllipse.release());
1924 : }
1925 : else
1926 : {
1927 : // TODO: emit error ?
1928 : }
1929 :
1930 29 : PrepareLineStyle(poFeature.get());
1931 :
1932 29 : return poFeature.release();
1933 : }
1934 :
1935 : /************************************************************************/
1936 : /* TranslateARC() */
1937 : /************************************************************************/
1938 :
1939 14 : OGRDXFFeature *OGRDXFLayer::TranslateARC()
1940 :
1941 : {
1942 : char szLineBuf[257];
1943 14 : int nCode = 0;
1944 28 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1945 14 : double dfX1 = 0.0;
1946 14 : double dfY1 = 0.0;
1947 14 : double dfZ1 = 0.0;
1948 14 : double dfRadius = 0.0;
1949 14 : double dfStartAngle = 0.0;
1950 14 : double dfEndAngle = 360.0;
1951 14 : bool bHaveZ = false;
1952 :
1953 : /* -------------------------------------------------------------------- */
1954 : /* Process values. */
1955 : /* -------------------------------------------------------------------- */
1956 202 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1957 : {
1958 188 : switch (nCode)
1959 : {
1960 14 : case 10:
1961 14 : dfX1 = CPLAtof(szLineBuf);
1962 14 : break;
1963 :
1964 14 : case 20:
1965 14 : dfY1 = CPLAtof(szLineBuf);
1966 14 : break;
1967 :
1968 14 : case 30:
1969 14 : dfZ1 = CPLAtof(szLineBuf);
1970 14 : bHaveZ = true;
1971 14 : break;
1972 :
1973 14 : case 40:
1974 14 : dfRadius = CPLAtof(szLineBuf);
1975 14 : break;
1976 :
1977 14 : case 50:
1978 : // This is apparently always degrees regardless of AUNITS
1979 14 : dfEndAngle = -1 * CPLAtof(szLineBuf);
1980 14 : break;
1981 :
1982 14 : case 51:
1983 : // This is apparently always degrees regardless of AUNITS
1984 14 : dfStartAngle = -1 * CPLAtof(szLineBuf);
1985 14 : break;
1986 :
1987 104 : default:
1988 104 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1989 104 : break;
1990 : }
1991 : }
1992 14 : if (nCode < 0)
1993 : {
1994 0 : DXF_LAYER_READER_ERROR();
1995 0 : return nullptr;
1996 : }
1997 :
1998 14 : if (nCode == 0)
1999 14 : poDS->UnreadValue();
2000 :
2001 : /* -------------------------------------------------------------------- */
2002 : /* Create geometry */
2003 : /* -------------------------------------------------------------------- */
2004 14 : if (dfStartAngle > dfEndAngle)
2005 13 : dfEndAngle += 360.0;
2006 :
2007 14 : if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
2008 : {
2009 : auto poArc = std::unique_ptr<OGRGeometry>(
2010 : OGRGeometryFactory::approximateArcAngles(
2011 : dfX1, dfY1, dfZ1, dfRadius, dfRadius, 0.0, dfStartAngle,
2012 28 : dfEndAngle, 0.0, poDS->InlineBlocks()));
2013 14 : if (!bHaveZ)
2014 0 : poArc->flattenTo2D();
2015 :
2016 14 : poFeature->ApplyOCSTransformer(poArc.get());
2017 14 : poFeature->SetGeometryDirectly(poArc.release());
2018 : }
2019 : else
2020 : {
2021 : // TODO: emit error ?
2022 : }
2023 :
2024 14 : PrepareLineStyle(poFeature.get());
2025 :
2026 14 : return poFeature.release();
2027 : }
2028 :
2029 : /************************************************************************/
2030 : /* TranslateSPLINE() */
2031 : /************************************************************************/
2032 :
2033 : void rbspline2(int npts, int k, int p1, double b[], double h[],
2034 : bool bCalculateKnots, double knots[], double p[]);
2035 :
2036 25 : OGRDXFFeature *OGRDXFLayer::TranslateSPLINE()
2037 :
2038 : {
2039 : char szLineBuf[257];
2040 : int nCode;
2041 50 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2042 :
2043 50 : std::vector<double> adfControlPoints(FORTRAN_INDEXING, 0.0);
2044 50 : std::vector<double> adfKnots(FORTRAN_INDEXING, 0.0);
2045 50 : std::vector<double> adfWeights(FORTRAN_INDEXING, 0.0);
2046 25 : int nDegree = -1;
2047 25 : int nControlPoints = -1;
2048 25 : int nKnots = -1;
2049 25 : bool bInsertNullZ = false;
2050 25 : bool bHasZ = false;
2051 :
2052 : /* -------------------------------------------------------------------- */
2053 : /* Process values. */
2054 : /* -------------------------------------------------------------------- */
2055 1363 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2056 : {
2057 1339 : bool bStop = false;
2058 1339 : switch (nCode)
2059 : {
2060 138 : case 10:
2061 138 : if (bInsertNullZ)
2062 : {
2063 0 : adfControlPoints.push_back(0.0);
2064 0 : bInsertNullZ = false;
2065 : }
2066 138 : adfControlPoints.push_back(CPLAtof(szLineBuf));
2067 138 : break;
2068 :
2069 138 : case 20:
2070 138 : adfControlPoints.push_back(CPLAtof(szLineBuf));
2071 138 : bInsertNullZ = true;
2072 138 : break;
2073 :
2074 138 : case 30:
2075 138 : adfControlPoints.push_back(CPLAtof(szLineBuf));
2076 138 : bHasZ = true;
2077 138 : bInsertNullZ = false;
2078 138 : break;
2079 :
2080 227 : case 40:
2081 : {
2082 227 : double dfVal = CPLAtof(szLineBuf);
2083 : // Ad-hoc fix for https://github.com/OSGeo/gdal/issues/1969
2084 : // where the first knot is at a very very close to zero negative
2085 : // value and following knots are at 0.
2086 227 : if (dfVal < 0 && dfVal > -1.0e-10)
2087 1 : dfVal = 0;
2088 227 : adfKnots.push_back(dfVal);
2089 227 : break;
2090 : }
2091 :
2092 10 : case 41:
2093 10 : adfWeights.push_back(CPLAtof(szLineBuf));
2094 10 : break;
2095 :
2096 25 : case 70:
2097 25 : break;
2098 :
2099 25 : case 71:
2100 25 : nDegree = atoi(szLineBuf);
2101 : // Arbitrary threshold
2102 25 : if (nDegree < 0 || nDegree > 100)
2103 : {
2104 0 : DXF_LAYER_READER_ERROR();
2105 0 : return nullptr;
2106 : }
2107 25 : break;
2108 :
2109 25 : case 72:
2110 25 : nKnots = atoi(szLineBuf);
2111 : // Arbitrary threshold
2112 25 : if (nKnots < 0 || nKnots > 10000000)
2113 : {
2114 0 : DXF_LAYER_READER_ERROR();
2115 0 : return nullptr;
2116 : }
2117 25 : break;
2118 :
2119 25 : case 73:
2120 25 : nControlPoints = atoi(szLineBuf);
2121 : // Arbitrary threshold
2122 25 : if (nControlPoints < 0 || nControlPoints > 10000000)
2123 : {
2124 0 : DXF_LAYER_READER_ERROR();
2125 0 : return nullptr;
2126 : }
2127 25 : break;
2128 :
2129 51 : case 100:
2130 51 : if (EQUAL(szLineBuf, "AcDbHelix"))
2131 1 : bStop = true;
2132 51 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2133 51 : break;
2134 :
2135 537 : default:
2136 537 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2137 537 : break;
2138 : }
2139 :
2140 1339 : if (bStop)
2141 1 : break;
2142 : }
2143 25 : if (nCode < 0)
2144 : {
2145 0 : DXF_LAYER_READER_ERROR();
2146 0 : return nullptr;
2147 : }
2148 :
2149 25 : if (nCode == 0)
2150 24 : poDS->UnreadValue();
2151 :
2152 25 : if (bInsertNullZ)
2153 : {
2154 0 : adfControlPoints.push_back(0.0);
2155 : }
2156 :
2157 25 : if (static_cast<int>(adfControlPoints.size() % 3) != FORTRAN_INDEXING)
2158 : {
2159 0 : CPLError(CE_Failure, CPLE_AppDefined,
2160 : "Invalid number of values for spline control points");
2161 0 : DXF_LAYER_READER_ERROR();
2162 0 : return nullptr;
2163 : }
2164 :
2165 : /* -------------------------------------------------------------------- */
2166 : /* Use the helper function to check the input data and insert */
2167 : /* the spline. */
2168 : /* -------------------------------------------------------------------- */
2169 : auto poLS =
2170 : InsertSplineWithChecks(nDegree, adfControlPoints, bHasZ, nControlPoints,
2171 50 : adfKnots, nKnots, adfWeights);
2172 :
2173 25 : if (!poLS)
2174 : {
2175 0 : DXF_LAYER_READER_ERROR();
2176 0 : return nullptr;
2177 : }
2178 :
2179 25 : poFeature->SetGeometryDirectly(poLS.release());
2180 :
2181 25 : PrepareLineStyle(poFeature.get());
2182 :
2183 25 : return poFeature.release();
2184 : }
2185 :
2186 : /************************************************************************/
2187 : /* InsertSplineWithChecks() */
2188 : /* */
2189 : /* Inserts a spline based on unchecked DXF input. The arrays are */
2190 : /* one-based. */
2191 : /************************************************************************/
2192 :
2193 27 : std::unique_ptr<OGRLineString> OGRDXFLayer::InsertSplineWithChecks(
2194 : const int nDegree, std::vector<double> &adfControlPoints, bool bHasZ,
2195 : int nControlPoints, std::vector<double> &adfKnots, int nKnots,
2196 : std::vector<double> &adfWeights)
2197 : {
2198 : /* -------------------------------------------------------------------- */
2199 : /* Sanity checks */
2200 : /* -------------------------------------------------------------------- */
2201 27 : const int nOrder = nDegree + 1;
2202 :
2203 27 : bool bResult = (nOrder >= 2);
2204 27 : if (bResult == true)
2205 : {
2206 : // Check whether nctrlpts value matches number of vertices read
2207 : int nCheck =
2208 27 : (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) / 3;
2209 :
2210 27 : if (nControlPoints == -1)
2211 0 : nControlPoints =
2212 0 : (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) /
2213 : 3;
2214 :
2215 : // min( num(ctrlpts) ) = order
2216 27 : bResult = (nControlPoints >= nOrder && nControlPoints == nCheck);
2217 : }
2218 :
2219 27 : bool bCalculateKnots = false;
2220 27 : if (bResult == true)
2221 : {
2222 27 : int nCheck = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
2223 :
2224 : // Recalculate knots when:
2225 : // - no knots data present, nknots is -1 and ncheck is 0
2226 : // - nknots value present, no knot vertices
2227 : // nknots is (nctrlpts + order), ncheck is 0
2228 27 : if (nCheck == 0)
2229 : {
2230 1 : bCalculateKnots = true;
2231 12 : for (int i = 0; i < (nControlPoints + nOrder); i++)
2232 11 : adfKnots.push_back(0.0);
2233 :
2234 1 : nCheck = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
2235 : }
2236 : // Adjust nknots value when:
2237 : // - nknots value not present, knot vertices present
2238 : // nknots is -1, ncheck is (nctrlpts + order)
2239 27 : if (nKnots == -1)
2240 0 : nKnots = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
2241 :
2242 : // num(knots) = num(ctrlpts) + order
2243 27 : bResult = (nKnots == (nControlPoints + nOrder) && nKnots == nCheck);
2244 : }
2245 :
2246 27 : if (bResult == true)
2247 : {
2248 27 : int nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
2249 :
2250 27 : if (nWeights == 0)
2251 : {
2252 158 : for (int i = 0; i < nControlPoints; i++)
2253 134 : adfWeights.push_back(1.0);
2254 :
2255 24 : nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
2256 : }
2257 :
2258 : // num(weights) = num(ctrlpts)
2259 27 : bResult = (nWeights == nControlPoints);
2260 : }
2261 :
2262 27 : if (bResult == false)
2263 0 : return nullptr;
2264 :
2265 : /* -------------------------------------------------------------------- */
2266 : /* Interpolate spline */
2267 : /* -------------------------------------------------------------------- */
2268 27 : int p1 = nControlPoints * 8;
2269 54 : std::vector<double> p(3 * p1 + FORTRAN_INDEXING);
2270 :
2271 27 : rbspline2(nControlPoints, nOrder, p1, &(adfControlPoints[0]),
2272 27 : &(adfWeights[0]), bCalculateKnots, &(adfKnots[0]), &(p[0]));
2273 :
2274 : /* -------------------------------------------------------------------- */
2275 : /* Turn into OGR geometry. */
2276 : /* -------------------------------------------------------------------- */
2277 54 : auto poLS = std::make_unique<OGRLineString>();
2278 :
2279 27 : poLS->setNumPoints(p1);
2280 27 : if (bHasZ)
2281 : {
2282 1129 : for (int i = 0; i < p1; i++)
2283 2208 : poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
2284 1104 : p[i * 3 + FORTRAN_INDEXING + 1],
2285 1104 : p[i * 3 + FORTRAN_INDEXING + 2]);
2286 : }
2287 : else
2288 : {
2289 106 : for (int i = 0; i < p1; i++)
2290 208 : poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
2291 104 : p[i * 3 + FORTRAN_INDEXING + 1]);
2292 : }
2293 :
2294 27 : return poLS;
2295 : }
2296 :
2297 : /************************************************************************/
2298 : /* Translate3DFACE() */
2299 : /************************************************************************/
2300 :
2301 10 : OGRDXFFeature *OGRDXFLayer::Translate3DFACE()
2302 :
2303 : {
2304 : char szLineBuf[257];
2305 10 : int nCode = 0;
2306 20 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2307 10 : double dfX1 = 0.0;
2308 10 : double dfY1 = 0.0;
2309 10 : double dfZ1 = 0.0;
2310 10 : double dfX2 = 0.0;
2311 10 : double dfY2 = 0.0;
2312 10 : double dfZ2 = 0.0;
2313 10 : double dfX3 = 0.0;
2314 10 : double dfY3 = 0.0;
2315 10 : double dfZ3 = 0.0;
2316 10 : double dfX4 = 0.0;
2317 10 : double dfY4 = 0.0;
2318 10 : double dfZ4 = 0.0;
2319 :
2320 : /* -------------------------------------------------------------------- */
2321 : /* Process values. */
2322 : /* -------------------------------------------------------------------- */
2323 172 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2324 : {
2325 162 : switch (nCode)
2326 : {
2327 10 : case 10:
2328 10 : dfX1 = CPLAtof(szLineBuf);
2329 10 : break;
2330 :
2331 10 : case 11:
2332 10 : dfX2 = CPLAtof(szLineBuf);
2333 10 : break;
2334 :
2335 10 : case 12:
2336 10 : dfX3 = CPLAtof(szLineBuf);
2337 10 : break;
2338 :
2339 10 : case 13:
2340 10 : dfX4 = CPLAtof(szLineBuf);
2341 10 : break;
2342 :
2343 10 : case 20:
2344 10 : dfY1 = CPLAtof(szLineBuf);
2345 10 : break;
2346 :
2347 10 : case 21:
2348 10 : dfY2 = CPLAtof(szLineBuf);
2349 10 : break;
2350 :
2351 10 : case 22:
2352 10 : dfY3 = CPLAtof(szLineBuf);
2353 10 : break;
2354 :
2355 10 : case 23:
2356 10 : dfY4 = CPLAtof(szLineBuf);
2357 10 : break;
2358 :
2359 10 : case 30:
2360 10 : dfZ1 = CPLAtof(szLineBuf);
2361 10 : break;
2362 :
2363 10 : case 31:
2364 10 : dfZ2 = CPLAtof(szLineBuf);
2365 10 : break;
2366 :
2367 10 : case 32:
2368 10 : dfZ3 = CPLAtof(szLineBuf);
2369 10 : break;
2370 :
2371 10 : case 33:
2372 10 : dfZ4 = CPLAtof(szLineBuf);
2373 10 : break;
2374 :
2375 42 : default:
2376 42 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2377 42 : break;
2378 : }
2379 : }
2380 10 : if (nCode < 0)
2381 : {
2382 0 : DXF_LAYER_READER_ERROR();
2383 0 : return nullptr;
2384 : }
2385 :
2386 10 : if (nCode == 0)
2387 10 : poDS->UnreadValue();
2388 :
2389 : /* -------------------------------------------------------------------- */
2390 : /* Create geometry */
2391 : /* -------------------------------------------------------------------- */
2392 20 : auto poPoly = std::make_unique<OGRPolygon>();
2393 10 : OGRLinearRing *poLR = new OGRLinearRing();
2394 10 : poLR->addPoint(dfX1, dfY1, dfZ1);
2395 10 : poLR->addPoint(dfX2, dfY2, dfZ2);
2396 10 : poLR->addPoint(dfX3, dfY3, dfZ3);
2397 10 : if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
2398 9 : poLR->addPoint(dfX4, dfY4, dfZ4);
2399 10 : poPoly->addRingDirectly(poLR);
2400 10 : poPoly->closeRings();
2401 :
2402 10 : poFeature->ApplyOCSTransformer(poLR);
2403 10 : poFeature->SetGeometryDirectly(poPoly.release());
2404 :
2405 10 : PrepareLineStyle(poFeature.get());
2406 :
2407 10 : return poFeature.release();
2408 : }
2409 :
2410 : /* -------------------------------------------------------------------- */
2411 : /* PointXAxisComparer */
2412 : /* */
2413 : /* Returns true if oP1 is to the left of oP2, or they have the */
2414 : /* same x-coordinate and oP1 is below oP2. */
2415 : /* -------------------------------------------------------------------- */
2416 :
2417 150 : static bool PointXAxisComparer(const OGRPoint &oP1, const OGRPoint &oP2)
2418 : {
2419 256 : return oP1.getX() == oP2.getX() ? oP1.getY() < oP2.getY()
2420 256 : : oP1.getX() < oP2.getX();
2421 : }
2422 :
2423 : /* -------------------------------------------------------------------- */
2424 : /* PointXYZEqualityComparer */
2425 : /* */
2426 : /* Returns true if oP1 is equal to oP2 in the X, Y and Z axes. */
2427 : /* -------------------------------------------------------------------- */
2428 :
2429 75 : static bool PointXYZEqualityComparer(const OGRPoint &oP1, const OGRPoint &oP2)
2430 : {
2431 86 : return oP1.getX() == oP2.getX() && oP1.getY() == oP2.getY() &&
2432 86 : oP1.getZ() == oP2.getZ();
2433 : }
2434 :
2435 : /************************************************************************/
2436 : /* TranslateSOLID() */
2437 : /************************************************************************/
2438 :
2439 25 : OGRDXFFeature *OGRDXFLayer::TranslateSOLID()
2440 :
2441 : {
2442 : char szLineBuf[257];
2443 25 : int nCode = 0;
2444 50 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2445 25 : double dfX1 = 0.0;
2446 25 : double dfY1 = 0.0;
2447 25 : double dfZ1 = 0.0;
2448 25 : double dfX2 = 0.0;
2449 25 : double dfY2 = 0.0;
2450 25 : double dfZ2 = 0.0;
2451 25 : double dfX3 = 0.0;
2452 25 : double dfY3 = 0.0;
2453 25 : double dfZ3 = 0.0;
2454 25 : double dfX4 = 0.0;
2455 25 : double dfY4 = 0.0;
2456 25 : double dfZ4 = 0.0;
2457 :
2458 472 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2459 : {
2460 447 : switch (nCode)
2461 : {
2462 25 : case 10:
2463 25 : dfX1 = CPLAtof(szLineBuf);
2464 25 : break;
2465 :
2466 25 : case 20:
2467 25 : dfY1 = CPLAtof(szLineBuf);
2468 25 : break;
2469 :
2470 25 : case 30:
2471 25 : dfZ1 = CPLAtof(szLineBuf);
2472 25 : break;
2473 :
2474 25 : case 11:
2475 25 : dfX2 = CPLAtof(szLineBuf);
2476 25 : break;
2477 :
2478 25 : case 21:
2479 25 : dfY2 = CPLAtof(szLineBuf);
2480 25 : break;
2481 :
2482 25 : case 31:
2483 25 : dfZ2 = CPLAtof(szLineBuf);
2484 25 : break;
2485 :
2486 25 : case 12:
2487 25 : dfX3 = CPLAtof(szLineBuf);
2488 25 : break;
2489 :
2490 25 : case 22:
2491 25 : dfY3 = CPLAtof(szLineBuf);
2492 25 : break;
2493 :
2494 25 : case 32:
2495 25 : dfZ3 = CPLAtof(szLineBuf);
2496 25 : break;
2497 :
2498 25 : case 13:
2499 25 : dfX4 = CPLAtof(szLineBuf);
2500 25 : break;
2501 :
2502 25 : case 23:
2503 25 : dfY4 = CPLAtof(szLineBuf);
2504 25 : break;
2505 :
2506 25 : case 33:
2507 25 : dfZ4 = CPLAtof(szLineBuf);
2508 25 : break;
2509 :
2510 147 : default:
2511 147 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2512 147 : break;
2513 : }
2514 : }
2515 25 : if (nCode < 0)
2516 : {
2517 0 : DXF_LAYER_READER_ERROR();
2518 0 : return nullptr;
2519 : }
2520 25 : if (nCode == 0)
2521 25 : poDS->UnreadValue();
2522 :
2523 : // do we want Z-coordinates?
2524 25 : const bool bWantZ =
2525 25 : dfZ1 != 0.0 || dfZ2 != 0.0 || dfZ3 != 0.0 || dfZ4 != 0.0;
2526 :
2527 : // check how many unique corners we have
2528 250 : OGRPoint oCorners[4];
2529 25 : oCorners[0].setX(dfX1);
2530 25 : oCorners[0].setY(dfY1);
2531 25 : if (bWantZ)
2532 3 : oCorners[0].setZ(dfZ1);
2533 25 : oCorners[1].setX(dfX2);
2534 25 : oCorners[1].setY(dfY2);
2535 25 : if (bWantZ)
2536 3 : oCorners[1].setZ(dfZ2);
2537 25 : oCorners[2].setX(dfX3);
2538 25 : oCorners[2].setY(dfY3);
2539 25 : if (bWantZ)
2540 3 : oCorners[2].setZ(dfZ3);
2541 25 : oCorners[3].setX(dfX4);
2542 25 : oCorners[3].setY(dfY4);
2543 25 : if (bWantZ)
2544 3 : oCorners[3].setZ(dfZ4);
2545 :
2546 25 : std::sort(&oCorners[0], &oCorners[4], PointXAxisComparer);
2547 : int nCornerCount = static_cast<int>(
2548 25 : std::unique(&oCorners[0], &oCorners[4], PointXYZEqualityComparer) -
2549 25 : &oCorners[0]);
2550 25 : if (nCornerCount < 1)
2551 : {
2552 0 : DXF_LAYER_READER_ERROR();
2553 0 : return nullptr;
2554 : }
2555 :
2556 25 : std::unique_ptr<OGRGeometry> poFinalGeom;
2557 :
2558 : // what kind of object do we need?
2559 25 : if (nCornerCount == 1)
2560 : {
2561 1 : poFinalGeom.reset(oCorners[0].clone());
2562 :
2563 1 : PrepareLineStyle(poFeature.get());
2564 : }
2565 24 : else if (nCornerCount == 2)
2566 : {
2567 2 : auto poLS = std::make_unique<OGRLineString>();
2568 1 : poLS->setPoint(0, &oCorners[0]);
2569 1 : poLS->setPoint(1, &oCorners[1]);
2570 1 : poFinalGeom.reset(poLS.release());
2571 :
2572 1 : PrepareLineStyle(poFeature.get());
2573 : }
2574 : else
2575 : {
2576 : // SOLID vertices seem to be joined in the order 1-2-4-3-1.
2577 : // See trac ticket #7089
2578 23 : OGRLinearRing *poLinearRing = new OGRLinearRing();
2579 23 : int iIndex = 0;
2580 23 : poLinearRing->setPoint(iIndex++, dfX1, dfY1, dfZ1);
2581 23 : if (dfX1 != dfX2 || dfY1 != dfY2 || dfZ1 != dfZ2)
2582 23 : poLinearRing->setPoint(iIndex++, dfX2, dfY2, dfZ2);
2583 23 : if (dfX2 != dfX4 || dfY2 != dfY4 || dfZ2 != dfZ4)
2584 23 : poLinearRing->setPoint(iIndex++, dfX4, dfY4, dfZ4);
2585 23 : if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
2586 17 : poLinearRing->setPoint(iIndex++, dfX3, dfY3, dfZ3);
2587 23 : poLinearRing->closeRings();
2588 :
2589 23 : if (!bWantZ)
2590 20 : poLinearRing->flattenTo2D();
2591 :
2592 46 : auto poPoly = std::make_unique<OGRPolygon>();
2593 23 : poPoly->addRingDirectly(poLinearRing);
2594 23 : poFinalGeom.reset(poPoly.release());
2595 :
2596 23 : PrepareBrushStyle(poFeature.get());
2597 : }
2598 :
2599 25 : poFeature->ApplyOCSTransformer(poFinalGeom.get());
2600 25 : poFeature->SetGeometryDirectly(poFinalGeom.release());
2601 :
2602 25 : return poFeature.release();
2603 : }
2604 :
2605 : /************************************************************************/
2606 : /* TranslateASMEntity() */
2607 : /* */
2608 : /* Translate Autodesk ShapeManager entities (3DSOLID, REGION, */
2609 : /* SURFACE), also known as ACIS entities. */
2610 : /************************************************************************/
2611 :
2612 2 : OGRDXFFeature *OGRDXFLayer::TranslateASMEntity()
2613 :
2614 : {
2615 : char szLineBuf[257];
2616 2 : int nCode = 0;
2617 4 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2618 :
2619 23 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2620 : {
2621 21 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2622 : }
2623 :
2624 2 : if (nCode < 0)
2625 : {
2626 0 : DXF_LAYER_READER_ERROR();
2627 0 : return nullptr;
2628 : }
2629 :
2630 2 : poDS->UnreadValue();
2631 :
2632 2 : const char *pszEntityHandle = poFeature->GetFieldAsString("EntityHandle");
2633 :
2634 : // The actual data is located at the end of the DXF file (sigh).
2635 : const GByte *pabyBinaryData;
2636 : size_t nDataLength =
2637 2 : poDS->GetEntryFromAcDsDataSection(pszEntityHandle, &pabyBinaryData);
2638 2 : if (!pabyBinaryData)
2639 : {
2640 0 : CPLError(CE_Warning, CPLE_AppDefined,
2641 : "ACDSRECORD data for entity %s was not found.",
2642 : pszEntityHandle);
2643 0 : return poFeature.release();
2644 : }
2645 :
2646 : // Return a feature with no geometry but with one very interesting field.
2647 2 : poFeature->SetField(poFeatureDefn->GetFieldIndex("ASMData"),
2648 : static_cast<int>(nDataLength), pabyBinaryData);
2649 :
2650 : // Set up an affine transformation matrix so the user will be able to
2651 : // transform the resulting 3D geometry
2652 2 : poFeature->poASMTransform = std::make_unique<OGRDXFAffineTransform>();
2653 :
2654 2 : poFeature->poASMTransform->SetField(poFeature.get(), "ASMTransform");
2655 :
2656 : #ifdef notdef
2657 : FILE *fp;
2658 : fopen_s(&fp,
2659 : CPLString().Printf("C:\\Projects\\output.sab", pszEntityHandle),
2660 : "wb");
2661 :
2662 : if (fp != nullptr)
2663 : {
2664 : fprintf(fp, "Entity handle: %s\r\n\r\n", pszEntityHandle);
2665 : fwrite(pabyBinaryData, sizeof(GByte), nDataLength, fp);
2666 : if (ferror(fp) != 0)
2667 : {
2668 : fputs("Error writing .sab file", stderr);
2669 : }
2670 : fclose(fp);
2671 : }
2672 : #endif
2673 :
2674 2 : PrepareBrushStyle(poFeature.get());
2675 :
2676 2 : return poFeature.release();
2677 : }
2678 :
2679 : /************************************************************************/
2680 : /* SimplifyBlockGeometry() */
2681 : /************************************************************************/
2682 :
2683 : OGRGeometry *
2684 75 : OGRDXFLayer::SimplifyBlockGeometry(OGRGeometryCollection *poCollection)
2685 : {
2686 : /* -------------------------------------------------------------------- */
2687 : /* If there is only one geometry in the collection, just return */
2688 : /* it. */
2689 : /* -------------------------------------------------------------------- */
2690 75 : if (poCollection->getNumGeometries() == 1)
2691 : {
2692 64 : OGRGeometry *poReturn = poCollection->getGeometryRef(0);
2693 64 : poCollection->removeGeometry(0, FALSE);
2694 64 : delete poCollection;
2695 64 : return poReturn;
2696 : }
2697 :
2698 : /* -------------------------------------------------------------------- */
2699 : /* Convert to polygon, multipolygon, multilinestring or multipoint */
2700 : /* -------------------------------------------------------------------- */
2701 :
2702 : OGRwkbGeometryType eType =
2703 11 : wkbFlatten(poCollection->getGeometryRef(0)->getGeometryType());
2704 : int i;
2705 37 : for (i = 1; i < poCollection->getNumGeometries(); i++)
2706 : {
2707 27 : if (wkbFlatten(poCollection->getGeometryRef(i)->getGeometryType()) !=
2708 : eType)
2709 : {
2710 1 : eType = wkbUnknown;
2711 1 : break;
2712 : }
2713 : }
2714 11 : if (eType == wkbPoint || eType == wkbLineString)
2715 : {
2716 : OGRGeometryCollection *poNewColl;
2717 9 : if (eType == wkbPoint)
2718 0 : poNewColl = new OGRMultiPoint();
2719 : else
2720 9 : poNewColl = new OGRMultiLineString();
2721 43 : while (poCollection->getNumGeometries() > 0)
2722 : {
2723 34 : OGRGeometry *poGeom = poCollection->getGeometryRef(0);
2724 34 : poCollection->removeGeometry(0, FALSE);
2725 34 : poNewColl->addGeometryDirectly(poGeom);
2726 : }
2727 9 : delete poCollection;
2728 9 : return poNewColl;
2729 : }
2730 2 : else if (eType == wkbPolygon)
2731 : {
2732 2 : std::vector<OGRGeometry *> aosPolygons;
2733 3 : while (poCollection->getNumGeometries() > 0)
2734 : {
2735 2 : OGRGeometry *poGeom = poCollection->getGeometryRef(0);
2736 2 : poCollection->removeGeometry(0, FALSE);
2737 2 : if (!aosPolygons.empty() && aosPolygons[0]->Equals(poGeom))
2738 : {
2739 : // Avoids a performance issue as in
2740 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=8067
2741 0 : delete poGeom;
2742 : }
2743 : else
2744 : {
2745 2 : aosPolygons.push_back(poGeom);
2746 : }
2747 : }
2748 1 : delete poCollection;
2749 : int bIsValidGeometry;
2750 1 : return OGRGeometryFactory::organizePolygons(&aosPolygons[0],
2751 1 : (int)aosPolygons.size(),
2752 1 : &bIsValidGeometry, nullptr);
2753 : }
2754 :
2755 1 : return poCollection;
2756 : }
2757 :
2758 : /************************************************************************/
2759 : /* InsertBlockReference() */
2760 : /* */
2761 : /* Returns a point geometry located at the block's insertion */
2762 : /* point. */
2763 : /************************************************************************/
2764 : OGRDXFFeature *
2765 801 : OGRDXFLayer::InsertBlockReference(const CPLString &osBlockName,
2766 : const OGRDXFInsertTransformer &oTransformer,
2767 : OGRDXFFeature *const poFeature)
2768 : {
2769 : // Store the block's properties in the special DXF-specific members
2770 : // on the feature object
2771 801 : poFeature->bIsBlockReference = true;
2772 801 : poFeature->osBlockName = osBlockName;
2773 801 : poFeature->dfBlockAngle = oTransformer.dfAngle * 180 / M_PI;
2774 1602 : poFeature->oBlockScale = DXFTriple(
2775 801 : oTransformer.dfXScale, oTransformer.dfYScale, oTransformer.dfZScale);
2776 1602 : poFeature->oOriginalCoords = DXFTriple(
2777 801 : oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
2778 :
2779 : // Only if DXF_INLINE_BLOCKS is false should we ever need to expose these
2780 : // to the end user as fields.
2781 801 : if (poFeature->GetFieldIndex("BlockName") != -1)
2782 : {
2783 13 : poFeature->SetField("BlockName", poFeature->osBlockName);
2784 13 : poFeature->SetField("BlockAngle", poFeature->dfBlockAngle);
2785 13 : poFeature->SetField("BlockScale", 3, &(poFeature->oBlockScale.dfX));
2786 13 : poFeature->SetField("BlockOCSNormal", 3, &(poFeature->oOCS.dfX));
2787 13 : poFeature->SetField("BlockOCSCoords", 3,
2788 13 : &(poFeature->oOriginalCoords.dfX));
2789 : }
2790 :
2791 : // For convenience to the end user, the point geometry will be located
2792 : // at the WCS coordinates of the insertion point.
2793 : OGRPoint *poInsertionPoint = new OGRPoint(
2794 801 : oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
2795 :
2796 801 : poFeature->ApplyOCSTransformer(poInsertionPoint);
2797 801 : poFeature->SetGeometryDirectly(poInsertionPoint);
2798 :
2799 801 : return poFeature;
2800 : }
2801 :
2802 : /************************************************************************/
2803 : /* InsertBlockInline() */
2804 : /* */
2805 : /* Inserts the given block at the location specified by the given */
2806 : /* transformer. Returns poFeature, or NULL if all features on */
2807 : /* the block have been pushed to the extra feature queue. */
2808 : /* If poFeature is not returned, it is deleted. */
2809 : /* Throws std::invalid_argument if the requested block */
2810 : /* doesn't exist. */
2811 : /* */
2812 : /* - poFeature: The feature to use as a template. This feature's */
2813 : /* OCS will be applied to the block. */
2814 : /* - bInlineRecursively: If true, INSERTs within this block */
2815 : /* will be recursively inserted. Otherwise, they will be */
2816 : /* represented as a point geometry using InsertBlockReference. */
2817 : /* - bMergeGeometry: If true, all features in the block, */
2818 : /* apart from text features, are merged into a */
2819 : /* GeometryCollection which is returned by the function. */
2820 : /************************************************************************/
2821 :
2822 1224 : OGRDXFFeature *OGRDXFLayer::InsertBlockInline(
2823 : GUInt32 nInitialErrorCounter, const CPLString &osBlockName,
2824 : OGRDXFInsertTransformer oTransformer, OGRDXFFeature *const poFeature,
2825 : OGRDXFFeatureQueue &apoExtraFeatures, const bool bInlineRecursively,
2826 : const bool bMergeGeometry)
2827 : {
2828 : /* -------------------------------------------------------------------- */
2829 : /* Set up protection against excessive recursion on this layer. */
2830 : /* -------------------------------------------------------------------- */
2831 1224 : if (!poDS->PushBlockInsertion(osBlockName))
2832 : {
2833 1003 : delete poFeature;
2834 1003 : return nullptr;
2835 : }
2836 :
2837 : /* -------------------------------------------------------------------- */
2838 : /* Transform the insertion point from OCS into */
2839 : /* world coordinates. */
2840 : /* -------------------------------------------------------------------- */
2841 : OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
2842 442 : oTransformer.dfZOffset);
2843 :
2844 221 : poFeature->ApplyOCSTransformer(&oInsertionPoint);
2845 :
2846 221 : oTransformer.dfXOffset = oInsertionPoint.getX();
2847 221 : oTransformer.dfYOffset = oInsertionPoint.getY();
2848 221 : oTransformer.dfZOffset = oInsertionPoint.getZ();
2849 :
2850 : /* -------------------------------------------------------------------- */
2851 : /* Lookup the block. */
2852 : /* -------------------------------------------------------------------- */
2853 221 : DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
2854 :
2855 221 : if (poBlock == nullptr)
2856 : {
2857 : // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
2858 9 : poDS->PopBlockInsertion();
2859 9 : throw std::invalid_argument("osBlockName");
2860 : }
2861 :
2862 : /* -------------------------------------------------------------------- */
2863 : /* If we have complete features associated with the block, push */
2864 : /* them on the pending feature stack copying over key override */
2865 : /* information. */
2866 : /* */
2867 : /* If bMergeGeometry is true, we merge the features */
2868 : /* (except text) into a single GeometryCollection. */
2869 : /* -------------------------------------------------------------------- */
2870 212 : OGRGeometryCollection *poMergedGeometry = nullptr;
2871 212 : if (bMergeGeometry)
2872 89 : poMergedGeometry = new OGRGeometryCollection();
2873 :
2874 424 : OGRDXFFeatureQueue apoInnerExtraFeatures;
2875 :
2876 1823 : for (unsigned int iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size();
2877 : iSubFeat++)
2878 : {
2879 : OGRDXFFeature *poSubFeature =
2880 1613 : poBlock->apoFeatures[iSubFeat]->CloneDXFFeature();
2881 :
2882 : // If the template feature is in PaperSpace, set this on the
2883 : // subfeature too
2884 1613 : if (poFeature->GetFieldAsInteger("PaperSpace"))
2885 48 : poSubFeature->SetField("PaperSpace", 1);
2886 :
2887 : // Does this feature represent a block reference? If so,
2888 : // insert that block
2889 1613 : if (bInlineRecursively && poSubFeature->IsBlockReference())
2890 : {
2891 : // Unpack the transformation data stored in fields of this
2892 : // feature
2893 0 : OGRDXFInsertTransformer oInnerTransformer;
2894 1073 : oInnerTransformer.dfXOffset = poSubFeature->oOriginalCoords.dfX;
2895 1073 : oInnerTransformer.dfYOffset = poSubFeature->oOriginalCoords.dfY;
2896 1073 : oInnerTransformer.dfZOffset = poSubFeature->oOriginalCoords.dfZ;
2897 1073 : oInnerTransformer.dfAngle = poSubFeature->dfBlockAngle * M_PI / 180;
2898 1073 : oInnerTransformer.dfXScale = poSubFeature->oBlockScale.dfX;
2899 1073 : oInnerTransformer.dfYScale = poSubFeature->oBlockScale.dfY;
2900 1073 : oInnerTransformer.dfZScale = poSubFeature->oBlockScale.dfZ;
2901 :
2902 1073 : poSubFeature->bIsBlockReference = false;
2903 :
2904 : // Keep a reference to the attributes that need to be inserted
2905 : std::vector<std::unique_ptr<OGRDXFFeature>> apoInnerAttribFeatures =
2906 1073 : std::move(poSubFeature->apoAttribFeatures);
2907 :
2908 : // Insert this block recursively
2909 : try
2910 : {
2911 2146 : poSubFeature = InsertBlockInline(
2912 1073 : nInitialErrorCounter, poSubFeature->osBlockName,
2913 1073 : std::move(oInnerTransformer), poSubFeature,
2914 : apoInnerExtraFeatures, true, bMergeGeometry);
2915 : }
2916 0 : catch (const std::invalid_argument &)
2917 : {
2918 : // Block doesn't exist. Skip it and keep going
2919 0 : delete poSubFeature;
2920 0 : if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
2921 : {
2922 0 : break;
2923 : }
2924 0 : continue;
2925 : }
2926 :
2927 1073 : if (!poSubFeature)
2928 : {
2929 1067 : if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
2930 : {
2931 2 : break;
2932 : }
2933 :
2934 : // Append the attribute features to the pending feature stack
2935 1067 : for (auto &poAttribFeature : apoInnerAttribFeatures)
2936 : {
2937 : // Clear the attribute tag so the feature doesn't get mistaken
2938 : // for an ATTDEF and skipped
2939 2 : poAttribFeature->osAttributeTag = "";
2940 :
2941 2 : apoInnerExtraFeatures.push(poAttribFeature.release());
2942 : }
2943 :
2944 1065 : if (apoInnerExtraFeatures.empty())
2945 : {
2946 : // Block is empty and has no attributes. Skip it and keep going
2947 1002 : continue;
2948 : }
2949 : else
2950 : {
2951 : // Load up the first extra feature ready for
2952 : // transformation
2953 63 : poSubFeature = apoInnerExtraFeatures.front();
2954 63 : apoInnerExtraFeatures.pop();
2955 : }
2956 : }
2957 : }
2958 :
2959 : // Go through the current feature and any extra features generated
2960 : // by the recursive insert, and apply transformations
2961 : while (true)
2962 : {
2963 696 : OGRGeometry *poSubFeatGeom = poSubFeature->GetGeometryRef();
2964 696 : if (poSubFeatGeom != nullptr)
2965 : {
2966 : // Rotation and scaling first
2967 : OGRDXFInsertTransformer oInnerTrans =
2968 1386 : oTransformer.GetRotateScaleTransformer();
2969 693 : poSubFeatGeom->transform(&oInnerTrans);
2970 :
2971 : // Then the OCS to WCS transformation
2972 693 : poFeature->ApplyOCSTransformer(poSubFeatGeom);
2973 :
2974 : // Offset translation last
2975 693 : oInnerTrans = oTransformer.GetOffsetTransformer();
2976 693 : poSubFeatGeom->transform(&oInnerTrans);
2977 : }
2978 : // Transform the specially-stored data for ASM entities
2979 3 : else if (poSubFeature->poASMTransform)
2980 : {
2981 : // Rotation and scaling first
2982 : OGRDXFInsertTransformer oInnerTrans =
2983 6 : oTransformer.GetRotateScaleTransformer();
2984 3 : poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
2985 :
2986 : // Then the OCS to WCS transformation
2987 3 : poFeature->ApplyOCSTransformer(
2988 : poSubFeature->poASMTransform.get());
2989 :
2990 : // Offset translation last
2991 3 : oInnerTrans = oTransformer.GetOffsetTransformer();
2992 3 : poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
2993 :
2994 3 : poSubFeature->poASMTransform->SetField(poSubFeature,
2995 : "ASMTransform");
2996 : }
2997 :
2998 : // If we are merging features, and this is not text or a block
2999 : // reference, merge it into the GeometryCollection
3000 876 : if (bMergeGeometry &&
3001 180 : (poSubFeature->GetStyleString() == nullptr ||
3002 163 : strstr(poSubFeature->GetStyleString(), "LABEL") == nullptr) &&
3003 981 : !poSubFeature->IsBlockReference() &&
3004 105 : poSubFeature->GetGeometryRef())
3005 : {
3006 102 : poMergedGeometry->addGeometryDirectly(
3007 102 : poSubFeature->StealGeometry());
3008 102 : delete poSubFeature;
3009 : }
3010 : // Import all other features, except ATTDEFs when inlining
3011 : // recursively
3012 594 : else if (!bInlineRecursively || poSubFeature->osAttributeTag == "")
3013 : {
3014 : // If the subfeature is on layer 0, this is a special case: the
3015 : // subfeature should take on the style properties of the layer
3016 : // the block is being inserted onto.
3017 : // But don't do this if we are inserting onto a Blocks layer
3018 : // (that is, the owning feature has no layer).
3019 773 : if (EQUAL(poSubFeature->GetFieldAsString("Layer"), "0") &&
3020 211 : !EQUAL(poFeature->GetFieldAsString("Layer"), ""))
3021 : {
3022 180 : poSubFeature->SetField(
3023 : "Layer", poFeature->GetFieldAsString("Layer"));
3024 : }
3025 :
3026 : // Update the style string to replace ByBlock and ByLayer
3027 : // values.
3028 562 : PrepareFeatureStyle(poSubFeature, poFeature);
3029 :
3030 562 : ACAdjustText(oTransformer.dfAngle * 180 / M_PI,
3031 : oTransformer.dfXScale, oTransformer.dfYScale,
3032 : poSubFeature);
3033 :
3034 562 : if (!EQUAL(poFeature->GetFieldAsString("EntityHandle"), ""))
3035 : {
3036 525 : poSubFeature->SetField(
3037 : "EntityHandle",
3038 : poFeature->GetFieldAsString("EntityHandle"));
3039 : }
3040 :
3041 562 : apoExtraFeatures.push(poSubFeature);
3042 : }
3043 : else
3044 : {
3045 32 : delete poSubFeature;
3046 : }
3047 :
3048 696 : if (apoInnerExtraFeatures.empty())
3049 : {
3050 609 : break;
3051 : }
3052 : else
3053 : {
3054 87 : poSubFeature = apoInnerExtraFeatures.front();
3055 87 : apoInnerExtraFeatures.pop();
3056 : }
3057 87 : }
3058 : }
3059 :
3060 214 : while (!apoInnerExtraFeatures.empty())
3061 : {
3062 2 : auto poFeatureToDelete = apoInnerExtraFeatures.front();
3063 2 : apoInnerExtraFeatures.pop();
3064 2 : delete poFeatureToDelete;
3065 : }
3066 :
3067 212 : poDS->PopBlockInsertion();
3068 :
3069 : /* -------------------------------------------------------------------- */
3070 : /* Return the merged geometry if applicable. Otherwise */
3071 : /* return NULL and let the machinery find the rest of the */
3072 : /* features in the pending feature stack. */
3073 : /* -------------------------------------------------------------------- */
3074 212 : if (bMergeGeometry)
3075 : {
3076 89 : if (poMergedGeometry->getNumGeometries() == 0)
3077 : {
3078 14 : delete poMergedGeometry;
3079 : }
3080 : else
3081 : {
3082 75 : poFeature->SetGeometryDirectly(
3083 : SimplifyBlockGeometry(poMergedGeometry));
3084 :
3085 75 : PrepareLineStyle(poFeature);
3086 75 : return poFeature;
3087 : }
3088 : }
3089 :
3090 137 : delete poFeature;
3091 137 : return nullptr;
3092 : }
3093 :
3094 : /************************************************************************/
3095 : /* TranslateINSERT() */
3096 : /************************************************************************/
3097 :
3098 130 : bool OGRDXFLayer::TranslateINSERT()
3099 :
3100 : {
3101 : char szLineBuf[257];
3102 130 : int nCode = 0;
3103 :
3104 130 : m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
3105 130 : m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
3106 130 : m_oInsertState.m_osBlockName.clear();
3107 130 : m_oInsertState.m_nColumnCount = 1;
3108 130 : m_oInsertState.m_nRowCount = 1;
3109 130 : m_oInsertState.m_iCurCol = 0;
3110 130 : m_oInsertState.m_iCurRow = 0;
3111 130 : m_oInsertState.m_dfColumnSpacing = 0.0;
3112 130 : m_oInsertState.m_dfRowSpacing = 0.0;
3113 :
3114 130 : bool bHasAttribs = false;
3115 130 : m_oInsertState.m_apoAttribs.clear();
3116 130 : m_oInsertState.m_aosAttribs.Clear();
3117 :
3118 : /* -------------------------------------------------------------------- */
3119 : /* Process values. */
3120 : /* -------------------------------------------------------------------- */
3121 1368 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3122 : {
3123 1238 : switch (nCode)
3124 : {
3125 128 : case 10:
3126 128 : m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
3127 128 : break;
3128 :
3129 128 : case 20:
3130 128 : m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
3131 128 : break;
3132 :
3133 121 : case 30:
3134 121 : m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
3135 121 : break;
3136 :
3137 28 : case 41:
3138 28 : m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
3139 28 : break;
3140 :
3141 31 : case 42:
3142 31 : m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
3143 31 : break;
3144 :
3145 16 : case 43:
3146 16 : m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
3147 16 : break;
3148 :
3149 5 : case 44:
3150 5 : m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
3151 5 : break;
3152 :
3153 5 : case 45:
3154 5 : m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
3155 5 : break;
3156 :
3157 23 : case 50:
3158 : // We want to transform this to radians.
3159 : // It is apparently always in degrees regardless of $AUNITS
3160 23 : m_oInsertState.m_oTransformer.dfAngle =
3161 23 : CPLAtof(szLineBuf) * M_PI / 180.0;
3162 23 : break;
3163 :
3164 13 : case 66:
3165 13 : bHasAttribs = atoi(szLineBuf) == 1;
3166 13 : break;
3167 :
3168 1 : case 70:
3169 1 : m_oInsertState.m_nColumnCount = atoi(szLineBuf);
3170 1 : if (m_oInsertState.m_nColumnCount <= 0)
3171 : {
3172 0 : DXF_LAYER_READER_ERROR();
3173 0 : m_oInsertState.m_nRowCount = 0;
3174 0 : m_oInsertState.m_nColumnCount = 0;
3175 0 : return false;
3176 : }
3177 1 : break;
3178 :
3179 3 : case 71:
3180 3 : m_oInsertState.m_nRowCount = atoi(szLineBuf);
3181 3 : if (m_oInsertState.m_nRowCount <= 0)
3182 : {
3183 0 : DXF_LAYER_READER_ERROR();
3184 0 : m_oInsertState.m_nRowCount = 0;
3185 0 : m_oInsertState.m_nColumnCount = 0;
3186 0 : return false;
3187 : }
3188 3 : break;
3189 :
3190 128 : case 2:
3191 128 : m_oInsertState.m_osBlockName = szLineBuf;
3192 128 : break;
3193 :
3194 608 : default:
3195 608 : TranslateGenericProperty(
3196 : m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
3197 608 : break;
3198 : }
3199 : }
3200 130 : if (nCode < 0)
3201 : {
3202 0 : DXF_LAYER_READER_ERROR();
3203 0 : m_oInsertState.m_nRowCount = 0;
3204 0 : m_oInsertState.m_nColumnCount = 0;
3205 0 : return false;
3206 : }
3207 :
3208 : /* -------------------------------------------------------------------- */
3209 : /* Process any attribute entities. */
3210 : /* -------------------------------------------------------------------- */
3211 :
3212 130 : if (bHasAttribs)
3213 : {
3214 40 : while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
3215 : {
3216 27 : if (!EQUAL(szLineBuf, "ATTRIB"))
3217 : {
3218 0 : DXF_LAYER_READER_ERROR();
3219 0 : m_oInsertState.m_nRowCount = 0;
3220 0 : m_oInsertState.m_nColumnCount = 0;
3221 0 : return false;
3222 : }
3223 :
3224 : auto poAttribFeature =
3225 27 : std::unique_ptr<OGRDXFFeature>(TranslateTEXT(true));
3226 :
3227 27 : if (poAttribFeature && poAttribFeature->osAttributeTag != "")
3228 : {
3229 : m_oInsertState.m_apoAttribs.emplace_back(
3230 27 : std::move(poAttribFeature));
3231 : }
3232 :
3233 27 : nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
3234 : }
3235 : }
3236 117 : else if (nCode == 0)
3237 : {
3238 117 : poDS->UnreadValue();
3239 : }
3240 :
3241 : /* -------------------------------------------------------------------- */
3242 : /* Prepare a string list of the attributes and their text values */
3243 : /* as space-separated entries, to be stored in the */
3244 : /* BlockAttributes field if we are not inlining blocks. */
3245 : /* -------------------------------------------------------------------- */
3246 :
3247 134 : if (!poDS->InlineBlocks() && bHasAttribs &&
3248 4 : poFeatureDefn->GetFieldIndex("BlockAttributes") != -1)
3249 : {
3250 10 : for (const auto &poAttr : m_oInsertState.m_apoAttribs)
3251 : {
3252 14 : CPLString osAttribString = poAttr->osAttributeTag;
3253 7 : osAttribString += " ";
3254 7 : osAttribString += poAttr->GetFieldAsString("Text");
3255 :
3256 7 : m_oInsertState.m_aosAttribs.AddString(osAttribString);
3257 : }
3258 : }
3259 :
3260 130 : return true;
3261 : }
3262 :
3263 : /************************************************************************/
3264 : /* GenerateINSERTFeatures() */
3265 : /************************************************************************/
3266 :
3267 897 : bool OGRDXFLayer::GenerateINSERTFeatures()
3268 : {
3269 : OGRDXFFeature *poFeature =
3270 897 : m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
3271 :
3272 897 : const double dfExtraXOffset =
3273 897 : m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3274 897 : cos(m_oInsertState.m_oTransformer.dfAngle) +
3275 897 : m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3276 897 : -sin(m_oInsertState.m_oTransformer.dfAngle);
3277 897 : const double dfExtraYOffset =
3278 897 : m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3279 897 : sin(m_oInsertState.m_oTransformer.dfAngle) +
3280 897 : m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3281 897 : cos(m_oInsertState.m_oTransformer.dfAngle);
3282 :
3283 1794 : OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
3284 897 : oTransformer.dfXOffset += dfExtraXOffset;
3285 897 : oTransformer.dfYOffset += dfExtraYOffset;
3286 :
3287 : // If we are not inlining blocks, just insert a point that refers
3288 : // to this block
3289 897 : if (!poDS->InlineBlocks())
3290 : {
3291 801 : poFeature = InsertBlockReference(m_oInsertState.m_osBlockName,
3292 : oTransformer, poFeature);
3293 :
3294 801 : auto papszAttribs = m_oInsertState.m_aosAttribs.List();
3295 801 : if (papszAttribs)
3296 3 : poFeature->SetField("BlockAttributes", papszAttribs);
3297 :
3298 801 : poFeature->apoAttribFeatures = std::move(m_oInsertState.m_apoAttribs);
3299 :
3300 801 : apoPendingFeatures.push(poFeature);
3301 : }
3302 : // Otherwise, try inlining the contents of this block
3303 : else
3304 : {
3305 96 : OGRDXFFeatureQueue apoExtraFeatures;
3306 : try
3307 : {
3308 192 : poFeature = InsertBlockInline(
3309 96 : CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
3310 96 : std::move(oTransformer), poFeature, apoExtraFeatures, true,
3311 96 : poDS->ShouldMergeBlockGeometries());
3312 : }
3313 0 : catch (const std::invalid_argument &)
3314 : {
3315 : // Block doesn't exist
3316 0 : CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
3317 : m_oInsertState.m_osBlockName.c_str());
3318 0 : delete poFeature;
3319 0 : return false;
3320 : }
3321 :
3322 96 : if (poFeature)
3323 52 : apoPendingFeatures.push(poFeature);
3324 :
3325 303 : while (!apoExtraFeatures.empty())
3326 : {
3327 207 : apoPendingFeatures.push(apoExtraFeatures.front());
3328 207 : apoExtraFeatures.pop();
3329 : }
3330 :
3331 : // Append the attribute features to the pending feature stack
3332 96 : if (!m_oInsertState.m_apoAttribs.empty())
3333 : {
3334 16 : OGRDXFInsertTransformer oAttribTransformer;
3335 16 : oAttribTransformer.dfXOffset = dfExtraXOffset;
3336 16 : oAttribTransformer.dfYOffset = dfExtraYOffset;
3337 :
3338 42 : for (const auto &poAttr : m_oInsertState.m_apoAttribs)
3339 : {
3340 26 : OGRDXFFeature *poAttribFeature = poAttr->CloneDXFFeature();
3341 :
3342 26 : if (poAttribFeature->GetGeometryRef())
3343 : {
3344 26 : poAttribFeature->GetGeometryRef()->transform(
3345 26 : &oAttribTransformer);
3346 : }
3347 :
3348 26 : apoPendingFeatures.push(poAttribFeature);
3349 : }
3350 : }
3351 : }
3352 897 : return true;
3353 : }
3354 :
3355 : /************************************************************************/
3356 : /* GetNextUnfilteredFeature() */
3357 : /************************************************************************/
3358 :
3359 1905 : OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
3360 :
3361 : {
3362 1905 : OGRDXFFeature *poFeature = nullptr;
3363 3653 : while (poFeature == nullptr)
3364 : {
3365 : /* --------------------------------------------------------------------
3366 : */
3367 : /* If we have pending features, return one of them. */
3368 : /* --------------------------------------------------------------------
3369 : */
3370 3121 : if (!apoPendingFeatures.empty())
3371 : {
3372 1283 : poFeature = apoPendingFeatures.front();
3373 1283 : apoPendingFeatures.pop();
3374 :
3375 1283 : poFeature->SetFID(iNextFID++);
3376 1373 : return poFeature;
3377 : }
3378 :
3379 : /* --------------------------------------------------------------------
3380 : */
3381 : /* Emit INSERT features. */
3382 : /* --------------------------------------------------------------------
3383 : */
3384 1838 : if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
3385 : {
3386 998 : if (m_oInsertState.m_iCurCol == m_oInsertState.m_nColumnCount)
3387 : {
3388 862 : m_oInsertState.m_iCurRow++;
3389 862 : m_oInsertState.m_iCurCol = 0;
3390 862 : if (m_oInsertState.m_iCurRow == m_oInsertState.m_nRowCount)
3391 : {
3392 101 : m_oInsertState.m_nRowCount = 0;
3393 101 : m_oInsertState.m_nColumnCount = 0;
3394 998 : continue;
3395 : }
3396 : }
3397 897 : if (GenerateINSERTFeatures())
3398 : {
3399 897 : m_oInsertState.m_iCurCol++;
3400 : }
3401 : else
3402 : {
3403 0 : m_oInsertState.m_nRowCount = 0;
3404 0 : m_oInsertState.m_nColumnCount = 0;
3405 : }
3406 897 : continue;
3407 : }
3408 :
3409 : // read ahead to an entity.
3410 : char szLineBuf[257];
3411 840 : int nCode = 0;
3412 960 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3413 : {
3414 : }
3415 840 : if (nCode < 0)
3416 : {
3417 5 : DXF_LAYER_READER_ERROR();
3418 5 : return nullptr;
3419 : }
3420 :
3421 835 : if (EQUAL(szLineBuf, "ENDSEC"))
3422 : {
3423 : // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
3424 13 : poDS->UnreadValue();
3425 13 : return nullptr;
3426 : }
3427 :
3428 822 : if (EQUAL(szLineBuf, "ENDBLK"))
3429 : {
3430 : // CPLDebug( "DXF", "Clean end of block at ENDBLK." );
3431 72 : poDS->UnreadValue();
3432 72 : return nullptr;
3433 : }
3434 :
3435 : /* --------------------------------------------------------------------
3436 : */
3437 : /* Handle the entity. */
3438 : /* --------------------------------------------------------------------
3439 : */
3440 750 : if (EQUAL(szLineBuf, "POINT"))
3441 : {
3442 33 : poFeature = TranslatePOINT();
3443 : }
3444 717 : else if (EQUAL(szLineBuf, "MTEXT"))
3445 : {
3446 40 : poFeature = TranslateMTEXT();
3447 : }
3448 677 : else if (EQUAL(szLineBuf, "TEXT"))
3449 : {
3450 26 : poFeature = TranslateTEXT(false);
3451 : }
3452 651 : else if (EQUAL(szLineBuf, "ATTDEF"))
3453 : {
3454 10 : poFeature = TranslateTEXT(true);
3455 : }
3456 641 : else if (EQUAL(szLineBuf, "LINE"))
3457 : {
3458 163 : poFeature = TranslateLINE();
3459 : }
3460 478 : else if (EQUAL(szLineBuf, "POLYLINE"))
3461 : {
3462 18 : poFeature = TranslatePOLYLINE();
3463 : }
3464 460 : else if (EQUAL(szLineBuf, "LWPOLYLINE"))
3465 : {
3466 37 : poFeature = TranslateLWPOLYLINE();
3467 : }
3468 423 : else if (EQUAL(szLineBuf, "MLINE"))
3469 : {
3470 3 : poFeature = TranslateMLINE();
3471 : }
3472 420 : else if (EQUAL(szLineBuf, "CIRCLE"))
3473 : {
3474 23 : poFeature = TranslateCIRCLE();
3475 : }
3476 397 : else if (EQUAL(szLineBuf, "ELLIPSE"))
3477 : {
3478 29 : poFeature = TranslateELLIPSE();
3479 : }
3480 368 : else if (EQUAL(szLineBuf, "ARC"))
3481 : {
3482 14 : poFeature = TranslateARC();
3483 : }
3484 354 : else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
3485 : {
3486 25 : poFeature = TranslateSPLINE();
3487 : }
3488 329 : else if (EQUAL(szLineBuf, "3DFACE"))
3489 : {
3490 10 : poFeature = Translate3DFACE();
3491 : }
3492 319 : else if (EQUAL(szLineBuf, "INSERT"))
3493 : {
3494 130 : if (!TranslateINSERT())
3495 0 : return nullptr;
3496 : }
3497 189 : else if (EQUAL(szLineBuf, "DIMENSION"))
3498 : {
3499 30 : poFeature = TranslateDIMENSION();
3500 : }
3501 159 : else if (EQUAL(szLineBuf, "HATCH"))
3502 : {
3503 28 : poFeature = TranslateHATCH();
3504 : }
3505 131 : else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
3506 : {
3507 25 : poFeature = TranslateSOLID();
3508 : }
3509 106 : else if (EQUAL(szLineBuf, "LEADER"))
3510 : {
3511 14 : poFeature = TranslateLEADER();
3512 : }
3513 92 : else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
3514 : {
3515 18 : poFeature = TranslateMLEADER();
3516 : }
3517 74 : else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
3518 72 : EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
3519 : {
3520 2 : if (poDS->In3DExtensibleMode())
3521 : {
3522 2 : poFeature = TranslateASMEntity();
3523 : }
3524 0 : else if (oIgnoredEntities.count(szLineBuf) == 0)
3525 : {
3526 0 : oIgnoredEntities.insert(szLineBuf);
3527 0 : CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
3528 : szLineBuf);
3529 : }
3530 : }
3531 : else
3532 : {
3533 72 : if (oIgnoredEntities.count(szLineBuf) == 0)
3534 : {
3535 13 : oIgnoredEntities.insert(szLineBuf);
3536 13 : CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
3537 : szLineBuf);
3538 : }
3539 : }
3540 : }
3541 :
3542 : /* -------------------------------------------------------------------- */
3543 : /* Set FID. */
3544 : /* -------------------------------------------------------------------- */
3545 532 : poFeature->SetFID(iNextFID++);
3546 532 : m_nFeaturesRead++;
3547 :
3548 532 : return poFeature;
3549 : }
3550 :
3551 : /************************************************************************/
3552 : /* GetNextFeature() */
3553 : /************************************************************************/
3554 :
3555 824 : OGRFeature *OGRDXFLayer::GetNextFeature()
3556 :
3557 : {
3558 : while (true)
3559 : {
3560 824 : OGRFeature *poFeature = GetNextUnfilteredFeature();
3561 :
3562 824 : if (poFeature == nullptr)
3563 15 : return nullptr;
3564 :
3565 1618 : if ((m_poFilterGeom == nullptr ||
3566 1618 : FilterGeometry(poFeature->GetGeometryRef())) &&
3567 809 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
3568 : {
3569 809 : return poFeature;
3570 : }
3571 :
3572 0 : delete poFeature;
3573 0 : }
3574 : }
3575 :
3576 : /************************************************************************/
3577 : /* TestCapability() */
3578 : /************************************************************************/
3579 :
3580 0 : int OGRDXFLayer::TestCapability(const char *pszCap)
3581 :
3582 : {
3583 0 : if (EQUAL(pszCap, OLCStringsAsUTF8))
3584 0 : return true;
3585 0 : else if (EQUAL(pszCap, OLCZGeometries))
3586 0 : return true;
3587 0 : return false;
3588 : }
3589 :
3590 : /************************************************************************/
3591 : /* GetDataset() */
3592 : /************************************************************************/
3593 :
3594 1 : GDALDataset *OGRDXFLayer::GetDataset()
3595 : {
3596 1 : return poDS;
3597 : }
|