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