Coverage Report

Created: 2025-06-09 08:44

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