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