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