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 2023 : void OGRDXFFeatureQueue::push(OGRDXFFeature *poFeature)
31 : {
32 2023 : apoFeatures.push(poFeature);
33 2023 : }
34 :
35 : /************************************************************************/
36 : /* pop() */
37 : /************************************************************************/
38 :
39 2023 : void OGRDXFFeatureQueue::pop()
40 : {
41 2023 : CPLAssert(!apoFeatures.empty());
42 2023 : apoFeatures.pop();
43 2023 : }
44 :
45 : /************************************************************************/
46 : /* OGRDXFLayer() */
47 : /************************************************************************/
48 :
49 206 : OGRDXFLayer::OGRDXFLayer(OGRDXFDataSource *poDSIn)
50 206 : : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("entities")), iNextFID(0)
51 : {
52 206 : poFeatureDefn->Reference();
53 :
54 206 : int nModes = ODFM_None;
55 206 : if (!poDS->InlineBlocks())
56 26 : nModes |= ODFM_IncludeBlockFields;
57 206 : if (poDS->ShouldIncludeRawCodeValues())
58 1 : nModes |= ODFM_IncludeRawCodeValues;
59 206 : if (poDS->In3DExtensibleMode())
60 1 : nModes |= ODFM_Include3DModeFields;
61 206 : OGRDXFDataSource::AddStandardFields(poFeatureDefn, nModes);
62 :
63 206 : SetDescription(poFeatureDefn->GetName());
64 206 : }
65 :
66 : /************************************************************************/
67 : /* ~OGRDXFLayer() */
68 : /************************************************************************/
69 :
70 391 : OGRDXFLayer::~OGRDXFLayer()
71 :
72 : {
73 206 : ClearPendingFeatures();
74 206 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
75 : {
76 74 : CPLDebug("DXF", "%d features read on layer '%s'.", (int)m_nFeaturesRead,
77 74 : poFeatureDefn->GetName());
78 : }
79 :
80 206 : if (poFeatureDefn)
81 206 : poFeatureDefn->Release();
82 391 : }
83 :
84 : /************************************************************************/
85 : /* ClearPendingFeatures() */
86 : /************************************************************************/
87 :
88 445 : void OGRDXFLayer::ClearPendingFeatures()
89 :
90 : {
91 445 : while (!apoPendingFeatures.empty())
92 : {
93 55 : OGRDXFFeature *poFeature = apoPendingFeatures.front();
94 55 : apoPendingFeatures.pop();
95 55 : delete poFeature;
96 : }
97 390 : }
98 :
99 : /************************************************************************/
100 : /* ResetReading() */
101 : /************************************************************************/
102 :
103 184 : void OGRDXFLayer::ResetReading()
104 :
105 : {
106 184 : iNextFID = 0;
107 184 : ClearPendingFeatures();
108 184 : m_oInsertState.m_nRowCount = 0;
109 184 : m_oInsertState.m_nColumnCount = 0;
110 184 : poDS->RestartEntities();
111 184 : }
112 :
113 : /************************************************************************/
114 : /* TranslateGenericProperty() */
115 : /* */
116 : /* Try and convert entity properties handled similarly for most */
117 : /* or all entity types. */
118 : /************************************************************************/
119 :
120 6763 : void OGRDXFLayer::TranslateGenericProperty(OGRDXFFeature *poFeature, int nCode,
121 : char *pszValue)
122 :
123 : {
124 6763 : switch (nCode)
125 : {
126 740 : case 8:
127 740 : poFeature->SetField("Layer", TextRecode(pszValue));
128 740 : break;
129 :
130 1364 : case 100:
131 : {
132 2728 : CPLString osSubClass = poFeature->GetFieldAsString("SubClasses");
133 1364 : if (!osSubClass.empty())
134 737 : osSubClass += ":";
135 1364 : osSubClass += pszValue;
136 1364 : poFeature->SetField("SubClasses", osSubClass.c_str());
137 : }
138 1364 : 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 338 : case 62:
175 338 : poFeature->oStyleProperties["Color"] = pszValue;
176 338 : break;
177 :
178 12 : case 420:
179 12 : poFeature->oStyleProperties["TrueColor"] = pszValue;
180 12 : break;
181 :
182 261 : case 6:
183 261 : poFeature->SetField("Linetype", TextRecode(pszValue));
184 261 : break;
185 :
186 7 : case 48:
187 7 : poFeature->oStyleProperties["LinetypeScale"] = pszValue;
188 7 : break;
189 :
190 207 : case 370:
191 : case 39:
192 207 : poFeature->oStyleProperties["LineWeight"] = pszValue;
193 207 : break;
194 :
195 731 : case 5:
196 731 : poFeature->SetField("EntityHandle", pszValue);
197 731 : 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 2649 : default:
213 2649 : 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 2649 : 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 748 : void OGRDXFLayer::PrepareFeatureStyle(
245 : OGRDXFFeature *const poFeature,
246 : OGRDXFFeature *const poBlockFeature /* = NULL */)
247 :
248 : {
249 748 : const char *pszStyleString = poFeature->GetStyleString();
250 :
251 748 : if (pszStyleString && STARTS_WITH_CI(pszStyleString, "BRUSH("))
252 : {
253 51 : PrepareBrushStyle(poFeature, poBlockFeature);
254 : }
255 697 : 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 633 : PrepareLineStyle(poFeature, poBlockFeature);
280 : }
281 748 : }
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 1145 : void OGRDXFLayer::PrepareLineStyle(
304 : OGRDXFFeature *const poFeature,
305 : OGRDXFFeature *const poBlockFeature /* = NULL */)
306 :
307 : {
308 2290 : const CPLString osLayer = poFeature->GetFieldAsString("Layer");
309 :
310 : /* -------------------------------------------------------------------- */
311 : /* Get line weight if available. */
312 : /* -------------------------------------------------------------------- */
313 1145 : double dfWeight = 0.0;
314 2290 : CPLString osWeight = "-1";
315 :
316 1145 : if (poFeature->oStyleProperties.count("LineWeight") > 0)
317 273 : osWeight = poFeature->oStyleProperties["LineWeight"];
318 :
319 : // Use ByBlock lineweight?
320 1145 : 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 1145 : if (CPLAtof(osWeight) == -1)
341 : {
342 1091 : osWeight = poDS->LookupLayerProperty(osLayer, "LineWeight");
343 : }
344 :
345 : // Will be zero in the case of an invalid value
346 1145 : dfWeight = CPLAtof(osWeight) / 100.0;
347 :
348 : /* -------------------------------------------------------------------- */
349 : /* Do we have a dash/dot line style? */
350 : /* -------------------------------------------------------------------- */
351 1145 : const char *pszLinetype = poFeature->GetFieldAsString("Linetype");
352 :
353 : // Use ByBlock line style?
354 1145 : 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 1145 : if (pszLinetype && EQUAL(pszLinetype, ""))
366 : {
367 865 : pszLinetype = poDS->LookupLayerProperty(osLayer, "Linetype");
368 : }
369 :
370 2290 : const std::vector<double> oLineType = poDS->LookupLineType(pszLinetype);
371 :
372 : // Linetype scale is not inherited from the block feature
373 1145 : double dfLineTypeScale = CPLAtof(poDS->GetVariable("$LTSCALE", "1.0"));
374 1145 : if (poFeature->oStyleProperties.count("LinetypeScale") > 0)
375 16 : dfLineTypeScale *=
376 16 : CPLAtof(poFeature->oStyleProperties["LinetypeScale"]);
377 :
378 2290 : CPLString osPattern;
379 1235 : for (std::vector<double>::const_iterator oIt = oLineType.begin();
380 1235 : 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 1145 : if (osPattern.length() > 0)
388 45 : osPattern.erase(osPattern.end() - 1);
389 :
390 : /* -------------------------------------------------------------------- */
391 : /* Format the style string. */
392 : /* -------------------------------------------------------------------- */
393 :
394 2290 : CPLString osStyle = "PEN(c:";
395 1145 : osStyle += poFeature->GetColor(poDS, poBlockFeature);
396 :
397 1145 : if (dfWeight > 0.0)
398 : {
399 : char szBuffer[64];
400 16 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight);
401 16 : osStyle += CPLString().Printf(",w:%sg", szBuffer);
402 : }
403 :
404 1145 : if (osPattern != "")
405 : {
406 45 : osStyle += ",p:\"";
407 45 : osStyle += osPattern;
408 45 : osStyle += "\"";
409 : }
410 :
411 1145 : osStyle += ")";
412 :
413 1145 : poFeature->SetStyleString(osStyle);
414 1145 : }
415 :
416 : /************************************************************************/
417 : /* TextRecode() */
418 : /************************************************************************/
419 :
420 1510 : CPLString OGRDXFLayer::TextRecode(const char *pszInput)
421 :
422 : {
423 3020 : 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 35 : OGRDXFFeature *OGRDXFLayer::TranslatePOINT()
906 :
907 : {
908 : char szLineBuf[257];
909 35 : int nCode = 0;
910 70 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
911 35 : double dfX = 0.0;
912 35 : double dfY = 0.0;
913 35 : double dfZ = 0.0;
914 35 : bool bHaveZ = false;
915 :
916 363 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
917 : {
918 328 : switch (nCode)
919 : {
920 35 : case 10:
921 35 : dfX = CPLAtof(szLineBuf);
922 35 : break;
923 :
924 35 : case 20:
925 35 : dfY = CPLAtof(szLineBuf);
926 35 : break;
927 :
928 31 : case 30:
929 31 : dfZ = CPLAtof(szLineBuf);
930 31 : bHaveZ = true;
931 31 : break;
932 :
933 227 : default:
934 227 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
935 227 : break;
936 : }
937 : }
938 35 : if (nCode < 0)
939 : {
940 0 : DXF_LAYER_READER_ERROR();
941 0 : return nullptr;
942 : }
943 :
944 35 : if (nCode == 0)
945 35 : poDS->UnreadValue();
946 :
947 35 : OGRPoint *poGeom = nullptr;
948 35 : if (bHaveZ)
949 31 : poGeom = new OGRPoint(dfX, dfY, dfZ);
950 : else
951 4 : poGeom = new OGRPoint(dfX, dfY);
952 :
953 35 : poFeature->SetGeometryDirectly(poGeom);
954 :
955 : // Set style pen color
956 35 : PrepareLineStyle(poFeature.get());
957 :
958 35 : return poFeature.release();
959 : }
960 :
961 : /************************************************************************/
962 : /* TranslateLINE() */
963 : /************************************************************************/
964 :
965 188 : OGRDXFFeature *OGRDXFLayer::TranslateLINE()
966 :
967 : {
968 : char szLineBuf[257];
969 188 : int nCode = 0;
970 376 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
971 188 : double dfX1 = 0.0;
972 188 : double dfY1 = 0.0;
973 188 : double dfZ1 = 0.0;
974 188 : double dfX2 = 0.0;
975 188 : double dfY2 = 0.0;
976 188 : double dfZ2 = 0.0;
977 188 : bool bHaveZ = false;
978 :
979 : /* -------------------------------------------------------------------- */
980 : /* Process values. */
981 : /* -------------------------------------------------------------------- */
982 2486 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
983 : {
984 2298 : switch (nCode)
985 : {
986 188 : case 10:
987 188 : dfX1 = CPLAtof(szLineBuf);
988 188 : break;
989 :
990 188 : case 11:
991 188 : dfX2 = CPLAtof(szLineBuf);
992 188 : break;
993 :
994 188 : case 20:
995 188 : dfY1 = CPLAtof(szLineBuf);
996 188 : break;
997 :
998 188 : case 21:
999 188 : dfY2 = CPLAtof(szLineBuf);
1000 188 : break;
1001 :
1002 188 : case 30:
1003 188 : dfZ1 = CPLAtof(szLineBuf);
1004 188 : bHaveZ = true;
1005 188 : break;
1006 :
1007 188 : case 31:
1008 188 : dfZ2 = CPLAtof(szLineBuf);
1009 188 : bHaveZ = true;
1010 188 : break;
1011 :
1012 1170 : default:
1013 1170 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1014 1170 : break;
1015 : }
1016 : }
1017 188 : if (nCode < 0)
1018 : {
1019 0 : DXF_LAYER_READER_ERROR();
1020 0 : return nullptr;
1021 : }
1022 :
1023 188 : if (nCode == 0)
1024 188 : poDS->UnreadValue();
1025 :
1026 : /* -------------------------------------------------------------------- */
1027 : /* Create geometry */
1028 : /* -------------------------------------------------------------------- */
1029 376 : auto poLS = std::make_unique<OGRLineString>();
1030 188 : if (bHaveZ)
1031 : {
1032 188 : poLS->addPoint(dfX1, dfY1, dfZ1);
1033 188 : poLS->addPoint(dfX2, dfY2, dfZ2);
1034 : }
1035 : else
1036 : {
1037 0 : poLS->addPoint(dfX1, dfY1);
1038 0 : poLS->addPoint(dfX2, dfY2);
1039 : }
1040 :
1041 188 : poFeature->SetGeometryDirectly(poLS.release());
1042 :
1043 188 : PrepareLineStyle(poFeature.get());
1044 :
1045 188 : 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 19 : OGRDXFFeature *OGRDXFLayer::TranslatePOLYLINE()
1192 :
1193 : {
1194 : char szLineBuf[257];
1195 19 : int nCode = 0;
1196 19 : int nPolylineFlag = 0;
1197 38 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1198 :
1199 : /* -------------------------------------------------------------------- */
1200 : /* Collect information from the POLYLINE object itself. */
1201 : /* -------------------------------------------------------------------- */
1202 217 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1203 : {
1204 198 : switch (nCode)
1205 : {
1206 19 : case 70:
1207 19 : nPolylineFlag = atoi(szLineBuf);
1208 19 : break;
1209 :
1210 179 : default:
1211 179 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1212 179 : break;
1213 : }
1214 : }
1215 19 : if (nCode < 0)
1216 : {
1217 0 : DXF_LAYER_READER_ERROR();
1218 0 : return nullptr;
1219 : }
1220 :
1221 19 : 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 19 : double dfX = 0.0;
1231 19 : double dfY = 0.0;
1232 19 : double dfZ = 0.0;
1233 19 : double dfBulge = 0.0;
1234 19 : int nVertexFlag = 0;
1235 38 : DXFSmoothPolyline smoothPolyline;
1236 19 : unsigned int vertexIndex71 = 0;
1237 19 : unsigned int vertexIndex72 = 0;
1238 19 : unsigned int vertexIndex73 = 0;
1239 19 : unsigned int vertexIndex74 = 0;
1240 38 : std::vector<OGRPoint> aoPoints;
1241 38 : auto poPS = std::make_unique<OGRPolyhedralSurface>();
1242 :
1243 19 : smoothPolyline.setCoordinateDimension(2);
1244 :
1245 100 : while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
1246 : {
1247 : // Eat non-vertex objects.
1248 81 : 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 916 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1264 : {
1265 835 : switch (nCode)
1266 : {
1267 81 : case 10:
1268 81 : dfX = CPLAtof(szLineBuf);
1269 81 : break;
1270 :
1271 81 : case 20:
1272 81 : dfY = CPLAtof(szLineBuf);
1273 81 : break;
1274 :
1275 81 : case 30:
1276 81 : dfZ = CPLAtof(szLineBuf);
1277 81 : smoothPolyline.setCoordinateDimension(3);
1278 81 : 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 500 : default:
1307 500 : break;
1308 : }
1309 : }
1310 :
1311 81 : 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 81 : 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 81 : 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 81 : if ((nVertexFlag & 16) == 0)
1391 81 : smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1392 81 : dfBulge = 0.0;
1393 : }
1394 :
1395 19 : if (smoothPolyline.IsEmpty())
1396 : {
1397 0 : return nullptr;
1398 : }
1399 :
1400 19 : 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 18 : const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
1411 18 : if (bIsClosed)
1412 11 : smoothPolyline.Close();
1413 :
1414 18 : const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
1415 :
1416 18 : smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
1417 18 : OGRGeometry *poGeom = smoothPolyline.Tessellate(bAsPolygon);
1418 :
1419 18 : if ((nPolylineFlag & 8) == 0)
1420 3 : poFeature->ApplyOCSTransformer(poGeom);
1421 18 : poFeature->SetGeometryDirectly(poGeom);
1422 :
1423 18 : PrepareLineStyle(poFeature.get());
1424 :
1425 18 : 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 24 : OGRDXFFeature *OGRDXFLayer::TranslateCIRCLE()
1638 :
1639 : {
1640 : char szLineBuf[257];
1641 24 : int nCode = 0;
1642 48 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1643 24 : double dfX1 = 0.0;
1644 24 : double dfY1 = 0.0;
1645 24 : double dfZ1 = 0.0;
1646 24 : double dfRadius = 0.0;
1647 24 : double dfThickness = 0.0;
1648 24 : bool bHaveZ = false;
1649 :
1650 : /* -------------------------------------------------------------------- */
1651 : /* Process values. */
1652 : /* -------------------------------------------------------------------- */
1653 243 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1654 : {
1655 219 : switch (nCode)
1656 : {
1657 24 : case 10:
1658 24 : dfX1 = CPLAtof(szLineBuf);
1659 24 : break;
1660 :
1661 24 : case 20:
1662 24 : dfY1 = CPLAtof(szLineBuf);
1663 24 : break;
1664 :
1665 24 : case 30:
1666 24 : dfZ1 = CPLAtof(szLineBuf);
1667 24 : bHaveZ = true;
1668 24 : break;
1669 :
1670 1 : case 39:
1671 1 : dfThickness = CPLAtof(szLineBuf);
1672 1 : break;
1673 :
1674 24 : case 40:
1675 24 : dfRadius = CPLAtof(szLineBuf);
1676 24 : break;
1677 :
1678 122 : default:
1679 122 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1680 122 : break;
1681 : }
1682 : }
1683 24 : if (nCode < 0)
1684 : {
1685 0 : DXF_LAYER_READER_ERROR();
1686 0 : return nullptr;
1687 : }
1688 :
1689 24 : if (nCode == 0)
1690 24 : 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 24 : poDS->InlineBlocks())
1699 48 : ->toLineString());
1700 :
1701 24 : 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 24 : 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 23 : if (!bHaveZ)
1775 0 : poCircle->flattenTo2D();
1776 :
1777 23 : poFeature->ApplyOCSTransformer(poCircle.get());
1778 23 : poFeature->SetGeometryDirectly(poCircle.release());
1779 : }
1780 :
1781 24 : PrepareLineStyle(poFeature.get());
1782 :
1783 24 : 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 79 : OGRDXFLayer::SimplifyBlockGeometry(OGRGeometryCollection *poCollection)
2685 : {
2686 : /* -------------------------------------------------------------------- */
2687 : /* If there is only one geometry in the collection, just return */
2688 : /* it. */
2689 : /* -------------------------------------------------------------------- */
2690 79 : if (poCollection->getNumGeometries() == 1)
2691 : {
2692 66 : OGRGeometry *poReturn = poCollection->getGeometryRef(0);
2693 66 : poCollection->removeGeometry(0, FALSE);
2694 66 : delete poCollection;
2695 66 : return poReturn;
2696 : }
2697 :
2698 : /* -------------------------------------------------------------------- */
2699 : /* Convert to polygon, multipolygon, multilinestring or multipoint */
2700 : /* -------------------------------------------------------------------- */
2701 :
2702 : OGRwkbGeometryType eType =
2703 13 : wkbFlatten(poCollection->getGeometryRef(0)->getGeometryType());
2704 : int i;
2705 47 : for (i = 1; i < poCollection->getNumGeometries(); i++)
2706 : {
2707 35 : if (wkbFlatten(poCollection->getGeometryRef(i)->getGeometryType()) !=
2708 : eType)
2709 : {
2710 1 : eType = wkbUnknown;
2711 1 : break;
2712 : }
2713 : }
2714 13 : if (eType == wkbPoint || eType == wkbLineString)
2715 : {
2716 : OGRGeometryCollection *poNewColl;
2717 11 : if (eType == wkbPoint)
2718 0 : poNewColl = new OGRMultiPoint();
2719 : else
2720 11 : poNewColl = new OGRMultiLineString();
2721 55 : while (poCollection->getNumGeometries() > 0)
2722 : {
2723 44 : OGRGeometry *poGeom = poCollection->getGeometryRef(0);
2724 44 : poCollection->removeGeometry(0, FALSE);
2725 44 : poNewColl->addGeometryDirectly(poGeom);
2726 : }
2727 11 : delete poCollection;
2728 11 : 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 : /* TranslateWIPEOUT() */
2760 : /* */
2761 : /* Translate Autodesk Wipeout entities */
2762 : /* This function reads only the geometry of the image outline and */
2763 : /* doesn't output the embedded image */
2764 : /************************************************************************/
2765 :
2766 2 : OGRDXFFeature *OGRDXFLayer::TranslateWIPEOUT()
2767 :
2768 : {
2769 : char szLineBuf[257];
2770 : int nCode;
2771 4 : auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2772 2 : double dfX = 0.0, dfY = 0.0, dfXOffset = 0.0, dfYOffset = 0.0;
2773 2 : double dfXscale = 1.0, dfYscale = 1.0;
2774 :
2775 2 : int nNumVertices = 0;
2776 2 : int nBoundaryVertexCount = 0;
2777 2 : int nFormat = 0;
2778 :
2779 4 : DXFSmoothPolyline smoothPolyline;
2780 :
2781 2 : smoothPolyline.setCoordinateDimension(2);
2782 :
2783 : /* Read main feature properties as class, insertion point (in WCS) */
2784 91 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2785 : {
2786 89 : if (nBoundaryVertexCount > nNumVertices)
2787 : {
2788 0 : CPLError(CE_Failure, CPLE_AppDefined,
2789 : "Too many vertices found in WIPEOUT.");
2790 0 : return nullptr;
2791 : }
2792 :
2793 89 : switch (nCode)
2794 : {
2795 : /* Group codes 10, 20 control the insertion point of the lower */
2796 : /* left corner of your image. */
2797 2 : case 10:
2798 2 : dfXOffset = CPLAtof(szLineBuf);
2799 2 : break;
2800 :
2801 2 : case 20:
2802 2 : dfYOffset = CPLAtof(szLineBuf);
2803 2 : smoothPolyline.AddPoint(dfXOffset, dfYOffset, 0.0, 0.0);
2804 2 : break;
2805 :
2806 : /* --------------------------------------------------------------------- */
2807 : /* The group codes 11, 21 and 31 are used to define a vector in 3D space */
2808 : /* that is the endpoint of a line whose start point is assumed to be */
2809 : /* 0,0,0, regardless of the origin point of the image. */
2810 : /* These group codes describe a relative vector. */
2811 : /* --------------------------------------------------------------------- */
2812 :
2813 2 : case 11:
2814 2 : dfXscale = CPLAtof(szLineBuf);
2815 2 : break;
2816 :
2817 2 : case 22:
2818 2 : dfYscale = CPLAtof(szLineBuf);
2819 2 : break;
2820 :
2821 2 : case 31:
2822 2 : break;
2823 :
2824 : /* Read image properties and set them in feature style (contrast...) */
2825 2 : case 281:
2826 2 : break;
2827 :
2828 2 : case 282:
2829 2 : break;
2830 :
2831 0 : case 293:
2832 0 : break;
2833 :
2834 2 : case 71:
2835 2 : nFormat = atoi(szLineBuf);
2836 2 : if (nFormat == 1)
2837 : {
2838 : // Here ignore feature because point format set to 1 is not supported
2839 0 : CPLError(
2840 : CE_Warning, CPLE_AppDefined,
2841 : "Format of points in WIPEOUT entity not supported.");
2842 0 : return nullptr;
2843 : }
2844 2 : break;
2845 :
2846 2 : case 91:
2847 2 : nNumVertices = atoi(szLineBuf);
2848 2 : break;
2849 :
2850 : /* -------------------------------------------------------------------- */
2851 : /* Read clipping boundary properties and set them feature geometry */
2852 : /* Collect vertices as a smooth polyline. */
2853 : /* -------------------------------------------------------------------- */
2854 10 : case 14:
2855 10 : dfX = CPLAtof(szLineBuf);
2856 10 : break;
2857 :
2858 10 : case 24:
2859 10 : dfY = CPLAtof(szLineBuf);
2860 10 : smoothPolyline.AddPoint(dfXOffset + (0.5 + dfX) * dfXscale,
2861 10 : dfYOffset + (0.5 - dfY) * dfYscale, 0.0,
2862 : 0.0);
2863 10 : nBoundaryVertexCount++;
2864 10 : break;
2865 :
2866 51 : default:
2867 51 : TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2868 51 : break;
2869 : }
2870 : }
2871 2 : if (nCode < 0)
2872 : {
2873 0 : DXF_LAYER_READER_ERROR();
2874 0 : return nullptr;
2875 : }
2876 :
2877 2 : if (nCode == 0)
2878 2 : poDS->UnreadValue();
2879 :
2880 : /* -------------------------------------------------------------------- */
2881 : /* Close polyline to output polygon geometry. */
2882 : /* -------------------------------------------------------------------- */
2883 2 : smoothPolyline.Close();
2884 :
2885 2 : OGRGeometry *poGeom = smoothPolyline.Tessellate(TRUE);
2886 :
2887 2 : poFeature->SetGeometryDirectly(poGeom);
2888 :
2889 : // Set style pen color
2890 2 : PrepareLineStyle(poFeature.get());
2891 : //CPLDebug("Wipeout translated", "%s", poFeature->GetFieldAsString("EntityHandle"));
2892 :
2893 2 : return poFeature.release();
2894 : }
2895 :
2896 : /************************************************************************/
2897 :
2898 : /************************************************************************/
2899 : /* InsertBlockReference() */
2900 : /* */
2901 : /* Returns a point geometry located at the block's insertion */
2902 : /* point. */
2903 : /************************************************************************/
2904 : OGRDXFFeature *
2905 805 : OGRDXFLayer::InsertBlockReference(const CPLString &osBlockName,
2906 : const OGRDXFInsertTransformer &oTransformer,
2907 : OGRDXFFeature *const poFeature)
2908 : {
2909 : // Store the block's properties in the special DXF-specific members
2910 : // on the feature object
2911 805 : poFeature->bIsBlockReference = true;
2912 805 : poFeature->osBlockName = osBlockName;
2913 805 : poFeature->dfBlockAngle = oTransformer.dfAngle * 180 / M_PI;
2914 1610 : poFeature->oBlockScale = DXFTriple(
2915 805 : oTransformer.dfXScale, oTransformer.dfYScale, oTransformer.dfZScale);
2916 1610 : poFeature->oOriginalCoords = DXFTriple(
2917 805 : oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
2918 :
2919 : // Only if DXF_INLINE_BLOCKS is false should we ever need to expose these
2920 : // to the end user as fields.
2921 805 : if (poFeature->GetFieldIndex("BlockName") != -1)
2922 : {
2923 13 : poFeature->SetField("BlockName", poFeature->osBlockName);
2924 13 : poFeature->SetField("BlockAngle", poFeature->dfBlockAngle);
2925 13 : poFeature->SetField("BlockScale", 3, &(poFeature->oBlockScale.dfX));
2926 13 : poFeature->SetField("BlockOCSNormal", 3, &(poFeature->oOCS.dfX));
2927 13 : poFeature->SetField("BlockOCSCoords", 3,
2928 13 : &(poFeature->oOriginalCoords.dfX));
2929 : }
2930 :
2931 : // For convenience to the end user, the point geometry will be located
2932 : // at the WCS coordinates of the insertion point.
2933 : OGRPoint *poInsertionPoint = new OGRPoint(
2934 805 : oTransformer.dfXOffset, oTransformer.dfYOffset, oTransformer.dfZOffset);
2935 :
2936 805 : poFeature->ApplyOCSTransformer(poInsertionPoint);
2937 805 : poFeature->SetGeometryDirectly(poInsertionPoint);
2938 :
2939 805 : return poFeature;
2940 : }
2941 :
2942 : /************************************************************************/
2943 : /* InsertBlockInline() */
2944 : /* */
2945 : /* Inserts the given block at the location specified by the given */
2946 : /* transformer. Returns poFeature, or NULL if all features on */
2947 : /* the block have been pushed to the extra feature queue. */
2948 : /* If poFeature is not returned, it is deleted. */
2949 : /* Throws std::invalid_argument if the requested block */
2950 : /* doesn't exist. */
2951 : /* */
2952 : /* - poFeature: The feature to use as a template. This feature's */
2953 : /* OCS will be applied to the block. */
2954 : /* - bInlineRecursively: If true, INSERTs within this block */
2955 : /* will be recursively inserted. Otherwise, they will be */
2956 : /* represented as a point geometry using InsertBlockReference. */
2957 : /* - bMergeGeometry: If true, all features in the block, */
2958 : /* apart from text features, are merged into a */
2959 : /* GeometryCollection which is returned by the function. */
2960 : /************************************************************************/
2961 :
2962 1248 : OGRDXFFeature *OGRDXFLayer::InsertBlockInline(
2963 : GUInt32 nInitialErrorCounter, const CPLString &osBlockName,
2964 : OGRDXFInsertTransformer oTransformer, OGRDXFFeature *const poFeature,
2965 : OGRDXFFeatureQueue &apoExtraFeatures, const bool bInlineRecursively,
2966 : const bool bMergeGeometry)
2967 : {
2968 : /* -------------------------------------------------------------------- */
2969 : /* Set up protection against excessive recursion on this layer. */
2970 : /* -------------------------------------------------------------------- */
2971 1248 : if (!poDS->PushBlockInsertion(osBlockName))
2972 : {
2973 1003 : delete poFeature;
2974 1003 : return nullptr;
2975 : }
2976 :
2977 : /* -------------------------------------------------------------------- */
2978 : /* Transform the insertion point from OCS into */
2979 : /* world coordinates. */
2980 : /* -------------------------------------------------------------------- */
2981 : OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
2982 490 : oTransformer.dfZOffset);
2983 :
2984 245 : poFeature->ApplyOCSTransformer(&oInsertionPoint);
2985 :
2986 245 : oTransformer.dfXOffset = oInsertionPoint.getX();
2987 245 : oTransformer.dfYOffset = oInsertionPoint.getY();
2988 245 : oTransformer.dfZOffset = oInsertionPoint.getZ();
2989 :
2990 : /* -------------------------------------------------------------------- */
2991 : /* Lookup the block. */
2992 : /* -------------------------------------------------------------------- */
2993 245 : DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
2994 :
2995 245 : if (poBlock == nullptr)
2996 : {
2997 : // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
2998 9 : poDS->PopBlockInsertion();
2999 9 : throw std::invalid_argument("osBlockName");
3000 : }
3001 :
3002 : /* -------------------------------------------------------------------- */
3003 : /* If we have complete features associated with the block, push */
3004 : /* them on the pending feature stack copying over key override */
3005 : /* information. */
3006 : /* */
3007 : /* If bMergeGeometry is true, we merge the features */
3008 : /* (except text) into a single GeometryCollection. */
3009 : /* -------------------------------------------------------------------- */
3010 236 : OGRGeometryCollection *poMergedGeometry = nullptr;
3011 236 : if (bMergeGeometry)
3012 93 : poMergedGeometry = new OGRGeometryCollection();
3013 :
3014 472 : OGRDXFFeatureQueue apoInnerExtraFeatures;
3015 :
3016 1975 : for (unsigned int iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size();
3017 : iSubFeat++)
3018 : {
3019 : OGRDXFFeature *poSubFeature =
3020 1741 : poBlock->apoFeatures[iSubFeat]->CloneDXFFeature();
3021 :
3022 : // If the template feature is in PaperSpace, set this on the
3023 : // subfeature too
3024 1741 : if (poFeature->GetFieldAsInteger("PaperSpace"))
3025 48 : poSubFeature->SetField("PaperSpace", 1);
3026 :
3027 : // Does this feature represent a block reference? If so,
3028 : // insert that block
3029 1741 : if (bInlineRecursively && poSubFeature->IsBlockReference())
3030 : {
3031 : // Unpack the transformation data stored in fields of this
3032 : // feature
3033 0 : OGRDXFInsertTransformer oInnerTransformer;
3034 1089 : oInnerTransformer.dfXOffset = poSubFeature->oOriginalCoords.dfX;
3035 1089 : oInnerTransformer.dfYOffset = poSubFeature->oOriginalCoords.dfY;
3036 1089 : oInnerTransformer.dfZOffset = poSubFeature->oOriginalCoords.dfZ;
3037 1089 : oInnerTransformer.dfAngle = poSubFeature->dfBlockAngle * M_PI / 180;
3038 1089 : oInnerTransformer.dfXScale = poSubFeature->oBlockScale.dfX;
3039 1089 : oInnerTransformer.dfYScale = poSubFeature->oBlockScale.dfY;
3040 1089 : oInnerTransformer.dfZScale = poSubFeature->oBlockScale.dfZ;
3041 :
3042 1089 : poSubFeature->bIsBlockReference = false;
3043 :
3044 : // Keep a reference to the attributes that need to be inserted
3045 : std::vector<std::unique_ptr<OGRDXFFeature>> apoInnerAttribFeatures =
3046 1089 : std::move(poSubFeature->apoAttribFeatures);
3047 :
3048 : // Insert this block recursively
3049 : try
3050 : {
3051 2178 : poSubFeature = InsertBlockInline(
3052 1089 : nInitialErrorCounter, poSubFeature->osBlockName,
3053 1089 : std::move(oInnerTransformer), poSubFeature,
3054 : apoInnerExtraFeatures, true, bMergeGeometry);
3055 : }
3056 0 : catch (const std::invalid_argument &)
3057 : {
3058 : // Block doesn't exist. Skip it and keep going
3059 0 : delete poSubFeature;
3060 0 : if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
3061 : {
3062 0 : break;
3063 : }
3064 0 : continue;
3065 : }
3066 :
3067 1089 : if (!poSubFeature)
3068 : {
3069 1083 : if (CPLGetErrorCounter() > nInitialErrorCounter + 1000)
3070 : {
3071 2 : break;
3072 : }
3073 :
3074 : // Append the attribute features to the pending feature stack
3075 1083 : for (auto &poAttribFeature : apoInnerAttribFeatures)
3076 : {
3077 : // Clear the attribute tag so the feature doesn't get mistaken
3078 : // for an ATTDEF and skipped
3079 2 : poAttribFeature->osAttributeTag = "";
3080 :
3081 2 : apoInnerExtraFeatures.push(poAttribFeature.release());
3082 : }
3083 :
3084 1081 : if (apoInnerExtraFeatures.empty())
3085 : {
3086 : // Block is empty and has no attributes. Skip it and keep going
3087 1002 : continue;
3088 : }
3089 : else
3090 : {
3091 : // Load up the first extra feature ready for
3092 : // transformation
3093 79 : poSubFeature = apoInnerExtraFeatures.front();
3094 79 : apoInnerExtraFeatures.pop();
3095 : }
3096 : }
3097 : }
3098 :
3099 : // Go through the current feature and any extra features generated
3100 : // by the recursive insert, and apply transformations
3101 : while (true)
3102 : {
3103 888 : OGRGeometry *poSubFeatGeom = poSubFeature->GetGeometryRef();
3104 888 : if (poSubFeatGeom != nullptr)
3105 : {
3106 : // Rotation and scaling first
3107 : OGRDXFInsertTransformer oInnerTrans =
3108 1770 : oTransformer.GetRotateScaleTransformer();
3109 885 : poSubFeatGeom->transform(&oInnerTrans);
3110 :
3111 : // Then the OCS to WCS transformation
3112 885 : poFeature->ApplyOCSTransformer(poSubFeatGeom);
3113 :
3114 : // Offset translation last
3115 885 : oInnerTrans = oTransformer.GetOffsetTransformer();
3116 885 : poSubFeatGeom->transform(&oInnerTrans);
3117 : }
3118 : // Transform the specially-stored data for ASM entities
3119 3 : else if (poSubFeature->poASMTransform)
3120 : {
3121 : // Rotation and scaling first
3122 : OGRDXFInsertTransformer oInnerTrans =
3123 6 : oTransformer.GetRotateScaleTransformer();
3124 3 : poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
3125 :
3126 : // Then the OCS to WCS transformation
3127 3 : poFeature->ApplyOCSTransformer(
3128 : poSubFeature->poASMTransform.get());
3129 :
3130 : // Offset translation last
3131 3 : oInnerTrans = oTransformer.GetOffsetTransformer();
3132 3 : poSubFeature->poASMTransform->ComposeWith(oInnerTrans);
3133 :
3134 3 : poSubFeature->poASMTransform->SetField(poSubFeature,
3135 : "ASMTransform");
3136 : }
3137 :
3138 : // If we are merging features, and this is not text or a block
3139 : // reference, merge it into the GeometryCollection
3140 1080 : if (bMergeGeometry &&
3141 192 : (poSubFeature->GetStyleString() == nullptr ||
3142 175 : strstr(poSubFeature->GetStyleString(), "LABEL") == nullptr) &&
3143 1197 : !poSubFeature->IsBlockReference() &&
3144 117 : poSubFeature->GetGeometryRef())
3145 : {
3146 114 : poMergedGeometry->addGeometryDirectly(
3147 114 : poSubFeature->StealGeometry());
3148 114 : delete poSubFeature;
3149 : }
3150 : // Import all other features, except ATTDEFs when inlining
3151 : // recursively
3152 774 : else if (!bInlineRecursively || poSubFeature->osAttributeTag == "")
3153 : {
3154 : // If the subfeature is on layer 0, this is a special case: the
3155 : // subfeature should take on the style properties of the layer
3156 : // the block is being inserted onto.
3157 : // But don't do this if we are inserting onto a Blocks layer
3158 : // (that is, the owning feature has no layer).
3159 973 : if (EQUAL(poSubFeature->GetFieldAsString("Layer"), "0") &&
3160 231 : !EQUAL(poFeature->GetFieldAsString("Layer"), ""))
3161 : {
3162 200 : poSubFeature->SetField(
3163 : "Layer", poFeature->GetFieldAsString("Layer"));
3164 : }
3165 :
3166 : // Update the style string to replace ByBlock and ByLayer
3167 : // values.
3168 742 : PrepareFeatureStyle(poSubFeature, poFeature);
3169 :
3170 742 : ACAdjustText(oTransformer.dfAngle * 180 / M_PI,
3171 : oTransformer.dfXScale, oTransformer.dfYScale,
3172 : poSubFeature);
3173 :
3174 742 : if (!EQUAL(poFeature->GetFieldAsString("EntityHandle"), ""))
3175 : {
3176 705 : poSubFeature->SetField(
3177 : "EntityHandle",
3178 : poFeature->GetFieldAsString("EntityHandle"));
3179 : }
3180 :
3181 742 : apoExtraFeatures.push(poSubFeature);
3182 : }
3183 : else
3184 : {
3185 32 : delete poSubFeature;
3186 : }
3187 :
3188 888 : if (apoInnerExtraFeatures.empty())
3189 : {
3190 737 : break;
3191 : }
3192 : else
3193 : {
3194 151 : poSubFeature = apoInnerExtraFeatures.front();
3195 151 : apoInnerExtraFeatures.pop();
3196 : }
3197 151 : }
3198 : }
3199 :
3200 238 : while (!apoInnerExtraFeatures.empty())
3201 : {
3202 2 : auto poFeatureToDelete = apoInnerExtraFeatures.front();
3203 2 : apoInnerExtraFeatures.pop();
3204 2 : delete poFeatureToDelete;
3205 : }
3206 :
3207 236 : poDS->PopBlockInsertion();
3208 :
3209 : /* -------------------------------------------------------------------- */
3210 : /* Return the merged geometry if applicable. Otherwise */
3211 : /* return NULL and let the machinery find the rest of the */
3212 : /* features in the pending feature stack. */
3213 : /* -------------------------------------------------------------------- */
3214 236 : if (bMergeGeometry)
3215 : {
3216 93 : if (poMergedGeometry->getNumGeometries() == 0)
3217 : {
3218 14 : delete poMergedGeometry;
3219 : }
3220 : else
3221 : {
3222 79 : poFeature->SetGeometryDirectly(
3223 : SimplifyBlockGeometry(poMergedGeometry));
3224 :
3225 79 : PrepareLineStyle(poFeature);
3226 79 : return poFeature;
3227 : }
3228 : }
3229 :
3230 157 : delete poFeature;
3231 157 : return nullptr;
3232 : }
3233 :
3234 : /************************************************************************/
3235 : /* TranslateINSERT() */
3236 : /************************************************************************/
3237 :
3238 141 : bool OGRDXFLayer::TranslateINSERT()
3239 :
3240 : {
3241 : char szLineBuf[257];
3242 141 : int nCode = 0;
3243 :
3244 141 : m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
3245 141 : m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
3246 141 : m_oInsertState.m_osBlockName.clear();
3247 141 : m_oInsertState.m_nColumnCount = 1;
3248 141 : m_oInsertState.m_nRowCount = 1;
3249 141 : m_oInsertState.m_iCurCol = 0;
3250 141 : m_oInsertState.m_iCurRow = 0;
3251 141 : m_oInsertState.m_dfColumnSpacing = 0.0;
3252 141 : m_oInsertState.m_dfRowSpacing = 0.0;
3253 :
3254 141 : bool bHasAttribs = false;
3255 141 : m_oInsertState.m_apoAttribs.clear();
3256 141 : m_oInsertState.m_aosAttribs.Clear();
3257 :
3258 : /* -------------------------------------------------------------------- */
3259 : /* Process values. */
3260 : /* -------------------------------------------------------------------- */
3261 1469 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3262 : {
3263 1328 : switch (nCode)
3264 : {
3265 139 : case 10:
3266 139 : m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
3267 139 : break;
3268 :
3269 139 : case 20:
3270 139 : m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
3271 139 : break;
3272 :
3273 132 : case 30:
3274 132 : m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
3275 132 : break;
3276 :
3277 29 : case 41:
3278 29 : m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
3279 29 : break;
3280 :
3281 32 : case 42:
3282 32 : m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
3283 32 : break;
3284 :
3285 17 : case 43:
3286 17 : m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
3287 17 : break;
3288 :
3289 6 : case 44:
3290 6 : m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
3291 6 : break;
3292 :
3293 6 : case 45:
3294 6 : m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
3295 6 : break;
3296 :
3297 24 : case 50:
3298 : // We want to transform this to radians.
3299 : // It is apparently always in degrees regardless of $AUNITS
3300 24 : m_oInsertState.m_oTransformer.dfAngle =
3301 24 : CPLAtof(szLineBuf) * M_PI / 180.0;
3302 24 : break;
3303 :
3304 14 : case 66:
3305 14 : bHasAttribs = atoi(szLineBuf) == 1;
3306 14 : break;
3307 :
3308 2 : case 70:
3309 2 : m_oInsertState.m_nColumnCount = atoi(szLineBuf);
3310 2 : if (m_oInsertState.m_nColumnCount < 0)
3311 : {
3312 0 : DXF_LAYER_READER_ERROR();
3313 0 : m_oInsertState.m_nRowCount = 0;
3314 0 : m_oInsertState.m_nColumnCount = 0;
3315 0 : return false;
3316 : }
3317 2 : break;
3318 :
3319 3 : case 71:
3320 3 : m_oInsertState.m_nRowCount = atoi(szLineBuf);
3321 3 : if (m_oInsertState.m_nRowCount < 0)
3322 : {
3323 0 : DXF_LAYER_READER_ERROR();
3324 0 : m_oInsertState.m_nRowCount = 0;
3325 0 : m_oInsertState.m_nColumnCount = 0;
3326 0 : return false;
3327 : }
3328 3 : break;
3329 :
3330 139 : case 2:
3331 139 : m_oInsertState.m_osBlockName = szLineBuf;
3332 139 : break;
3333 :
3334 646 : default:
3335 646 : TranslateGenericProperty(
3336 : m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
3337 646 : break;
3338 : }
3339 : }
3340 141 : if (nCode < 0)
3341 : {
3342 0 : DXF_LAYER_READER_ERROR();
3343 0 : m_oInsertState.m_nRowCount = 0;
3344 0 : m_oInsertState.m_nColumnCount = 0;
3345 0 : return false;
3346 : }
3347 :
3348 141 : if (m_oInsertState.m_nRowCount == 0 || m_oInsertState.m_nColumnCount == 0)
3349 : {
3350 : // AutoCad doesn't allow setting to 0 in its UI, but interprets 0
3351 : // as 1 (but other software such as LibreCAD interpret 0 as 0)
3352 1 : m_oInsertState.m_nRowCount = 1;
3353 1 : m_oInsertState.m_nColumnCount = 1;
3354 : }
3355 :
3356 : /* -------------------------------------------------------------------- */
3357 : /* Process any attribute entities. */
3358 : /* -------------------------------------------------------------------- */
3359 :
3360 141 : if (bHasAttribs)
3361 : {
3362 41 : while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
3363 : {
3364 27 : if (!EQUAL(szLineBuf, "ATTRIB"))
3365 : {
3366 0 : DXF_LAYER_READER_ERROR();
3367 0 : m_oInsertState.m_nRowCount = 0;
3368 0 : m_oInsertState.m_nColumnCount = 0;
3369 0 : return false;
3370 : }
3371 :
3372 : auto poAttribFeature =
3373 27 : std::unique_ptr<OGRDXFFeature>(TranslateTEXT(true));
3374 :
3375 27 : if (poAttribFeature && poAttribFeature->osAttributeTag != "")
3376 : {
3377 : m_oInsertState.m_apoAttribs.emplace_back(
3378 27 : std::move(poAttribFeature));
3379 : }
3380 :
3381 27 : nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
3382 : }
3383 : }
3384 127 : else if (nCode == 0)
3385 : {
3386 127 : poDS->UnreadValue();
3387 : }
3388 :
3389 : /* -------------------------------------------------------------------- */
3390 : /* Prepare a string list of the attributes and their text values */
3391 : /* as space-separated entries, to be stored in the */
3392 : /* BlockAttributes field if we are not inlining blocks. */
3393 : /* -------------------------------------------------------------------- */
3394 :
3395 145 : if (!poDS->InlineBlocks() && bHasAttribs &&
3396 4 : poFeatureDefn->GetFieldIndex("BlockAttributes") != -1)
3397 : {
3398 10 : for (const auto &poAttr : m_oInsertState.m_apoAttribs)
3399 : {
3400 14 : CPLString osAttribString = poAttr->osAttributeTag;
3401 7 : osAttribString += " ";
3402 7 : osAttribString += poAttr->GetFieldAsString("Text");
3403 :
3404 7 : m_oInsertState.m_aosAttribs.AddString(osAttribString);
3405 : }
3406 : }
3407 :
3408 141 : return true;
3409 : }
3410 :
3411 : /************************************************************************/
3412 : /* GenerateINSERTFeatures() */
3413 : /************************************************************************/
3414 :
3415 908 : bool OGRDXFLayer::GenerateINSERTFeatures()
3416 : {
3417 : OGRDXFFeature *poFeature =
3418 908 : m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
3419 :
3420 908 : const double dfExtraXOffset =
3421 908 : m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3422 908 : cos(m_oInsertState.m_oTransformer.dfAngle) +
3423 908 : m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3424 908 : -sin(m_oInsertState.m_oTransformer.dfAngle);
3425 908 : const double dfExtraYOffset =
3426 908 : m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3427 908 : sin(m_oInsertState.m_oTransformer.dfAngle) +
3428 908 : m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3429 908 : cos(m_oInsertState.m_oTransformer.dfAngle);
3430 :
3431 1816 : OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
3432 908 : oTransformer.dfXOffset += dfExtraXOffset;
3433 908 : oTransformer.dfYOffset += dfExtraYOffset;
3434 :
3435 : // If we are not inlining blocks, just insert a point that refers
3436 : // to this block
3437 908 : if (!poDS->InlineBlocks())
3438 : {
3439 805 : poFeature = InsertBlockReference(m_oInsertState.m_osBlockName,
3440 : oTransformer, poFeature);
3441 :
3442 805 : auto papszAttribs = m_oInsertState.m_aosAttribs.List();
3443 805 : if (papszAttribs)
3444 3 : poFeature->SetField("BlockAttributes", papszAttribs);
3445 :
3446 805 : poFeature->apoAttribFeatures = std::move(m_oInsertState.m_apoAttribs);
3447 :
3448 805 : apoPendingFeatures.push(poFeature);
3449 : }
3450 : // Otherwise, try inlining the contents of this block
3451 : else
3452 : {
3453 103 : OGRDXFFeatureQueue apoExtraFeatures;
3454 : try
3455 : {
3456 206 : poFeature = InsertBlockInline(
3457 103 : CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
3458 103 : std::move(oTransformer), poFeature, apoExtraFeatures, true,
3459 103 : poDS->ShouldMergeBlockGeometries());
3460 : }
3461 0 : catch (const std::invalid_argument &)
3462 : {
3463 : // Block doesn't exist
3464 0 : CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
3465 : m_oInsertState.m_osBlockName.c_str());
3466 0 : delete poFeature;
3467 0 : return false;
3468 : }
3469 :
3470 103 : if (poFeature)
3471 55 : apoPendingFeatures.push(poFeature);
3472 :
3473 410 : while (!apoExtraFeatures.empty())
3474 : {
3475 307 : apoPendingFeatures.push(apoExtraFeatures.front());
3476 307 : apoExtraFeatures.pop();
3477 : }
3478 :
3479 : // Append the attribute features to the pending feature stack
3480 103 : if (!m_oInsertState.m_apoAttribs.empty())
3481 : {
3482 16 : OGRDXFInsertTransformer oAttribTransformer;
3483 16 : oAttribTransformer.dfXOffset = dfExtraXOffset;
3484 16 : oAttribTransformer.dfYOffset = dfExtraYOffset;
3485 :
3486 42 : for (const auto &poAttr : m_oInsertState.m_apoAttribs)
3487 : {
3488 26 : OGRDXFFeature *poAttribFeature = poAttr->CloneDXFFeature();
3489 :
3490 26 : if (poAttribFeature->GetGeometryRef())
3491 : {
3492 26 : poAttribFeature->GetGeometryRef()->transform(
3493 26 : &oAttribTransformer);
3494 : }
3495 :
3496 26 : apoPendingFeatures.push(poAttribFeature);
3497 : }
3498 : }
3499 : }
3500 908 : return true;
3501 : }
3502 :
3503 : /************************************************************************/
3504 : /* GetNextUnfilteredFeature() */
3505 : /************************************************************************/
3506 :
3507 2051 : OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
3508 :
3509 : {
3510 2051 : OGRDXFFeature *poFeature = nullptr;
3511 3861 : while (poFeature == nullptr)
3512 : {
3513 : /* --------------------------------------------------------------------
3514 : */
3515 : /* If we have pending features, return one of them. */
3516 : /* --------------------------------------------------------------------
3517 : */
3518 3298 : if (!apoPendingFeatures.empty())
3519 : {
3520 1390 : poFeature = apoPendingFeatures.front();
3521 1390 : apoPendingFeatures.pop();
3522 :
3523 1390 : poFeature->SetFID(iNextFID++);
3524 1488 : return poFeature;
3525 : }
3526 :
3527 : /* --------------------------------------------------------------------
3528 : */
3529 : /* Emit INSERT features. */
3530 : /* --------------------------------------------------------------------
3531 : */
3532 1908 : if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
3533 : {
3534 1018 : if (m_oInsertState.m_iCurCol == m_oInsertState.m_nColumnCount)
3535 : {
3536 871 : m_oInsertState.m_iCurRow++;
3537 871 : m_oInsertState.m_iCurCol = 0;
3538 871 : if (m_oInsertState.m_iCurRow == m_oInsertState.m_nRowCount)
3539 : {
3540 110 : m_oInsertState.m_nRowCount = 0;
3541 110 : m_oInsertState.m_nColumnCount = 0;
3542 1018 : continue;
3543 : }
3544 : }
3545 908 : if (GenerateINSERTFeatures())
3546 : {
3547 908 : m_oInsertState.m_iCurCol++;
3548 : }
3549 : else
3550 : {
3551 0 : m_oInsertState.m_nRowCount = 0;
3552 0 : m_oInsertState.m_nColumnCount = 0;
3553 : }
3554 908 : continue;
3555 : }
3556 :
3557 : // read ahead to an entity.
3558 : char szLineBuf[257];
3559 890 : int nCode = 0;
3560 1016 : while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3561 : {
3562 : }
3563 890 : if (nCode < 0)
3564 : {
3565 5 : DXF_LAYER_READER_ERROR();
3566 5 : return nullptr;
3567 : }
3568 :
3569 885 : if (EQUAL(szLineBuf, "ENDSEC"))
3570 : {
3571 : // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
3572 14 : poDS->UnreadValue();
3573 14 : return nullptr;
3574 : }
3575 :
3576 871 : if (EQUAL(szLineBuf, "ENDBLK"))
3577 : {
3578 : // CPLDebug( "DXF", "Clean end of block at ENDBLK." );
3579 79 : poDS->UnreadValue();
3580 79 : return nullptr;
3581 : }
3582 :
3583 : /* --------------------------------------------------------------------
3584 : */
3585 : /* Handle the entity. */
3586 : /* --------------------------------------------------------------------
3587 : */
3588 792 : if (EQUAL(szLineBuf, "POINT"))
3589 : {
3590 35 : poFeature = TranslatePOINT();
3591 : }
3592 757 : else if (EQUAL(szLineBuf, "MTEXT"))
3593 : {
3594 40 : poFeature = TranslateMTEXT();
3595 : }
3596 717 : else if (EQUAL(szLineBuf, "TEXT"))
3597 : {
3598 26 : poFeature = TranslateTEXT(false);
3599 : }
3600 691 : else if (EQUAL(szLineBuf, "ATTDEF"))
3601 : {
3602 10 : poFeature = TranslateTEXT(true);
3603 : }
3604 681 : else if (EQUAL(szLineBuf, "LINE"))
3605 : {
3606 188 : poFeature = TranslateLINE();
3607 : }
3608 493 : else if (EQUAL(szLineBuf, "POLYLINE"))
3609 : {
3610 19 : poFeature = TranslatePOLYLINE();
3611 : }
3612 474 : else if (EQUAL(szLineBuf, "LWPOLYLINE"))
3613 : {
3614 37 : poFeature = TranslateLWPOLYLINE();
3615 : }
3616 437 : else if (EQUAL(szLineBuf, "MLINE"))
3617 : {
3618 3 : poFeature = TranslateMLINE();
3619 : }
3620 434 : else if (EQUAL(szLineBuf, "CIRCLE"))
3621 : {
3622 24 : poFeature = TranslateCIRCLE();
3623 : }
3624 410 : else if (EQUAL(szLineBuf, "ELLIPSE"))
3625 : {
3626 29 : poFeature = TranslateELLIPSE();
3627 : }
3628 381 : else if (EQUAL(szLineBuf, "ARC"))
3629 : {
3630 14 : poFeature = TranslateARC();
3631 : }
3632 367 : else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
3633 : {
3634 25 : poFeature = TranslateSPLINE();
3635 : }
3636 342 : else if (EQUAL(szLineBuf, "3DFACE"))
3637 : {
3638 10 : poFeature = Translate3DFACE();
3639 : }
3640 332 : else if (EQUAL(szLineBuf, "INSERT"))
3641 : {
3642 141 : if (!TranslateINSERT())
3643 0 : return nullptr;
3644 : }
3645 191 : else if (EQUAL(szLineBuf, "DIMENSION"))
3646 : {
3647 30 : poFeature = TranslateDIMENSION();
3648 : }
3649 161 : else if (EQUAL(szLineBuf, "HATCH"))
3650 : {
3651 28 : poFeature = TranslateHATCH();
3652 : }
3653 133 : else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
3654 : {
3655 25 : poFeature = TranslateSOLID();
3656 : }
3657 108 : else if (EQUAL(szLineBuf, "LEADER"))
3658 : {
3659 14 : poFeature = TranslateLEADER();
3660 : }
3661 94 : else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
3662 : {
3663 18 : poFeature = TranslateMLEADER();
3664 : }
3665 76 : else if (EQUAL(szLineBuf, "WIPEOUT"))
3666 : {
3667 2 : poFeature = TranslateWIPEOUT();
3668 : }
3669 74 : else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
3670 72 : EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
3671 : {
3672 2 : if (poDS->In3DExtensibleMode())
3673 : {
3674 2 : poFeature = TranslateASMEntity();
3675 : }
3676 0 : else if (oIgnoredEntities.count(szLineBuf) == 0)
3677 : {
3678 0 : oIgnoredEntities.insert(szLineBuf);
3679 0 : CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
3680 : szLineBuf);
3681 : }
3682 : }
3683 : else
3684 : {
3685 72 : if (oIgnoredEntities.count(szLineBuf) == 0)
3686 : {
3687 13 : oIgnoredEntities.insert(szLineBuf);
3688 13 : CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
3689 : szLineBuf);
3690 : }
3691 : }
3692 : }
3693 :
3694 : /* -------------------------------------------------------------------- */
3695 : /* Set FID. */
3696 : /* -------------------------------------------------------------------- */
3697 563 : poFeature->SetFID(iNextFID++);
3698 563 : m_nFeaturesRead++;
3699 :
3700 563 : return poFeature;
3701 : }
3702 :
3703 : /************************************************************************/
3704 : /* GetNextFeature() */
3705 : /************************************************************************/
3706 :
3707 934 : OGRFeature *OGRDXFLayer::GetNextFeature()
3708 :
3709 : {
3710 : while (true)
3711 : {
3712 934 : OGRFeature *poFeature = GetNextUnfilteredFeature();
3713 :
3714 934 : if (poFeature == nullptr)
3715 16 : return nullptr;
3716 :
3717 1836 : if ((m_poFilterGeom == nullptr ||
3718 1836 : FilterGeometry(poFeature->GetGeometryRef())) &&
3719 918 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
3720 : {
3721 918 : return poFeature;
3722 : }
3723 :
3724 0 : delete poFeature;
3725 0 : }
3726 : }
3727 :
3728 : /************************************************************************/
3729 : /* TestCapability() */
3730 : /************************************************************************/
3731 :
3732 0 : int OGRDXFLayer::TestCapability(const char *pszCap)
3733 :
3734 : {
3735 0 : if (EQUAL(pszCap, OLCStringsAsUTF8))
3736 0 : return true;
3737 0 : else if (EQUAL(pszCap, OLCZGeometries))
3738 0 : return true;
3739 0 : return false;
3740 : }
3741 :
3742 : /************************************************************************/
3743 : /* GetDataset() */
3744 : /************************************************************************/
3745 :
3746 1 : GDALDataset *OGRDXFLayer::GetDataset()
3747 : {
3748 1 : return poDS;
3749 : }
|