Coverage Report

Created: 2026-09-26 08:22

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