Coverage Report

Created: 2026-06-30 08:33

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
150k
{
32
150k
    apoFeatures.push(poFeature);
33
150k
}
34
35
/************************************************************************/
36
/*                                pop()                                 */
37
/************************************************************************/
38
39
void OGRDXFFeatureQueue::pop()
40
150k
{
41
150k
    CPLAssert(!apoFeatures.empty());
42
150k
    apoFeatures.pop();
43
150k
}
44
45
/************************************************************************/
46
/*                            OGRDXFLayer()                             */
47
/************************************************************************/
48
49
OGRDXFLayer::OGRDXFLayer(OGRDXFDataSource *poDSIn)
50
10.7k
    : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("entities")), iNextFID(0)
51
10.7k
{
52
10.7k
    poFeatureDefn->Reference();
53
54
10.7k
    int nModes = ODFM_None;
55
10.7k
    if (!poDS->InlineBlocks())
56
0
        nModes |= ODFM_IncludeBlockFields;
57
10.7k
    if (poDS->ShouldIncludeRawCodeValues())
58
0
        nModes |= ODFM_IncludeRawCodeValues;
59
10.7k
    if (poDS->In3DExtensibleMode())
60
0
        nModes |= ODFM_Include3DModeFields;
61
10.7k
    OGRDXFDataSource::AddStandardFields(poFeatureDefn, nModes);
62
63
10.7k
    SetDescription(poFeatureDefn->GetName());
64
10.7k
}
65
66
/************************************************************************/
67
/*                            ~OGRDXFLayer()                            */
68
/************************************************************************/
69
70
OGRDXFLayer::~OGRDXFLayer()
71
72
10.7k
{
73
10.7k
    ClearPendingFeatures();
74
10.7k
    if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
75
9.90k
    {
76
9.90k
        CPLDebug("DXF", "%d features read on layer '%s'.", (int)m_nFeaturesRead,
77
9.90k
                 poFeatureDefn->GetName());
78
9.90k
    }
79
80
10.7k
    if (poFeatureDefn)
81
10.7k
        poFeatureDefn->Release();
82
10.7k
}
83
84
/************************************************************************/
85
/*                        ClearPendingFeatures()                        */
86
/************************************************************************/
87
88
void OGRDXFLayer::ClearPendingFeatures()
89
90
21.5k
{
91
21.6k
    while (!apoPendingFeatures.empty())
92
31
    {
93
31
        OGRDXFFeature *poFeature = apoPendingFeatures.front();
94
31
        apoPendingFeatures.pop();
95
31
        delete poFeature;
96
31
    }
97
21.5k
}
98
99
/************************************************************************/
100
/*                            ResetReading()                            */
101
/************************************************************************/
102
103
void OGRDXFLayer::ResetReading()
104
105
10.7k
{
106
10.7k
    iNextFID = 0;
107
10.7k
    ClearPendingFeatures();
108
10.7k
    m_oInsertState.m_nRowCount = 0;
109
10.7k
    m_oInsertState.m_nColumnCount = 0;
110
10.7k
    poDS->RestartEntities();
111
10.7k
}
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
5.89M
{
124
5.89M
    switch (nCode)
125
5.89M
    {
126
165k
        case 8:
127
165k
            poFeature->SetField("Layer", TextRecode(pszValue));
128
165k
            break;
129
130
197k
        case 100:
131
197k
        {
132
197k
            CPLString osSubClass = poFeature->GetFieldAsString("SubClasses");
133
197k
            if (!osSubClass.empty())
134
120k
                osSubClass += ":";
135
197k
            osSubClass += pszValue;
136
197k
            poFeature->SetField("SubClasses", osSubClass.c_str());
137
197k
        }
138
197k
        break;
139
140
2.29k
        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.29k
            {
145
2.29k
                char szLineBuf[257];
146
                // Eat the rest of this entity
147
7.93k
                while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) >
148
7.93k
                       0)
149
5.64k
                {
150
5.64k
                }
151
152
2.29k
                if (nCode < 0)
153
435
                {
154
                    // Let the entity reader function discover this error for
155
                    // itself
156
435
                    return;
157
435
                }
158
159
1.86k
                CPLAssert(nCode == 0);
160
1.86k
                poDS->UnreadValue();
161
1.86k
            }
162
0
            break;
163
164
71.6k
        case 60:
165
71.6k
            if (atoi(pszValue))
166
42.2k
                poFeature->oStyleProperties["Hidden"] = "1";
167
71.6k
            break;
168
169
9.74k
        case 67:
170
9.74k
            if (atoi(pszValue))
171
4.75k
                poFeature->SetField("PaperSpace", 1);
172
9.74k
            break;
173
174
65.3k
        case 62:
175
65.3k
            poFeature->oStyleProperties["Color"] = pszValue;
176
65.3k
            break;
177
178
40.0k
        case 420:
179
40.0k
            poFeature->oStyleProperties["TrueColor"] = pszValue;
180
40.0k
            break;
181
182
3.12k
        case 440:
183
3.12k
            poFeature->oStyleProperties["Transparency"] = pszValue;
184
3.12k
            break;
185
186
105k
        case 6:
187
105k
            poFeature->SetField("Linetype", TextRecode(pszValue));
188
105k
            break;
189
190
82.3k
        case 48:
191
82.3k
            poFeature->oStyleProperties["LinetypeScale"] = pszValue;
192
82.3k
            break;
193
194
3.17k
        case 370:
195
32.8k
        case 39:
196
32.8k
            poFeature->oStyleProperties["LineWeight"] = pszValue;
197
32.8k
            break;
198
199
190k
        case 5:
200
190k
            poFeature->SetField("EntityHandle", pszValue);
201
190k
            break;
202
203
        // OCS vector.
204
45.6k
        case 210:
205
45.6k
            poFeature->oOCS.dfX = CPLAtof(pszValue);
206
45.6k
            break;
207
208
96.8k
        case 220:
209
96.8k
            poFeature->oOCS.dfY = CPLAtof(pszValue);
210
96.8k
            break;
211
212
78.8k
        case 230:
213
78.8k
            poFeature->oOCS.dfZ = CPLAtof(pszValue);
214
78.8k
            break;
215
216
4.70M
        default:
217
4.70M
            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
4.70M
            break;
235
5.89M
    }
236
5.89M
}
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
169k
{
296
169k
    CPLString osStyle = "BRUSH(fc:";
297
169k
    const std::string osForegroundColor =
298
169k
        poFeature->GetColor(poDS, poBlockFeature);
299
169k
    osStyle += osForegroundColor;
300
301
169k
    if (poFeature->oStyleProperties.count("FillFlag") > 0 &&
302
14.8k
        poFeature->oStyleProperties["FillFlag"] == "Pattern")
303
13.2k
    {
304
13.2k
        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
13.2k
        double dfRotation = 0.0;
332
13.2k
        if (poFeature->oStyleProperties.count("HatchPatternRotation") > 0)
333
955
        {
334
955
            dfRotation =
335
955
                CPLAtof(poFeature->oStyleProperties["HatchPatternRotation"]);
336
955
        }
337
338
13.2k
        const char *pszPatternName = poFeature->GetFieldAsString("Text");
339
13.2k
        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
13.2k
        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
13.2k
        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
13.2k
        if (poFeature->oStyleProperties.count("HatchPatternScale") > 0)
391
1.01k
        {
392
1.01k
            const double dfScale =
393
1.01k
                CPLAtof(poFeature->oStyleProperties["HatchPatternScale"]);
394
1.01k
            if (std::fabs(dfScale - 1) > 1e-12)
395
70
            {
396
70
                osStyle += CPLSPrintf(",s:%f", dfScale);
397
70
            }
398
1.01k
        }
399
13.2k
    }
400
156k
    else if (osForegroundColor == "#00000000")
401
11.4k
    {
402
11.4k
        osStyle += ",id:\"ogr-brush-1\"";
403
11.4k
    }
404
405
169k
    osStyle += ")";
406
407
169k
    poFeature->SetStyleString(osStyle);
408
169k
}
409
410
/************************************************************************/
411
/*                          PrepareLineStyle()                          */
412
/************************************************************************/
413
414
void OGRDXFLayer::PrepareLineStyle(
415
    OGRDXFFeature *const poFeature,
416
    OGRDXFFeature *const poBlockFeature /* = NULL */)
417
418
456k
{
419
456k
    const CPLString osLayer = poFeature->GetFieldAsString("Layer");
420
421
    /* -------------------------------------------------------------------- */
422
    /*      Get line weight if available.                                   */
423
    /* -------------------------------------------------------------------- */
424
456k
    double dfWeight = 0.0;
425
456k
    CPLString osWeight = "-1";
426
427
456k
    if (poFeature->oStyleProperties.count("LineWeight") > 0)
428
14.7k
        osWeight = poFeature->oStyleProperties["LineWeight"];
429
430
    // Use ByBlock lineweight?
431
456k
    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
456k
    if (CPLAtof(osWeight) == -1)
452
441k
    {
453
441k
        auto osLayerLineWeight =
454
441k
            poDS->LookupLayerProperty(osLayer, "LineWeight");
455
441k
        osWeight = osLayerLineWeight ? *osLayerLineWeight : CPLString();
456
441k
    }
457
458
    // Will be zero in the case of an invalid value
459
456k
    dfWeight = CPLAtof(osWeight) / 100.0;
460
461
    /* -------------------------------------------------------------------- */
462
    /*      Do we have a dash/dot line style?                               */
463
    /* -------------------------------------------------------------------- */
464
456k
    CPLString osLinetype = poFeature->GetFieldAsString("Linetype");
465
466
    // Use ByBlock line style?
467
456k
    if (!osLinetype.empty() && EQUAL(osLinetype, "ByBlock") && poBlockFeature)
468
3
    {
469
3
        osLinetype = poBlockFeature->GetFieldAsString("Linetype");
470
471
        // Use the inherited line style if we regenerate the style string
472
        // again during block insertion
473
3
        if (!osLinetype.empty())
474
3
            poFeature->SetField("Linetype", osLinetype);
475
3
    }
476
477
    // Use layer line style?
478
456k
    if (osLinetype.empty())
479
430k
    {
480
430k
        auto osLayerLineType = poDS->LookupLayerProperty(osLayer, "Linetype");
481
430k
        if (osLayerLineType)
482
0
            osLinetype = *osLayerLineType;
483
430k
    }
484
485
456k
    const std::vector<double> oLineType = poDS->LookupLineType(osLinetype);
486
487
    // Linetype scale is not inherited from the block feature
488
456k
    double dfLineTypeScale = CPLAtof(poDS->GetVariable("$LTSCALE", "1.0"));
489
456k
    if (poFeature->oStyleProperties.count("LinetypeScale") > 0)
490
23.9k
        dfLineTypeScale *=
491
23.9k
            CPLAtof(poFeature->oStyleProperties["LinetypeScale"]);
492
493
456k
    CPLString osPattern;
494
456k
    for (std::vector<double>::const_iterator oIt = oLineType.begin();
495
456k
         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
456k
    if (osPattern.length() > 0)
503
0
        osPattern.erase(osPattern.end() - 1);
504
505
    /* -------------------------------------------------------------------- */
506
    /*      Format the style string.                                        */
507
    /* -------------------------------------------------------------------- */
508
509
456k
    CPLString osStyle = "PEN(c:";
510
456k
    osStyle += poFeature->GetColor(poDS, poBlockFeature);
511
512
456k
    if (dfWeight > 0.0)
513
8.83k
    {
514
8.83k
        char szBuffer[64];
515
8.83k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight);
516
8.83k
        osStyle += CPLString().Printf(",w:%sg", szBuffer);
517
8.83k
    }
518
519
456k
    if (osPattern != "")
520
0
    {
521
0
        osStyle += ",p:\"";
522
0
        osStyle += osPattern;
523
0
        osStyle += "\"";
524
0
    }
525
526
456k
    osStyle += ")";
527
528
456k
    poFeature->SetStyleString(osStyle);
529
456k
}
530
531
/************************************************************************/
532
/*                             TextRecode()                             */
533
/************************************************************************/
534
535
CPLString OGRDXFLayer::TextRecode(const char *pszInput)
536
537
336k
{
538
336k
    return CPLString(pszInput).Recode(poDS->GetEncoding(), CPL_ENC_UTF8);
539
336k
}
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
336k
{
551
336k
    if (poDS->ShouldTranslateEscapes())
552
336k
        return ACTextUnescape(pszInput, poDS->GetEncoding(), bIsMText);
553
554
0
    return TextRecode(pszInput);
555
336k
}
556
557
/************************************************************************/
558
/*                           TranslateMTEXT()                           */
559
/************************************************************************/
560
561
OGRDXFFeature *OGRDXFLayer::TranslateMTEXT()
562
563
51.3k
{
564
51.3k
    char szLineBuf[512];
565
51.3k
    int nCode = 0;
566
51.3k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
567
51.3k
    double dfX = 0.0;
568
51.3k
    double dfY = 0.0;
569
51.3k
    double dfZ = 0.0;
570
51.3k
    double dfAngle = 0.0;
571
51.3k
    double dfHeight = 0.0;
572
51.3k
    double dfXDirection = 0.0;
573
51.3k
    double dfYDirection = 0.0;
574
51.3k
    bool bHaveZ = false;
575
51.3k
    int nAttachmentPoint = -1;
576
51.3k
    CPLString osText;
577
51.3k
    CPLString osStyleName = "STANDARD";
578
579
280k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
580
228k
    {
581
228k
        switch (nCode)
582
228k
        {
583
10.6k
            case 10:
584
10.6k
                dfX = CPLAtof(szLineBuf);
585
10.6k
                break;
586
587
9.53k
            case 20:
588
9.53k
                dfY = CPLAtof(szLineBuf);
589
9.53k
                break;
590
591
9.85k
            case 30:
592
9.85k
                dfZ = CPLAtof(szLineBuf);
593
9.85k
                bHaveZ = true;
594
9.85k
                break;
595
596
8.60k
            case 40:
597
8.60k
                dfHeight = CPLAtof(szLineBuf);
598
8.60k
                break;
599
600
13.2k
            case 71:
601
13.2k
                nAttachmentPoint = atoi(szLineBuf);
602
13.2k
                break;
603
604
2.24k
            case 11:
605
2.24k
                dfXDirection = CPLAtof(szLineBuf);
606
2.24k
                break;
607
608
11.0k
            case 21:
609
11.0k
                dfYDirection = CPLAtof(szLineBuf);
610
11.0k
                dfAngle = atan2(dfYDirection, dfXDirection) * 180.0 / M_PI;
611
11.0k
                break;
612
613
51.9k
            case 1:
614
77.4k
            case 3:
615
77.4k
                osText += TextUnescape(szLineBuf, true);
616
77.4k
                break;
617
618
1.31k
            case 50:
619
1.31k
                dfAngle = CPLAtof(szLineBuf);
620
1.31k
                break;
621
622
5.45k
            case 7:
623
5.45k
                osStyleName = TextRecode(szLineBuf);
624
5.45k
                break;
625
626
79.6k
            default:
627
79.6k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
628
79.6k
                break;
629
228k
        }
630
228k
    }
631
51.3k
    if (nCode < 0)
632
1.83k
    {
633
1.83k
        DXF_LAYER_READER_ERROR();
634
1.83k
        return nullptr;
635
1.83k
    }
636
637
49.5k
    poDS->UnreadValue();
638
639
49.5k
    OGRPoint *poGeom = nullptr;
640
49.5k
    if (bHaveZ)
641
2.71k
        poGeom = new OGRPoint(dfX, dfY, dfZ);
642
46.8k
    else
643
46.8k
        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
49.5k
    poFeature->SetGeometryDirectly(poGeom);
650
651
    /* -------------------------------------------------------------------- */
652
    /*      Apply text after stripping off any extra terminating newline.   */
653
    /* -------------------------------------------------------------------- */
654
49.5k
    if (!osText.empty() && osText.back() == '\n')
655
1.45k
        osText.pop_back();
656
657
49.5k
    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
49.5k
    if (strchr(osText, '"') != nullptr)
664
12.4k
    {
665
12.4k
        std::string osEscaped;
666
667
2.75M
        for (size_t iC = 0; iC < osText.size(); iC++)
668
2.73M
        {
669
2.73M
            if (osText[iC] == '"')
670
48.3k
                osEscaped += "\\\"";
671
2.68M
            else
672
2.68M
                osEscaped += osText[iC];
673
2.73M
        }
674
12.4k
        osText = std::move(osEscaped);
675
12.4k
    }
676
677
    /* -------------------------------------------------------------------- */
678
    /*      Prepare style string.                                           */
679
    /* -------------------------------------------------------------------- */
680
49.5k
    CPLString osStyle;
681
49.5k
    char szBuffer[64];
682
683
    // Font name
684
49.5k
    osStyle.Printf("LABEL(f:\"");
685
686
    // Preserve legacy behavior of specifying "Arial" as a default font name.
687
49.5k
    osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
688
689
49.5k
    osStyle += "\"";
690
691
    // Bold, italic
692
49.5k
    if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
693
0
    {
694
0
        osStyle += ",bo:1";
695
0
    }
696
49.5k
    if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
697
0
    {
698
0
        osStyle += ",it:1";
699
0
    }
700
701
    // Text string itself
702
49.5k
    osStyle += ",t:\"";
703
49.5k
    osStyle += osText;
704
49.5k
    osStyle += "\"";
705
706
49.5k
    if (dfAngle != 0.0)
707
4.75k
    {
708
4.75k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
709
4.75k
        osStyle += CPLString().Printf(",a:%s", szBuffer);
710
4.75k
    }
711
712
49.5k
    if (dfHeight != 0.0)
713
1.29k
    {
714
1.29k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
715
1.29k
        osStyle += CPLString().Printf(",s:%sg", szBuffer);
716
1.29k
    }
717
718
49.5k
    const char *pszWidthFactor =
719
49.5k
        poDS->LookupTextStyleProperty(osStyleName, "Width", "1");
720
49.5k
    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
49.5k
    if (nAttachmentPoint >= 0 && nAttachmentPoint <= 9)
728
3.71k
    {
729
3.71k
        const static int anAttachmentMap[10] = {-1, 7, 8, 9, 4, 5, 6, 1, 2, 3};
730
731
3.71k
        osStyle +=
732
3.71k
            CPLString().Printf(",p:%d", anAttachmentMap[nAttachmentPoint]);
733
3.71k
    }
734
735
    // Color
736
49.5k
    osStyle += ",c:";
737
49.5k
    osStyle += poFeature->GetColor(poDS);
738
739
49.5k
    osStyle += ")";
740
741
49.5k
    poFeature->SetStyleString(osStyle);
742
743
49.5k
    return poFeature.release();
744
51.3k
}
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
93.2k
{
756
93.2k
    char szLineBuf[257];
757
93.2k
    int nCode = 0;
758
93.2k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
759
760
93.2k
    double dfX = 0.0;
761
93.2k
    double dfY = 0.0;
762
93.2k
    double dfZ = 0.0;
763
93.2k
    bool bHaveZ = false;
764
765
93.2k
    double dfAngle = 0.0;
766
93.2k
    double dfHeight = 0.0;
767
93.2k
    double dfWidthFactor = 1.0;
768
93.2k
    bool bHasAlignmentPoint = false;
769
93.2k
    double dfAlignmentPointX = 0.0;
770
93.2k
    double dfAlignmentPointY = 0.0;
771
772
93.2k
    CPLString osText;
773
93.2k
    CPLString osStyleName = "STANDARD";
774
775
93.2k
    int nAnchorPosition = 1;
776
93.2k
    int nHorizontalAlignment = 0;
777
93.2k
    int nVerticalAlignment = 0;
778
779
800k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
780
706k
    {
781
706k
        switch (nCode)
782
706k
        {
783
42.2k
            case 10:
784
42.2k
                dfX = CPLAtof(szLineBuf);
785
42.2k
                break;
786
787
33.6k
            case 20:
788
33.6k
                dfY = CPLAtof(szLineBuf);
789
33.6k
                break;
790
791
7.89k
            case 11:
792
7.89k
                dfAlignmentPointX = CPLAtof(szLineBuf);
793
7.89k
                break;
794
795
23.1k
            case 21:
796
23.1k
                dfAlignmentPointY = CPLAtof(szLineBuf);
797
23.1k
                bHasAlignmentPoint = true;
798
23.1k
                break;
799
800
31.1k
            case 30:
801
31.1k
                dfZ = CPLAtof(szLineBuf);
802
31.1k
                bHaveZ = true;
803
31.1k
                break;
804
805
17.5k
            case 40:
806
17.5k
                dfHeight = CPLAtof(szLineBuf);
807
17.5k
                break;
808
809
7.98k
            case 41:
810
7.98k
                dfWidthFactor = CPLAtof(szLineBuf);
811
7.98k
                break;
812
813
70.2k
            case 1:
814
70.2k
                osText += TextUnescape(szLineBuf, false);
815
70.2k
                break;
816
817
11.3k
            case 50:
818
11.3k
                dfAngle = CPLAtof(szLineBuf);
819
11.3k
                break;
820
821
18.5k
            case 72:
822
18.5k
                nHorizontalAlignment = atoi(szLineBuf);
823
18.5k
                break;
824
825
24.5k
            case 73:
826
24.5k
                if (!bIsAttribOrAttdef)
827
8.83k
                    nVerticalAlignment = atoi(szLineBuf);
828
24.5k
                break;
829
830
20.8k
            case 74:
831
20.8k
                if (bIsAttribOrAttdef)
832
18.6k
                    nVerticalAlignment = atoi(szLineBuf);
833
20.8k
                break;
834
835
60.0k
            case 7:
836
60.0k
                osStyleName = TextRecode(szLineBuf);
837
60.0k
                break;
838
839
            // 2 and 70 are for ATTRIB and ATTDEF entities only
840
60.5k
            case 2:
841
60.5k
                if (bIsAttribOrAttdef)
842
38.8k
                {
843
                    // Attribute tags are not supposed to contain spaces (but
844
                    // sometimes they do)
845
55.3k
                    while (char *pchSpace = strchr(szLineBuf, ' '))
846
16.4k
                        *pchSpace = '_';
847
848
38.8k
                    poFeature->osAttributeTag = szLineBuf;
849
38.8k
                }
850
60.5k
                break;
851
852
21.3k
            case 70:
853
21.3k
                if (bIsAttribOrAttdef)
854
13.8k
                {
855
                    // When the LSB is set, this ATTRIB is "invisible"
856
13.8k
                    if (atoi(szLineBuf) & 1)
857
4.82k
                        poFeature->oStyleProperties["Hidden"] = "1";
858
                    // If the next bit is set, this ATTDEF is to be preserved
859
                    // and treated as constant TEXT
860
9.05k
                    else if (atoi(szLineBuf) & 2)
861
2.85k
                        poFeature->osAttributeTag.Clear();
862
13.8k
                }
863
21.3k
                break;
864
865
255k
            default:
866
255k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
867
255k
                break;
868
706k
        }
869
706k
    }
870
93.2k
    if (nCode < 0)
871
2.69k
    {
872
2.69k
        DXF_LAYER_READER_ERROR();
873
2.69k
        return nullptr;
874
2.69k
    }
875
876
90.5k
    poDS->UnreadValue();
877
878
90.5k
    OGRPoint *poGeom = nullptr;
879
90.5k
    if (bHaveZ)
880
19.9k
        poGeom = new OGRPoint(dfX, dfY, dfZ);
881
70.6k
    else
882
70.6k
        poGeom = new OGRPoint(dfX, dfY);
883
90.5k
    poFeature->ApplyOCSTransformer(poGeom);
884
90.5k
    poFeature->SetGeometryDirectly(poGeom);
885
886
    /* -------------------------------------------------------------------- */
887
    /*      Determine anchor position.                                      */
888
    /* -------------------------------------------------------------------- */
889
90.5k
    if (nHorizontalAlignment > 0 || nVerticalAlignment > 0)
890
17.9k
    {
891
17.9k
        switch (nVerticalAlignment)
892
17.9k
        {
893
688
            case 1:  // bottom
894
688
                nAnchorPosition = 10;
895
688
                break;
896
897
4.14k
            case 2:  // middle
898
4.14k
                nAnchorPosition = 4;
899
4.14k
                break;
900
901
537
            case 3:  // top
902
537
                nAnchorPosition = 7;
903
537
                break;
904
905
12.5k
            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.5k
                if (nHorizontalAlignment == 4)
911
616
                    nAnchorPosition = 5;
912
12.5k
                break;
913
17.9k
        }
914
17.9k
        if (nHorizontalAlignment < 3)
915
14.1k
            nAnchorPosition += nHorizontalAlignment;
916
        // TODO other alignment options
917
17.9k
    }
918
919
90.5k
    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
90.5k
    if (strchr(osText, '"') != nullptr)
926
18.9k
    {
927
18.9k
        CPLString osEscaped;
928
929
773k
        for (size_t iC = 0; iC < osText.size(); iC++)
930
754k
        {
931
754k
            if (osText[iC] == '"')
932
129k
                osEscaped += "\\\"";
933
625k
            else
934
625k
                osEscaped += osText[iC];
935
754k
        }
936
18.9k
        osText = std::move(osEscaped);
937
18.9k
    }
938
939
    /* -------------------------------------------------------------------- */
940
    /*      Prepare style string.                                           */
941
    /* -------------------------------------------------------------------- */
942
90.5k
    CPLString osStyle;
943
90.5k
    char szBuffer[64];
944
945
    // Font name
946
90.5k
    osStyle.Printf("LABEL(f:\"");
947
948
    // Preserve legacy behavior of specifying "Arial" as a default font name.
949
90.5k
    osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial");
950
951
90.5k
    osStyle += "\"";
952
953
    // Bold, italic
954
90.5k
    if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1"))
955
0
    {
956
0
        osStyle += ",bo:1";
957
0
    }
958
90.5k
    if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1"))
959
0
    {
960
0
        osStyle += ",it:1";
961
0
    }
962
963
    // Text string itself
964
90.5k
    osStyle += ",t:\"";
965
90.5k
    osStyle += osText;
966
90.5k
    osStyle += "\"";
967
968
    // Other attributes
969
90.5k
    osStyle += CPLString().Printf(",p:%d", nAnchorPosition);
970
971
90.5k
    if (dfAngle != 0.0)
972
6.07k
    {
973
6.07k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
974
6.07k
        osStyle += CPLString().Printf(",a:%s", szBuffer);
975
6.07k
    }
976
977
90.5k
    if (dfHeight != 0.0)
978
5.55k
    {
979
5.55k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
980
5.55k
        osStyle += CPLString().Printf(",s:%sg", szBuffer);
981
5.55k
    }
982
983
90.5k
    if (dfWidthFactor != 1.0)
984
5.62k
    {
985
5.62k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g", dfWidthFactor * 100.0);
986
5.62k
        osStyle += CPLString().Printf(",w:%s", szBuffer);
987
5.62k
    }
988
989
90.5k
    if (bHasAlignmentPoint && dfAlignmentPointX != dfX)
990
2.99k
    {
991
2.99k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
992
2.99k
                    dfAlignmentPointX - dfX);
993
2.99k
        osStyle += CPLString().Printf(",dx:%sg", szBuffer);
994
2.99k
    }
995
996
90.5k
    if (bHasAlignmentPoint && dfAlignmentPointY != dfY)
997
10.8k
    {
998
10.8k
        CPLsnprintf(szBuffer, sizeof(szBuffer), "%.6g",
999
10.8k
                    dfAlignmentPointY - dfY);
1000
10.8k
        osStyle += CPLString().Printf(",dy:%sg", szBuffer);
1001
10.8k
    }
1002
1003
    // Color
1004
90.5k
    osStyle += ",c:";
1005
90.5k
    osStyle += poFeature->GetColor(poDS);
1006
1007
90.5k
    osStyle += ")";
1008
1009
90.5k
    poFeature->SetStyleString(osStyle);
1010
1011
90.5k
    return poFeature.release();
1012
90.5k
}
1013
1014
/************************************************************************/
1015
/*                           TranslatePOINT()                           */
1016
/************************************************************************/
1017
1018
OGRDXFFeature *OGRDXFLayer::TranslatePOINT()
1019
1020
11.3k
{
1021
11.3k
    char szLineBuf[257];
1022
11.3k
    int nCode = 0;
1023
11.3k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1024
11.3k
    double dfX = 0.0;
1025
11.3k
    double dfY = 0.0;
1026
11.3k
    double dfZ = 0.0;
1027
11.3k
    bool bHaveZ = false;
1028
1029
87.7k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1030
76.4k
    {
1031
76.4k
        switch (nCode)
1032
76.4k
        {
1033
7.36k
            case 10:
1034
7.36k
                dfX = CPLAtof(szLineBuf);
1035
7.36k
                break;
1036
1037
6.82k
            case 20:
1038
6.82k
                dfY = CPLAtof(szLineBuf);
1039
6.82k
                break;
1040
1041
4.72k
            case 30:
1042
4.72k
                dfZ = CPLAtof(szLineBuf);
1043
4.72k
                bHaveZ = true;
1044
4.72k
                break;
1045
1046
57.5k
            default:
1047
57.5k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1048
57.5k
                break;
1049
76.4k
        }
1050
76.4k
    }
1051
11.3k
    if (nCode < 0)
1052
340
    {
1053
340
        DXF_LAYER_READER_ERROR();
1054
340
        return nullptr;
1055
340
    }
1056
1057
10.9k
    poDS->UnreadValue();
1058
1059
10.9k
    OGRPoint *poGeom = nullptr;
1060
10.9k
    if (bHaveZ)
1061
3.49k
        poGeom = new OGRPoint(dfX, dfY, dfZ);
1062
7.47k
    else
1063
7.47k
        poGeom = new OGRPoint(dfX, dfY);
1064
1065
10.9k
    poFeature->SetGeometryDirectly(poGeom);
1066
1067
    // Set style pen color
1068
10.9k
    PrepareLineStyle(poFeature.get());
1069
1070
10.9k
    return poFeature.release();
1071
11.3k
}
1072
1073
/************************************************************************/
1074
/*                           TranslateLINE()                            */
1075
/************************************************************************/
1076
1077
OGRDXFFeature *OGRDXFLayer::TranslateLINE()
1078
1079
13.5k
{
1080
13.5k
    char szLineBuf[257];
1081
13.5k
    int nCode = 0;
1082
13.5k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1083
13.5k
    double dfX1 = 0.0;
1084
13.5k
    double dfY1 = 0.0;
1085
13.5k
    double dfZ1 = 0.0;
1086
13.5k
    double dfX2 = 0.0;
1087
13.5k
    double dfY2 = 0.0;
1088
13.5k
    double dfZ2 = 0.0;
1089
13.5k
    bool bHaveZ = false;
1090
1091
    /* -------------------------------------------------------------------- */
1092
    /*      Process values.                                                 */
1093
    /* -------------------------------------------------------------------- */
1094
166k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1095
152k
    {
1096
152k
        switch (nCode)
1097
152k
        {
1098
6.50k
            case 10:
1099
6.50k
                dfX1 = CPLAtof(szLineBuf);
1100
6.50k
                break;
1101
1102
6.73k
            case 11:
1103
6.73k
                dfX2 = CPLAtof(szLineBuf);
1104
6.73k
                break;
1105
1106
20.9k
            case 20:
1107
20.9k
                dfY1 = CPLAtof(szLineBuf);
1108
20.9k
                break;
1109
1110
4.72k
            case 21:
1111
4.72k
                dfY2 = CPLAtof(szLineBuf);
1112
4.72k
                break;
1113
1114
9.31k
            case 30:
1115
9.31k
                dfZ1 = CPLAtof(szLineBuf);
1116
9.31k
                bHaveZ = true;
1117
9.31k
                break;
1118
1119
3.91k
            case 31:
1120
3.91k
                dfZ2 = CPLAtof(szLineBuf);
1121
3.91k
                bHaveZ = true;
1122
3.91k
                break;
1123
1124
100k
            default:
1125
100k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1126
100k
                break;
1127
152k
        }
1128
152k
    }
1129
13.5k
    if (nCode < 0)
1130
2.46k
    {
1131
2.46k
        DXF_LAYER_READER_ERROR();
1132
2.46k
        return nullptr;
1133
2.46k
    }
1134
1135
11.0k
    poDS->UnreadValue();
1136
1137
    /* -------------------------------------------------------------------- */
1138
    /*      Create geometry                                                 */
1139
    /* -------------------------------------------------------------------- */
1140
11.0k
    auto poLS = std::make_unique<OGRLineString>();
1141
11.0k
    if (bHaveZ)
1142
5.99k
    {
1143
5.99k
        poLS->addPoint(dfX1, dfY1, dfZ1);
1144
5.99k
        poLS->addPoint(dfX2, dfY2, dfZ2);
1145
5.99k
    }
1146
5.03k
    else
1147
5.03k
    {
1148
5.03k
        poLS->addPoint(dfX1, dfY1);
1149
5.03k
        poLS->addPoint(dfX2, dfY2);
1150
5.03k
    }
1151
1152
11.0k
    poFeature->SetGeometryDirectly(poLS.release());
1153
1154
11.0k
    PrepareLineStyle(poFeature.get());
1155
1156
11.0k
    return poFeature.release();
1157
13.5k
}
1158
1159
/************************************************************************/
1160
/*                        TranslateLWPOLYLINE()                         */
1161
/************************************************************************/
1162
OGRDXFFeature *OGRDXFLayer::TranslateLWPOLYLINE()
1163
1164
30.6k
{
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
30.6k
    char szLineBuf[257];
1170
30.6k
    int nCode = 0;
1171
30.6k
    int nPolylineFlag = 0;
1172
1173
30.6k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1174
30.6k
    double dfX = 0.0;
1175
30.6k
    double dfY = 0.0;
1176
30.6k
    double dfZ = 0.0;
1177
30.6k
    bool bHaveX = false;
1178
30.6k
    bool bHaveY = false;
1179
1180
30.6k
    int nNumVertices = 1;  // use 1 based index
1181
30.6k
    int npolyarcVertexCount = 1;
1182
30.6k
    double dfBulge = 0.0;
1183
30.6k
    DXFSmoothPolyline smoothPolyline;
1184
1185
30.6k
    smoothPolyline.setCoordinateDimension(2);
1186
1187
    /* -------------------------------------------------------------------- */
1188
    /*      Collect information from the LWPOLYLINE object itself.          */
1189
    /* -------------------------------------------------------------------- */
1190
388k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1191
359k
    {
1192
359k
        if (npolyarcVertexCount > nNumVertices)
1193
1.80k
        {
1194
1.80k
            CPLError(CE_Failure, CPLE_AppDefined,
1195
1.80k
                     "Too many vertices found in LWPOLYLINE.");
1196
1.80k
            return nullptr;
1197
1.80k
        }
1198
1199
358k
        switch (nCode)
1200
358k
        {
1201
9.85k
            case 38:
1202
                // Constant elevation.
1203
9.85k
                dfZ = CPLAtof(szLineBuf);
1204
9.85k
                smoothPolyline.setCoordinateDimension(3);
1205
9.85k
                break;
1206
1207
16.9k
            case 90:
1208
16.9k
                nNumVertices = atoi(szLineBuf);
1209
16.9k
                break;
1210
1211
8.60k
            case 70:
1212
8.60k
                nPolylineFlag = atoi(szLineBuf);
1213
8.60k
                break;
1214
1215
59.9k
            case 10:
1216
59.9k
                if (bHaveX && bHaveY)
1217
16.0k
                {
1218
16.0k
                    smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1219
16.0k
                    npolyarcVertexCount++;
1220
16.0k
                    dfBulge = 0.0;
1221
16.0k
                    bHaveY = false;
1222
16.0k
                }
1223
59.9k
                dfX = CPLAtof(szLineBuf);
1224
59.9k
                bHaveX = true;
1225
59.9k
                break;
1226
1227
76.4k
            case 20:
1228
76.4k
                if (bHaveX && bHaveY)
1229
11.5k
                {
1230
11.5k
                    smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1231
11.5k
                    npolyarcVertexCount++;
1232
11.5k
                    dfBulge = 0.0;
1233
11.5k
                    bHaveX = false;
1234
11.5k
                }
1235
76.4k
                dfY = CPLAtof(szLineBuf);
1236
76.4k
                bHaveY = true;
1237
76.4k
                break;
1238
1239
11.7k
            case 42:
1240
11.7k
                dfBulge = CPLAtof(szLineBuf);
1241
11.7k
                break;
1242
1243
174k
            default:
1244
174k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1245
174k
                break;
1246
358k
        }
1247
358k
    }
1248
28.8k
    if (nCode < 0)
1249
967
    {
1250
967
        DXF_LAYER_READER_ERROR();
1251
967
        return nullptr;
1252
967
    }
1253
1254
27.8k
    poDS->UnreadValue();
1255
1256
27.8k
    if (bHaveX && bHaveY)
1257
11.0k
        smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1258
1259
27.8k
    if (smoothPolyline.IsEmpty())
1260
11.3k
    {
1261
11.3k
        return nullptr;
1262
11.3k
    }
1263
1264
    /* -------------------------------------------------------------------- */
1265
    /*      Close polyline if necessary.                                    */
1266
    /* -------------------------------------------------------------------- */
1267
16.4k
    const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
1268
16.4k
    if (bIsClosed)
1269
4.22k
        smoothPolyline.Close();
1270
1271
16.4k
    const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
1272
1273
16.4k
    smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
1274
16.4k
    auto poGeom =
1275
16.4k
        std::unique_ptr<OGRGeometry>(smoothPolyline.Tessellate(bAsPolygon));
1276
16.4k
    poFeature->ApplyOCSTransformer(poGeom.get());
1277
16.4k
    poFeature->SetGeometryDirectly(poGeom.release());
1278
1279
16.4k
    PrepareLineStyle(poFeature.get());
1280
1281
16.4k
    return poFeature.release();
1282
27.8k
}
1283
1284
/************************************************************************/
1285
/*                              SafeAbs()                               */
1286
/************************************************************************/
1287
1288
static inline int SafeAbs(int x)
1289
23.9k
{
1290
23.9k
    if (x == std::numeric_limits<int>::min())
1291
0
        return std::numeric_limits<int>::max();
1292
23.9k
    return abs(x);
1293
23.9k
}
1294
1295
/************************************************************************/
1296
/*                         TranslatePOLYLINE()                          */
1297
/*                                                                      */
1298
/*      We also capture the following vertices.                         */
1299
/************************************************************************/
1300
1301
OGRDXFFeature *OGRDXFLayer::TranslatePOLYLINE()
1302
1303
11.8k
{
1304
11.8k
    char szLineBuf[257];
1305
11.8k
    int nCode = 0;
1306
11.8k
    int nPolylineFlag = 0;
1307
11.8k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1308
1309
    /* -------------------------------------------------------------------- */
1310
    /*      Collect information from the POLYLINE object itself.            */
1311
    /* -------------------------------------------------------------------- */
1312
98.7k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1313
86.8k
    {
1314
86.8k
        switch (nCode)
1315
86.8k
        {
1316
7.59k
            case 70:
1317
7.59k
                nPolylineFlag = atoi(szLineBuf);
1318
7.59k
                break;
1319
1320
79.2k
            default:
1321
79.2k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1322
79.2k
                break;
1323
86.8k
        }
1324
86.8k
    }
1325
11.8k
    if (nCode < 0)
1326
576
    {
1327
576
        DXF_LAYER_READER_ERROR();
1328
576
        return nullptr;
1329
576
    }
1330
1331
11.3k
    if ((nPolylineFlag & 16) != 0)
1332
95
    {
1333
95
        CPLDebug("DXF", "Polygon mesh not supported.");
1334
95
        return nullptr;
1335
95
    }
1336
1337
    /* -------------------------------------------------------------------- */
1338
    /*      Collect vertices as a smooth polyline.                          */
1339
    /* -------------------------------------------------------------------- */
1340
11.2k
    double dfX = 0.0;
1341
11.2k
    double dfY = 0.0;
1342
11.2k
    double dfZ = 0.0;
1343
11.2k
    double dfBulge = 0.0;
1344
11.2k
    int nVertexFlag = 0;
1345
11.2k
    DXFSmoothPolyline smoothPolyline;
1346
11.2k
    unsigned int vertexIndex71 = 0;
1347
11.2k
    unsigned int vertexIndex72 = 0;
1348
11.2k
    unsigned int vertexIndex73 = 0;
1349
11.2k
    unsigned int vertexIndex74 = 0;
1350
11.2k
    std::vector<OGRPoint> aoPoints;
1351
11.2k
    auto poPS = std::make_unique<OGRPolyhedralSurface>();
1352
1353
11.2k
    smoothPolyline.setCoordinateDimension(2);
1354
1355
1.04M
    while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
1356
1.03M
    {
1357
        // Eat non-vertex objects.
1358
1.03M
        if (!EQUAL(szLineBuf, "VERTEX"))
1359
964k
        {
1360
1.79M
            while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1361
826k
            {
1362
826k
            }
1363
964k
            if (nCode < 0)
1364
2.04k
            {
1365
2.04k
                DXF_LAYER_READER_ERROR();
1366
2.04k
                return nullptr;
1367
2.04k
            }
1368
1369
962k
            continue;
1370
964k
        }
1371
1372
        // process a Vertex
1373
386k
        while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1374
317k
        {
1375
317k
            switch (nCode)
1376
317k
            {
1377
9.51k
                case 10:
1378
9.51k
                    dfX = CPLAtof(szLineBuf);
1379
9.51k
                    break;
1380
1381
72.5k
                case 20:
1382
72.5k
                    dfY = CPLAtof(szLineBuf);
1383
72.5k
                    break;
1384
1385
12.5k
                case 30:
1386
12.5k
                    dfZ = CPLAtof(szLineBuf);
1387
12.5k
                    smoothPolyline.setCoordinateDimension(3);
1388
12.5k
                    break;
1389
1390
13.0k
                case 42:
1391
13.0k
                    dfBulge = CPLAtof(szLineBuf);
1392
13.0k
                    break;
1393
1394
27.5k
                case 70:
1395
27.5k
                    nVertexFlag = atoi(szLineBuf);
1396
27.5k
                    break;
1397
1398
8.44k
                case 71:
1399
                    // See comment below about negative values for 71, 72, 73,
1400
                    // 74
1401
8.44k
                    vertexIndex71 = SafeAbs(atoi(szLineBuf));
1402
8.44k
                    break;
1403
1404
7.41k
                case 72:
1405
7.41k
                    vertexIndex72 = SafeAbs(atoi(szLineBuf));
1406
7.41k
                    break;
1407
1408
3.23k
                case 73:
1409
3.23k
                    vertexIndex73 = SafeAbs(atoi(szLineBuf));
1410
3.23k
                    break;
1411
1412
4.87k
                case 74:
1413
4.87k
                    vertexIndex74 = SafeAbs(atoi(szLineBuf));
1414
4.87k
                    break;
1415
1416
158k
                default:
1417
158k
                    break;
1418
317k
            }
1419
317k
        }
1420
1421
68.6k
        if (((nVertexFlag & 64) != 0) && ((nVertexFlag & 128) != 0))
1422
17.7k
        {
1423
            // add the point to the list of points
1424
17.7k
            try
1425
17.7k
            {
1426
17.7k
                aoPoints.emplace_back(dfX, dfY, dfZ);
1427
17.7k
            }
1428
17.7k
            catch (const std::exception &e)
1429
17.7k
            {
1430
0
                CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1431
0
                return nullptr;
1432
0
            }
1433
17.7k
        }
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
68.6k
        if (nVertexFlag == 128)
1443
12.4k
        {
1444
            // create a polygon and add it to the Polyhedral Surface
1445
12.4k
            auto poLR = std::make_unique<OGRLinearRing>();
1446
12.4k
            int iPoint = 0;
1447
12.4k
            int startPoint = -1;
1448
12.4k
            poLR->set3D(TRUE);
1449
12.4k
            if (vertexIndex71 != 0 && vertexIndex71 <= aoPoints.size())
1450
2.83k
            {
1451
                // if (startPoint == -1)
1452
2.83k
                startPoint = vertexIndex71 - 1;
1453
2.83k
                poLR->setPoint(iPoint, &aoPoints[vertexIndex71 - 1]);
1454
2.83k
                iPoint++;
1455
2.83k
                vertexIndex71 = 0;
1456
2.83k
            }
1457
12.4k
            if (vertexIndex72 != 0 && vertexIndex72 <= aoPoints.size())
1458
1.12k
            {
1459
1.12k
                if (startPoint == -1)
1460
895
                    startPoint = vertexIndex72 - 1;
1461
1.12k
                poLR->setPoint(iPoint, &aoPoints[vertexIndex72 - 1]);
1462
1.12k
                iPoint++;
1463
1.12k
                vertexIndex72 = 0;
1464
1.12k
            }
1465
12.4k
            if (vertexIndex73 != 0 && vertexIndex73 <= aoPoints.size())
1466
1.01k
            {
1467
1.01k
                if (startPoint == -1)
1468
678
                    startPoint = vertexIndex73 - 1;
1469
1.01k
                poLR->setPoint(iPoint, &aoPoints[vertexIndex73 - 1]);
1470
1.01k
                iPoint++;
1471
1.01k
                vertexIndex73 = 0;
1472
1.01k
            }
1473
12.4k
            if (vertexIndex74 != 0 && vertexIndex74 <= aoPoints.size())
1474
813
            {
1475
813
                if (startPoint == -1)
1476
506
                    startPoint = vertexIndex74 - 1;
1477
813
                poLR->setPoint(iPoint, &aoPoints[vertexIndex74 - 1]);
1478
813
                iPoint++;
1479
813
                vertexIndex74 = 0;
1480
813
            }
1481
12.4k
            if (startPoint >= 0)
1482
4.91k
            {
1483
                // complete the ring
1484
4.91k
                poLR->setPoint(iPoint, &aoPoints[startPoint]);
1485
1486
4.91k
                OGRPolygon *poPolygon = new OGRPolygon();
1487
4.91k
                poPolygon->addRingDirectly(poLR.release());
1488
1489
4.91k
                poPS->addGeometryDirectly(poPolygon);
1490
4.91k
            }
1491
12.4k
        }
1492
1493
68.6k
        if (nCode < 0)
1494
553
        {
1495
553
            DXF_LAYER_READER_ERROR();
1496
553
            return nullptr;
1497
553
        }
1498
1499
        // Ignore Spline frame control points ( see #4683 )
1500
68.0k
        if ((nVertexFlag & 16) == 0)
1501
64.3k
            smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge);
1502
68.0k
        dfBulge = 0.0;
1503
68.0k
    }
1504
1505
8.62k
    if (smoothPolyline.IsEmpty())
1506
1.28k
    {
1507
1.28k
        return nullptr;
1508
1.28k
    }
1509
1510
7.34k
    if (poPS->getNumGeometries() > 0)
1511
291
    {
1512
291
        poFeature->SetGeometryDirectly(poPS.release());
1513
291
        PrepareBrushStyle(poFeature.get());
1514
291
        return poFeature.release();
1515
291
    }
1516
1517
    /* -------------------------------------------------------------------- */
1518
    /*      Close polyline if necessary.                                    */
1519
    /* -------------------------------------------------------------------- */
1520
7.05k
    const bool bIsClosed = (nPolylineFlag & 0x01) != 0;
1521
7.05k
    if (bIsClosed)
1522
4.09k
        smoothPolyline.Close();
1523
1524
7.05k
    const bool bAsPolygon = bIsClosed && poDS->ClosedLineAsPolygon();
1525
1526
7.05k
    smoothPolyline.SetUseMaxGapWhenTessellatingArcs(poDS->InlineBlocks());
1527
7.05k
    OGRGeometry *poGeom = smoothPolyline.Tessellate(bAsPolygon);
1528
1529
7.05k
    if ((nPolylineFlag & 8) == 0)
1530
4.27k
        poFeature->ApplyOCSTransformer(poGeom);
1531
7.05k
    poFeature->SetGeometryDirectly(poGeom);
1532
1533
7.05k
    PrepareLineStyle(poFeature.get());
1534
1535
7.05k
    return poFeature.release();
1536
7.34k
}
1537
1538
/************************************************************************/
1539
/*                           TranslateMLINE()                           */
1540
/************************************************************************/
1541
1542
OGRDXFFeature *OGRDXFLayer::TranslateMLINE()
1543
1544
39.0k
{
1545
39.0k
    char szLineBuf[257];
1546
39.0k
    int nCode = 0;
1547
1548
39.0k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1549
1550
39.0k
    bool bIsClosed = false;
1551
39.0k
    int nNumVertices = 0;
1552
39.0k
    int nNumElements = 0;
1553
1554
122k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0 &&
1555
98.0k
           nCode != 11)
1556
89.5k
    {
1557
89.5k
        switch (nCode)
1558
89.5k
        {
1559
21.5k
            case 71:
1560
21.5k
                bIsClosed = (atoi(szLineBuf) & 2) == 2;
1561
21.5k
                break;
1562
1563
9.08k
            case 72:
1564
9.08k
                nNumVertices = atoi(szLineBuf);
1565
9.08k
                break;
1566
1567
16.1k
            case 73:
1568
16.1k
                nNumElements = atoi(szLineBuf);
1569
                // No-one should ever need more than 1000 elements!
1570
16.1k
                if (nNumElements <= 0 || nNumElements > 1000)
1571
6.34k
                {
1572
6.34k
                    CPLDebug("DXF", "Invalid number of MLINE elements (73): %s",
1573
6.34k
                             szLineBuf);
1574
6.34k
                    DXF_LAYER_READER_ERROR();
1575
6.34k
                    return nullptr;
1576
6.34k
                }
1577
9.80k
                break;
1578
1579
42.8k
            default:
1580
42.8k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1581
42.8k
                break;
1582
89.5k
        }
1583
89.5k
    }
1584
32.7k
    if (nCode < 0)
1585
662
    {
1586
662
        DXF_LAYER_READER_ERROR();
1587
662
        return nullptr;
1588
662
    }
1589
1590
32.0k
    if (nCode == 0 || nCode == 11)
1591
32.0k
        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
32.0k
    auto poMLS = std::make_unique<OGRMultiLineString>();
1599
32.0k
    std::vector<std::unique_ptr<OGRLineString>> apoCurrentLines(nNumElements);
1600
1601
    // For use when bIsClosed is true
1602
32.0k
    std::vector<DXFTriple> aoInitialVertices(nNumElements);
1603
1604
32.0k
#define EXPECT_CODE(code)                                                      \
1605
32.0k
    if (poDS->ReadValue(szLineBuf, sizeof(szLineBuf)) != (code))               \
1606
13.2k
    {                                                                          \
1607
6.88k
        DXF_LAYER_READER_ERROR();                                              \
1608
6.88k
        return nullptr;                                                        \
1609
6.88k
    }
1610
1611
32.0k
    for (int iVertex = 0; iVertex < nNumVertices; iVertex++)
1612
6.88k
    {
1613
6.88k
        EXPECT_CODE(11);
1614
5.52k
        const double dfVertexX = CPLAtof(szLineBuf);
1615
5.52k
        EXPECT_CODE(21);
1616
807
        const double dfVertexY = CPLAtof(szLineBuf);
1617
807
        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
25.2k
#undef EXPECT_CODE
1717
1718
    // Close the MLINE if required.
1719
25.2k
    if (bIsClosed)
1720
9.89k
    {
1721
275k
        for (int iElement = 0; iElement < nNumElements; iElement++)
1722
265k
        {
1723
265k
            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
265k
        }
1732
9.89k
    }
1733
1734
    // Apparently extrusions are ignored for MLINE entities.
1735
    // poFeature->ApplyOCSTransformer( poMLS );
1736
25.2k
    poFeature->SetGeometryDirectly(poMLS.release());
1737
1738
25.2k
    PrepareLineStyle(poFeature.get());
1739
1740
25.2k
    return poFeature.release();
1741
32.0k
}
1742
1743
/************************************************************************/
1744
/*                          TranslateCIRCLE()                           */
1745
/************************************************************************/
1746
1747
OGRDXFFeature *OGRDXFLayer::TranslateCIRCLE()
1748
1749
35.0k
{
1750
35.0k
    char szLineBuf[257];
1751
35.0k
    int nCode = 0;
1752
35.0k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1753
35.0k
    double dfX1 = 0.0;
1754
35.0k
    double dfY1 = 0.0;
1755
35.0k
    double dfZ1 = 0.0;
1756
35.0k
    double dfRadius = 0.0;
1757
35.0k
    double dfThickness = 0.0;
1758
35.0k
    bool bHaveZ = false;
1759
1760
    /* -------------------------------------------------------------------- */
1761
    /*      Process values.                                                 */
1762
    /* -------------------------------------------------------------------- */
1763
195k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1764
160k
    {
1765
160k
        switch (nCode)
1766
160k
        {
1767
6.53k
            case 10:
1768
6.53k
                dfX1 = CPLAtof(szLineBuf);
1769
6.53k
                break;
1770
1771
13.0k
            case 20:
1772
13.0k
                dfY1 = CPLAtof(szLineBuf);
1773
13.0k
                break;
1774
1775
8.99k
            case 30:
1776
8.99k
                dfZ1 = CPLAtof(szLineBuf);
1777
8.99k
                bHaveZ = true;
1778
8.99k
                break;
1779
1780
24.7k
            case 39:
1781
24.7k
                dfThickness = CPLAtof(szLineBuf);
1782
24.7k
                break;
1783
1784
4.80k
            case 40:
1785
4.80k
                dfRadius = CPLAtof(szLineBuf);
1786
4.80k
                break;
1787
1788
102k
            default:
1789
102k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1790
102k
                break;
1791
160k
        }
1792
160k
    }
1793
35.0k
    if (nCode < 0)
1794
443
    {
1795
443
        DXF_LAYER_READER_ERROR();
1796
443
        return nullptr;
1797
443
    }
1798
1799
34.6k
    poDS->UnreadValue();
1800
1801
    /* -------------------------------------------------------------------- */
1802
    /*      Create geometry                                                 */
1803
    /* -------------------------------------------------------------------- */
1804
34.6k
    auto poCircle = std::unique_ptr<OGRLineString>(
1805
34.6k
        OGRGeometryFactory::approximateArcAngles(dfX1, dfY1, dfZ1, dfRadius,
1806
34.6k
                                                 dfRadius, 0.0, 0.0, 360.0, 0.0,
1807
34.6k
                                                 poDS->InlineBlocks())
1808
34.6k
            ->toLineString());
1809
1810
34.6k
    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
34.6k
    if (dfThickness != 0.0 && nPoints > 1)
1815
9.33k
    {
1816
9.33k
        OGRPolyhedralSurface *poSurface = new OGRPolyhedralSurface();
1817
1818
        // Add the bottom base as a polygon
1819
9.33k
        OGRLinearRing *poRing1 = new OGRLinearRing();
1820
9.33k
        poRing1->addSubLineString(poCircle.get());
1821
1822
9.33k
        OGRPolygon *poBase1 = new OGRPolygon();
1823
9.33k
        poBase1->addRingDirectly(poRing1);
1824
9.33k
        poSurface->addGeometryDirectly(poBase1);
1825
1826
        // Create and add the top base
1827
9.33k
        OGRLinearRing *poRing2 = poRing1->clone();
1828
1829
9.33k
        OGRDXFInsertTransformer oTransformer;
1830
9.33k
        oTransformer.dfZOffset = dfThickness;
1831
9.33k
        poRing2->transform(&oTransformer);
1832
1833
9.33k
        OGRPolygon *poBase2 = new OGRPolygon();
1834
9.33k
        poBase2->addRingDirectly(poRing2);
1835
9.33k
        poSurface->addGeometryDirectly(poBase2);
1836
1837
        // Add the side of the cylinder as two "semicylindrical" polygons
1838
9.33k
        auto poRect = std::make_unique<OGRLinearRing>();
1839
9.33k
        OGRPoint oPoint;
1840
1841
438k
        for (int iPoint = nPoints / 2; iPoint >= 0; iPoint--)
1842
429k
        {
1843
429k
            poRing1->getPoint(iPoint, &oPoint);
1844
429k
            poRect->addPoint(&oPoint);
1845
429k
        }
1846
438k
        for (int iPoint = 0; iPoint <= nPoints / 2; iPoint++)
1847
429k
        {
1848
429k
            poRing2->getPoint(iPoint, &oPoint);
1849
429k
            poRect->addPoint(&oPoint);
1850
429k
        }
1851
1852
9.33k
        poRect->closeRings();
1853
1854
9.33k
        OGRPolygon *poRectPolygon = new OGRPolygon();
1855
9.33k
        poRectPolygon->addRingDirectly(poRect.release());
1856
9.33k
        poSurface->addGeometryDirectly(poRectPolygon);
1857
1858
9.33k
        poRect = std::make_unique<OGRLinearRing>();
1859
1860
438k
        for (int iPoint = nPoints - 1; iPoint >= nPoints / 2; iPoint--)
1861
429k
        {
1862
429k
            poRing1->getPoint(iPoint, &oPoint);
1863
429k
            poRect->addPoint(&oPoint);
1864
429k
        }
1865
438k
        for (int iPoint = nPoints / 2; iPoint < nPoints; iPoint++)
1866
429k
        {
1867
429k
            poRing2->getPoint(iPoint, &oPoint);
1868
429k
            poRect->addPoint(&oPoint);
1869
429k
        }
1870
1871
9.33k
        poRect->closeRings();
1872
1873
9.33k
        poRectPolygon = new OGRPolygon();
1874
9.33k
        poRectPolygon->addRingDirectly(poRect.release());
1875
9.33k
        poSurface->addGeometryDirectly(poRectPolygon);
1876
1877
        // That's your cylinder, folks
1878
9.33k
        poFeature->ApplyOCSTransformer(poSurface);
1879
9.33k
        poFeature->SetGeometryDirectly(poSurface);
1880
9.33k
    }
1881
25.2k
    else
1882
25.2k
    {
1883
25.2k
        if (!bHaveZ)
1884
18.8k
            poCircle->flattenTo2D();
1885
1886
25.2k
        poFeature->ApplyOCSTransformer(poCircle.get());
1887
25.2k
        poFeature->SetGeometryDirectly(poCircle.release());
1888
25.2k
    }
1889
1890
34.6k
    PrepareLineStyle(poFeature.get());
1891
1892
34.6k
    return poFeature.release();
1893
35.0k
}
1894
1895
/************************************************************************/
1896
/*                          TranslateELLIPSE()                          */
1897
/************************************************************************/
1898
1899
OGRDXFFeature *OGRDXFLayer::TranslateELLIPSE()
1900
1901
28.9k
{
1902
28.9k
    char szLineBuf[257];
1903
28.9k
    int nCode = 0;
1904
28.9k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
1905
28.9k
    double dfX1 = 0.0;
1906
28.9k
    double dfY1 = 0.0;
1907
28.9k
    double dfZ1 = 0.0;
1908
28.9k
    double dfRatio = 0.0;
1909
28.9k
    double dfStartAngle = 0.0;
1910
28.9k
    double dfEndAngle = 360.0;
1911
28.9k
    double dfAxisX = 0.0;
1912
28.9k
    double dfAxisY = 0.0;
1913
28.9k
    double dfAxisZ = 0.0;
1914
28.9k
    bool bHaveZ = false;
1915
28.9k
    bool bApplyOCSTransform = false;
1916
1917
    /* -------------------------------------------------------------------- */
1918
    /*      Process values.                                                 */
1919
    /* -------------------------------------------------------------------- */
1920
171k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
1921
142k
    {
1922
142k
        switch (nCode)
1923
142k
        {
1924
9.15k
            case 10:
1925
9.15k
                dfX1 = CPLAtof(szLineBuf);
1926
9.15k
                break;
1927
1928
11.0k
            case 20:
1929
11.0k
                dfY1 = CPLAtof(szLineBuf);
1930
11.0k
                break;
1931
1932
7.18k
            case 30:
1933
7.18k
                dfZ1 = CPLAtof(szLineBuf);
1934
7.18k
                bHaveZ = true;
1935
7.18k
                break;
1936
1937
7.63k
            case 11:
1938
7.63k
                dfAxisX = CPLAtof(szLineBuf);
1939
7.63k
                break;
1940
1941
5.93k
            case 21:
1942
5.93k
                dfAxisY = CPLAtof(szLineBuf);
1943
5.93k
                break;
1944
1945
942
            case 31:
1946
942
                dfAxisZ = CPLAtof(szLineBuf);
1947
942
                break;
1948
1949
3.67k
            case 40:
1950
3.67k
                dfRatio = CPLAtof(szLineBuf);
1951
3.67k
                break;
1952
1953
7.86k
            case 41:
1954
                // These *seem* to always be in radians regardless of $AUNITS
1955
7.86k
                dfEndAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
1956
7.86k
                break;
1957
1958
7.93k
            case 42:
1959
                // These *seem* to always be in radians regardless of $AUNITS
1960
7.93k
                dfStartAngle = -1 * CPLAtof(szLineBuf) * 180.0 / M_PI;
1961
7.93k
                break;
1962
1963
81.3k
            default:
1964
81.3k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
1965
81.3k
                break;
1966
142k
        }
1967
142k
    }
1968
28.9k
    if (nCode < 0)
1969
168
    {
1970
168
        DXF_LAYER_READER_ERROR();
1971
168
        return nullptr;
1972
168
    }
1973
1974
28.7k
    poDS->UnreadValue();
1975
1976
    /* -------------------------------------------------------------------- */
1977
    /*      Setup coordinate system                                         */
1978
    /* -------------------------------------------------------------------- */
1979
28.7k
    double adfN[3];
1980
28.7k
    poFeature->oOCS.ToArray(adfN);
1981
1982
28.7k
    if ((adfN[0] == 0.0 && adfN[1] == 0.0 && adfN[2] == 1.0) == false)
1983
6.37k
    {
1984
6.37k
        OGRDXFOCSTransformer oTransformer(adfN, true);
1985
1986
6.37k
        bApplyOCSTransform = true;
1987
1988
6.37k
        double *x = &dfX1;
1989
6.37k
        double *y = &dfY1;
1990
6.37k
        double *z = &dfZ1;
1991
6.37k
        oTransformer.InverseTransform(1, x, y, z);
1992
1993
6.37k
        x = &dfAxisX;
1994
6.37k
        y = &dfAxisY;
1995
6.37k
        z = &dfAxisZ;
1996
6.37k
        oTransformer.InverseTransform(1, x, y, z);
1997
6.37k
    }
1998
1999
    /* -------------------------------------------------------------------- */
2000
    /*      Compute primary and secondary axis lengths, and the angle of    */
2001
    /*      rotation for the ellipse.                                       */
2002
    /* -------------------------------------------------------------------- */
2003
28.7k
    double dfPrimaryRadius =
2004
28.7k
        sqrt(dfAxisX * dfAxisX + dfAxisY * dfAxisY + dfAxisZ * dfAxisZ);
2005
2006
28.7k
    double dfSecondaryRadius = dfRatio * dfPrimaryRadius;
2007
2008
28.7k
    double dfRotation = -1 * atan2(dfAxisY, dfAxisX) * 180 / M_PI;
2009
2010
    /* -------------------------------------------------------------------- */
2011
    /*      Create geometry                                                 */
2012
    /* -------------------------------------------------------------------- */
2013
28.7k
    if (dfStartAngle > dfEndAngle)
2014
2.98k
        dfEndAngle += 360.0;
2015
2016
28.7k
    if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
2017
23.6k
    {
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
23.6k
        auto poEllipse = std::unique_ptr<OGRGeometry>(
2021
23.6k
            OGRGeometryFactory::approximateArcAngles(
2022
23.6k
                dfX1, dfY1, dfZ1, dfPrimaryRadius, dfSecondaryRadius,
2023
23.6k
                dfRotation, dfStartAngle, dfEndAngle, 0.0,
2024
23.6k
                poDS->InlineBlocks()));
2025
2026
23.6k
        if (!bHaveZ)
2027
20.1k
            poEllipse->flattenTo2D();
2028
2029
23.6k
        if (bApplyOCSTransform == true)
2030
5.58k
            poFeature->ApplyOCSTransformer(poEllipse.get());
2031
23.6k
        poFeature->SetGeometryDirectly(poEllipse.release());
2032
23.6k
    }
2033
5.11k
    else
2034
5.11k
    {
2035
        // TODO: emit error ?
2036
5.11k
    }
2037
2038
28.7k
    PrepareLineStyle(poFeature.get());
2039
2040
28.7k
    return poFeature.release();
2041
28.9k
}
2042
2043
/************************************************************************/
2044
/*                            TranslateARC()                            */
2045
/************************************************************************/
2046
2047
OGRDXFFeature *OGRDXFLayer::TranslateARC()
2048
2049
26.0k
{
2050
26.0k
    char szLineBuf[257];
2051
26.0k
    int nCode = 0;
2052
26.0k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2053
26.0k
    double dfX1 = 0.0;
2054
26.0k
    double dfY1 = 0.0;
2055
26.0k
    double dfZ1 = 0.0;
2056
26.0k
    double dfRadius = 0.0;
2057
26.0k
    double dfStartAngle = 0.0;
2058
26.0k
    double dfEndAngle = 360.0;
2059
26.0k
    bool bHaveZ = false;
2060
2061
    /* -------------------------------------------------------------------- */
2062
    /*      Process values.                                                 */
2063
    /* -------------------------------------------------------------------- */
2064
185k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2065
159k
    {
2066
159k
        switch (nCode)
2067
159k
        {
2068
9.54k
            case 10:
2069
9.54k
                dfX1 = CPLAtof(szLineBuf);
2070
9.54k
                break;
2071
2072
5.90k
            case 20:
2073
5.90k
                dfY1 = CPLAtof(szLineBuf);
2074
5.90k
                break;
2075
2076
9.32k
            case 30:
2077
9.32k
                dfZ1 = CPLAtof(szLineBuf);
2078
9.32k
                bHaveZ = true;
2079
9.32k
                break;
2080
2081
11.9k
            case 40:
2082
11.9k
                dfRadius = CPLAtof(szLineBuf);
2083
11.9k
                break;
2084
2085
5.03k
            case 50:
2086
                // This is apparently always degrees regardless of AUNITS
2087
5.03k
                dfEndAngle = -1 * CPLAtof(szLineBuf);
2088
5.03k
                break;
2089
2090
10.2k
            case 51:
2091
                // This is apparently always degrees regardless of AUNITS
2092
10.2k
                dfStartAngle = -1 * CPLAtof(szLineBuf);
2093
10.2k
                break;
2094
2095
107k
            default:
2096
107k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2097
107k
                break;
2098
159k
        }
2099
159k
    }
2100
26.0k
    if (nCode < 0)
2101
893
    {
2102
893
        DXF_LAYER_READER_ERROR();
2103
893
        return nullptr;
2104
893
    }
2105
2106
25.1k
    poDS->UnreadValue();
2107
2108
    /* -------------------------------------------------------------------- */
2109
    /*      Create geometry                                                 */
2110
    /* -------------------------------------------------------------------- */
2111
25.1k
    if (dfStartAngle > dfEndAngle)
2112
3.44k
        dfEndAngle += 360.0;
2113
2114
25.1k
    if (fabs(dfEndAngle - dfStartAngle) <= 361.0)
2115
17.3k
    {
2116
17.3k
        auto poArc = std::unique_ptr<OGRGeometry>(
2117
17.3k
            OGRGeometryFactory::approximateArcAngles(
2118
17.3k
                dfX1, dfY1, dfZ1, dfRadius, dfRadius, 0.0, dfStartAngle,
2119
17.3k
                dfEndAngle, 0.0, poDS->InlineBlocks()));
2120
17.3k
        if (!bHaveZ)
2121
12.4k
            poArc->flattenTo2D();
2122
2123
17.3k
        poFeature->ApplyOCSTransformer(poArc.get());
2124
17.3k
        poFeature->SetGeometryDirectly(poArc.release());
2125
17.3k
    }
2126
7.81k
    else
2127
7.81k
    {
2128
        // TODO: emit error ?
2129
7.81k
    }
2130
2131
25.1k
    PrepareLineStyle(poFeature.get());
2132
2133
25.1k
    return poFeature.release();
2134
26.0k
}
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
84.8k
{
2146
84.8k
    char szLineBuf[257];
2147
84.8k
    int nCode;
2148
84.8k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2149
2150
84.8k
    std::vector<double> adfControlPoints(FORTRAN_INDEXING, 0.0);
2151
84.8k
    std::vector<double> adfKnots(FORTRAN_INDEXING, 0.0);
2152
84.8k
    std::vector<double> adfWeights(FORTRAN_INDEXING, 0.0);
2153
84.8k
    int nDegree = -1;
2154
84.8k
    int nControlPoints = -1;
2155
84.8k
    int nKnots = -1;
2156
84.8k
    bool bInsertNullZ = false;
2157
84.8k
    bool bHasZ = false;
2158
2159
    /* -------------------------------------------------------------------- */
2160
    /*      Process values.                                                 */
2161
    /* -------------------------------------------------------------------- */
2162
1.06M
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2163
979k
    {
2164
979k
        bool bStop = false;
2165
979k
        switch (nCode)
2166
979k
        {
2167
124k
            case 10:
2168
124k
                if (bInsertNullZ)
2169
19.5k
                {
2170
19.5k
                    adfControlPoints.push_back(0.0);
2171
19.5k
                    bInsertNullZ = false;
2172
19.5k
                }
2173
124k
                adfControlPoints.push_back(CPLAtof(szLineBuf));
2174
124k
                break;
2175
2176
194k
            case 20:
2177
194k
                adfControlPoints.push_back(CPLAtof(szLineBuf));
2178
194k
                bInsertNullZ = true;
2179
194k
                break;
2180
2181
27.4k
            case 30:
2182
27.4k
                adfControlPoints.push_back(CPLAtof(szLineBuf));
2183
27.4k
                bHasZ = true;
2184
27.4k
                bInsertNullZ = false;
2185
27.4k
                break;
2186
2187
46.8k
            case 40:
2188
46.8k
            {
2189
46.8k
                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
46.8k
                if (dfVal < 0 && dfVal > -1.0e-10)
2194
0
                    dfVal = 0;
2195
46.8k
                adfKnots.push_back(dfVal);
2196
46.8k
                break;
2197
0
            }
2198
2199
18.0k
            case 41:
2200
18.0k
                adfWeights.push_back(CPLAtof(szLineBuf));
2201
18.0k
                break;
2202
2203
19.3k
            case 70:
2204
19.3k
                break;
2205
2206
107k
            case 71:
2207
107k
                nDegree = atoi(szLineBuf);
2208
                // Arbitrary threshold
2209
107k
                if (nDegree < 0 || nDegree > 100)
2210
1.68k
                {
2211
1.68k
                    DXF_LAYER_READER_ERROR();
2212
1.68k
                    return nullptr;
2213
1.68k
                }
2214
105k
                break;
2215
2216
105k
            case 72:
2217
32.3k
                nKnots = atoi(szLineBuf);
2218
                // Arbitrary threshold
2219
32.3k
                if (nKnots < 0 || nKnots > 10000000)
2220
556
                {
2221
556
                    DXF_LAYER_READER_ERROR();
2222
556
                    return nullptr;
2223
556
                }
2224
31.8k
                break;
2225
2226
31.8k
            case 73:
2227
9.20k
                nControlPoints = atoi(szLineBuf);
2228
                // Arbitrary threshold
2229
9.20k
                if (nControlPoints < 0 || nControlPoints > 10000000)
2230
1.28k
                {
2231
1.28k
                    DXF_LAYER_READER_ERROR();
2232
1.28k
                    return nullptr;
2233
1.28k
                }
2234
7.92k
                break;
2235
2236
11.7k
            case 100:
2237
11.7k
                if (EQUAL(szLineBuf, "AcDbHelix"))
2238
0
                    bStop = true;
2239
11.7k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2240
11.7k
                break;
2241
2242
387k
            default:
2243
387k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2244
387k
                break;
2245
979k
        }
2246
2247
975k
        if (bStop)
2248
0
            break;
2249
975k
    }
2250
81.2k
    if (nCode < 0)
2251
2.87k
    {
2252
2.87k
        DXF_LAYER_READER_ERROR();
2253
2.87k
        return nullptr;
2254
2.87k
    }
2255
2256
78.4k
    if (nCode == 0)
2257
78.4k
        poDS->UnreadValue();
2258
2259
78.4k
    if (bInsertNullZ)
2260
28.0k
    {
2261
28.0k
        adfControlPoints.push_back(0.0);
2262
28.0k
    }
2263
2264
78.4k
    if (static_cast<int>(adfControlPoints.size() % 3) != FORTRAN_INDEXING)
2265
22.0k
    {
2266
22.0k
        CPLError(CE_Failure, CPLE_AppDefined,
2267
22.0k
                 "Invalid number of values for spline control points");
2268
22.0k
        DXF_LAYER_READER_ERROR();
2269
22.0k
        return nullptr;
2270
22.0k
    }
2271
2272
    /* -------------------------------------------------------------------- */
2273
    /*      Use the helper function to check the input data and insert      */
2274
    /*      the spline.                                                     */
2275
    /* -------------------------------------------------------------------- */
2276
56.4k
    auto poLS =
2277
56.4k
        InsertSplineWithChecks(nDegree, adfControlPoints, bHasZ, nControlPoints,
2278
56.4k
                               adfKnots, nKnots, adfWeights);
2279
2280
56.4k
    if (!poLS)
2281
48.0k
    {
2282
48.0k
        DXF_LAYER_READER_ERROR();
2283
48.0k
        return nullptr;
2284
48.0k
    }
2285
2286
8.35k
    poFeature->SetGeometryDirectly(poLS.release());
2287
2288
8.35k
    PrepareLineStyle(poFeature.get());
2289
2290
8.35k
    return poFeature.release();
2291
56.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
56.4k
{
2305
    /* -------------------------------------------------------------------- */
2306
    /*      Sanity checks                                                   */
2307
    /* -------------------------------------------------------------------- */
2308
56.4k
    const int nOrder = nDegree + 1;
2309
2310
56.4k
    bool bResult = (nOrder >= 2);
2311
56.4k
    if (bResult == true)
2312
17.7k
    {
2313
        // Check whether nctrlpts value matches number of vertices read
2314
17.7k
        int nCheck =
2315
17.7k
            (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) / 3;
2316
2317
17.7k
        if (nControlPoints == -1)
2318
16.1k
            nControlPoints =
2319
16.1k
                (static_cast<int>(adfControlPoints.size()) - FORTRAN_INDEXING) /
2320
16.1k
                3;
2321
2322
        // min( num(ctrlpts) ) = order
2323
17.7k
        bResult = (nControlPoints >= nOrder && nControlPoints == nCheck);
2324
17.7k
    }
2325
2326
56.4k
    bool bCalculateKnots = false;
2327
56.4k
    if (bResult == true)
2328
11.3k
    {
2329
11.3k
        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
11.3k
        if (nCheck == 0)
2336
10.6k
        {
2337
10.6k
            bCalculateKnots = true;
2338
93.0k
            for (int i = 0; i < (nControlPoints + nOrder); i++)
2339
82.4k
                adfKnots.push_back(0.0);
2340
2341
10.6k
            nCheck = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
2342
10.6k
        }
2343
        // Adjust nknots value when:
2344
        // - nknots value not present, knot vertices present
2345
        //   nknots is -1, ncheck is (nctrlpts + order)
2346
11.3k
        if (nKnots == -1)
2347
10.5k
            nKnots = static_cast<int>(adfKnots.size()) - FORTRAN_INDEXING;
2348
2349
        // num(knots) = num(ctrlpts) + order
2350
11.3k
        bResult = (nKnots == (nControlPoints + nOrder) && nKnots == nCheck);
2351
11.3k
    }
2352
2353
56.4k
    if (bResult == true)
2354
10.8k
    {
2355
10.8k
        int nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
2356
2357
10.8k
        if (nWeights == 0)
2358
8.34k
        {
2359
39.6k
            for (int i = 0; i < nControlPoints; i++)
2360
31.3k
                adfWeights.push_back(1.0);
2361
2362
8.34k
            nWeights = static_cast<int>(adfWeights.size()) - FORTRAN_INDEXING;
2363
8.34k
        }
2364
2365
        // num(weights) = num(ctrlpts)
2366
10.8k
        bResult = (nWeights == nControlPoints);
2367
10.8k
    }
2368
2369
56.4k
    if (bResult == false)
2370
48.0k
        return nullptr;
2371
2372
    /* -------------------------------------------------------------------- */
2373
    /*      Interpolate spline                                              */
2374
    /* -------------------------------------------------------------------- */
2375
8.35k
    int p1 = nControlPoints * 8;
2376
8.35k
    std::vector<double> p(3 * p1 + FORTRAN_INDEXING);
2377
2378
8.35k
    rbspline2(nControlPoints, nOrder, p1, &(adfControlPoints[0]),
2379
8.35k
              &(adfWeights[0]), bCalculateKnots, &(adfKnots[0]), &(p[0]));
2380
2381
    /* -------------------------------------------------------------------- */
2382
    /*      Turn into OGR geometry.                                         */
2383
    /* -------------------------------------------------------------------- */
2384
8.35k
    auto poLS = std::make_unique<OGRLineString>();
2385
2386
8.35k
    poLS->setNumPoints(p1);
2387
8.35k
    if (bHasZ)
2388
1.38k
    {
2389
51.0k
        for (int i = 0; i < p1; i++)
2390
49.6k
            poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
2391
49.6k
                           p[i * 3 + FORTRAN_INDEXING + 1],
2392
49.6k
                           p[i * 3 + FORTRAN_INDEXING + 2]);
2393
1.38k
    }
2394
6.96k
    else
2395
6.96k
    {
2396
208k
        for (int i = 0; i < p1; i++)
2397
201k
            poLS->setPoint(i, p[i * 3 + FORTRAN_INDEXING],
2398
201k
                           p[i * 3 + FORTRAN_INDEXING + 1]);
2399
6.96k
    }
2400
2401
8.35k
    return poLS;
2402
56.4k
}
2403
2404
/************************************************************************/
2405
/*                          Translate3DFACE()                           */
2406
/************************************************************************/
2407
2408
OGRDXFFeature *OGRDXFLayer::Translate3DFACE()
2409
2410
34.5k
{
2411
34.5k
    char szLineBuf[257];
2412
34.5k
    int nCode = 0;
2413
34.5k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2414
34.5k
    double dfX1 = 0.0;
2415
34.5k
    double dfY1 = 0.0;
2416
34.5k
    double dfZ1 = 0.0;
2417
34.5k
    double dfX2 = 0.0;
2418
34.5k
    double dfY2 = 0.0;
2419
34.5k
    double dfZ2 = 0.0;
2420
34.5k
    double dfX3 = 0.0;
2421
34.5k
    double dfY3 = 0.0;
2422
34.5k
    double dfZ3 = 0.0;
2423
34.5k
    double dfX4 = 0.0;
2424
34.5k
    double dfY4 = 0.0;
2425
34.5k
    double dfZ4 = 0.0;
2426
2427
    /* -------------------------------------------------------------------- */
2428
    /*      Process values.                                                 */
2429
    /* -------------------------------------------------------------------- */
2430
290k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2431
256k
    {
2432
256k
        switch (nCode)
2433
256k
        {
2434
20.0k
            case 10:
2435
20.0k
                dfX1 = CPLAtof(szLineBuf);
2436
20.0k
                break;
2437
2438
9.23k
            case 11:
2439
9.23k
                dfX2 = CPLAtof(szLineBuf);
2440
9.23k
                break;
2441
2442
5.53k
            case 12:
2443
5.53k
                dfX3 = CPLAtof(szLineBuf);
2444
5.53k
                break;
2445
2446
4.39k
            case 13:
2447
4.39k
                dfX4 = CPLAtof(szLineBuf);
2448
4.39k
                break;
2449
2450
23.3k
            case 20:
2451
23.3k
                dfY1 = CPLAtof(szLineBuf);
2452
23.3k
                break;
2453
2454
5.69k
            case 21:
2455
5.69k
                dfY2 = CPLAtof(szLineBuf);
2456
5.69k
                break;
2457
2458
6.63k
            case 22:
2459
6.63k
                dfY3 = CPLAtof(szLineBuf);
2460
6.63k
                break;
2461
2462
3.75k
            case 23:
2463
3.75k
                dfY4 = CPLAtof(szLineBuf);
2464
3.75k
                break;
2465
2466
10.6k
            case 30:
2467
10.6k
                dfZ1 = CPLAtof(szLineBuf);
2468
10.6k
                break;
2469
2470
13.1k
            case 31:
2471
13.1k
                dfZ2 = CPLAtof(szLineBuf);
2472
13.1k
                break;
2473
2474
6.49k
            case 32:
2475
6.49k
                dfZ3 = CPLAtof(szLineBuf);
2476
6.49k
                break;
2477
2478
5.29k
            case 33:
2479
5.29k
                dfZ4 = CPLAtof(szLineBuf);
2480
5.29k
                break;
2481
2482
141k
            default:
2483
141k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2484
141k
                break;
2485
256k
        }
2486
256k
    }
2487
34.5k
    if (nCode < 0)
2488
2.06k
    {
2489
2.06k
        DXF_LAYER_READER_ERROR();
2490
2.06k
        return nullptr;
2491
2.06k
    }
2492
2493
32.5k
    poDS->UnreadValue();
2494
2495
    /* -------------------------------------------------------------------- */
2496
    /*      Create geometry                                                 */
2497
    /* -------------------------------------------------------------------- */
2498
32.5k
    auto poPoly = std::make_unique<OGRPolygon>();
2499
32.5k
    OGRLinearRing *poLR = new OGRLinearRing();
2500
32.5k
    poLR->addPoint(dfX1, dfY1, dfZ1);
2501
32.5k
    poLR->addPoint(dfX2, dfY2, dfZ2);
2502
32.5k
    poLR->addPoint(dfX3, dfY3, dfZ3);
2503
32.5k
    if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
2504
12.2k
        poLR->addPoint(dfX4, dfY4, dfZ4);
2505
32.5k
    poPoly->addRingDirectly(poLR);
2506
32.5k
    poPoly->closeRings();
2507
2508
32.5k
    poFeature->ApplyOCSTransformer(poLR);
2509
32.5k
    poFeature->SetGeometryDirectly(poPoly.release());
2510
2511
32.5k
    PrepareLineStyle(poFeature.get());
2512
2513
32.5k
    return poFeature.release();
2514
34.5k
}
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
256k
{
2525
256k
    return oP1.getX() == oP2.getX() ? oP1.getY() < oP2.getY()
2526
256k
                                    : oP1.getX() < oP2.getX();
2527
256k
}
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
185k
{
2537
185k
    return oP1.getX() == oP2.getX() && oP1.getY() == oP2.getY() &&
2538
115k
           oP1.getZ() == oP2.getZ();
2539
185k
}
2540
2541
/************************************************************************/
2542
/*                           TranslateSOLID()                           */
2543
/************************************************************************/
2544
2545
OGRDXFFeature *OGRDXFLayer::TranslateSOLID()
2546
2547
63.2k
{
2548
63.2k
    char szLineBuf[257];
2549
63.2k
    int nCode = 0;
2550
63.2k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2551
63.2k
    double dfX1 = 0.0;
2552
63.2k
    double dfY1 = 0.0;
2553
63.2k
    double dfZ1 = 0.0;
2554
63.2k
    double dfX2 = 0.0;
2555
63.2k
    double dfY2 = 0.0;
2556
63.2k
    double dfZ2 = 0.0;
2557
63.2k
    double dfX3 = 0.0;
2558
63.2k
    double dfY3 = 0.0;
2559
63.2k
    double dfZ3 = 0.0;
2560
63.2k
    double dfX4 = 0.0;
2561
63.2k
    double dfY4 = 0.0;
2562
63.2k
    double dfZ4 = 0.0;
2563
2564
379k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2565
316k
    {
2566
316k
        switch (nCode)
2567
316k
        {
2568
25.4k
            case 10:
2569
25.4k
                dfX1 = CPLAtof(szLineBuf);
2570
25.4k
                break;
2571
2572
40.8k
            case 20:
2573
40.8k
                dfY1 = CPLAtof(szLineBuf);
2574
40.8k
                break;
2575
2576
17.5k
            case 30:
2577
17.5k
                dfZ1 = CPLAtof(szLineBuf);
2578
17.5k
                break;
2579
2580
14.9k
            case 11:
2581
14.9k
                dfX2 = CPLAtof(szLineBuf);
2582
14.9k
                break;
2583
2584
20.6k
            case 21:
2585
20.6k
                dfY2 = CPLAtof(szLineBuf);
2586
20.6k
                break;
2587
2588
15.3k
            case 31:
2589
15.3k
                dfZ2 = CPLAtof(szLineBuf);
2590
15.3k
                break;
2591
2592
12.6k
            case 12:
2593
12.6k
                dfX3 = CPLAtof(szLineBuf);
2594
12.6k
                break;
2595
2596
8.40k
            case 22:
2597
8.40k
                dfY3 = CPLAtof(szLineBuf);
2598
8.40k
                break;
2599
2600
7.37k
            case 32:
2601
7.37k
                dfZ3 = CPLAtof(szLineBuf);
2602
7.37k
                break;
2603
2604
3.48k
            case 13:
2605
3.48k
                dfX4 = CPLAtof(szLineBuf);
2606
3.48k
                break;
2607
2608
27.1k
            case 23:
2609
27.1k
                dfY4 = CPLAtof(szLineBuf);
2610
27.1k
                break;
2611
2612
4.01k
            case 33:
2613
4.01k
                dfZ4 = CPLAtof(szLineBuf);
2614
4.01k
                break;
2615
2616
118k
            default:
2617
118k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2618
118k
                break;
2619
316k
        }
2620
316k
    }
2621
63.2k
    if (nCode < 0)
2622
1.33k
    {
2623
1.33k
        DXF_LAYER_READER_ERROR();
2624
1.33k
        return nullptr;
2625
1.33k
    }
2626
61.9k
    poDS->UnreadValue();
2627
2628
    // do we want Z-coordinates?
2629
61.9k
    const bool bWantZ =
2630
61.9k
        dfZ1 != 0.0 || dfZ2 != 0.0 || dfZ3 != 0.0 || dfZ4 != 0.0;
2631
2632
    // check how many unique corners we have
2633
61.9k
    OGRPoint oCorners[4];
2634
61.9k
    oCorners[0].setX(dfX1);
2635
61.9k
    oCorners[0].setY(dfY1);
2636
61.9k
    if (bWantZ)
2637
15.4k
        oCorners[0].setZ(dfZ1);
2638
61.9k
    oCorners[1].setX(dfX2);
2639
61.9k
    oCorners[1].setY(dfY2);
2640
61.9k
    if (bWantZ)
2641
15.4k
        oCorners[1].setZ(dfZ2);
2642
61.9k
    oCorners[2].setX(dfX3);
2643
61.9k
    oCorners[2].setY(dfY3);
2644
61.9k
    if (bWantZ)
2645
15.4k
        oCorners[2].setZ(dfZ3);
2646
61.9k
    oCorners[3].setX(dfX4);
2647
61.9k
    oCorners[3].setY(dfY4);
2648
61.9k
    if (bWantZ)
2649
15.4k
        oCorners[3].setZ(dfZ4);
2650
2651
61.9k
    std::sort(&oCorners[0], &oCorners[4], PointXAxisComparer);
2652
61.9k
    int nCornerCount = static_cast<int>(
2653
61.9k
        std::unique(&oCorners[0], &oCorners[4], PointXYZEqualityComparer) -
2654
61.9k
        &oCorners[0]);
2655
61.9k
    if (nCornerCount < 1)
2656
0
    {
2657
0
        DXF_LAYER_READER_ERROR();
2658
0
        return nullptr;
2659
0
    }
2660
2661
61.9k
    std::unique_ptr<OGRGeometry> poFinalGeom;
2662
2663
    // what kind of object do we need?
2664
61.9k
    if (nCornerCount == 1)
2665
17.2k
    {
2666
17.2k
        poFinalGeom.reset(oCorners[0].clone());
2667
2668
17.2k
        PrepareLineStyle(poFeature.get());
2669
17.2k
    }
2670
44.6k
    else if (nCornerCount == 2)
2671
14.6k
    {
2672
14.6k
        auto poLS = std::make_unique<OGRLineString>();
2673
14.6k
        poLS->setPoint(0, &oCorners[0]);
2674
14.6k
        poLS->setPoint(1, &oCorners[1]);
2675
14.6k
        poFinalGeom.reset(poLS.release());
2676
2677
14.6k
        PrepareLineStyle(poFeature.get());
2678
14.6k
    }
2679
30.0k
    else
2680
30.0k
    {
2681
        // SOLID vertices seem to be joined in the order 1-2-4-3-1.
2682
        // See trac ticket #7089
2683
30.0k
        OGRLinearRing *poLinearRing = new OGRLinearRing();
2684
30.0k
        int iIndex = 0;
2685
30.0k
        poLinearRing->setPoint(iIndex++, dfX1, dfY1, dfZ1);
2686
30.0k
        if (dfX1 != dfX2 || dfY1 != dfY2 || dfZ1 != dfZ2)
2687
27.6k
            poLinearRing->setPoint(iIndex++, dfX2, dfY2, dfZ2);
2688
30.0k
        if (dfX2 != dfX4 || dfY2 != dfY4 || dfZ2 != dfZ4)
2689
23.6k
            poLinearRing->setPoint(iIndex++, dfX4, dfY4, dfZ4);
2690
30.0k
        if (dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3)
2691
20.7k
            poLinearRing->setPoint(iIndex++, dfX3, dfY3, dfZ3);
2692
30.0k
        poLinearRing->closeRings();
2693
2694
30.0k
        if (!bWantZ)
2695
15.7k
            poLinearRing->flattenTo2D();
2696
2697
30.0k
        auto poPoly = std::make_unique<OGRPolygon>();
2698
30.0k
        poPoly->addRingDirectly(poLinearRing);
2699
30.0k
        poFinalGeom.reset(poPoly.release());
2700
2701
30.0k
        PrepareBrushStyle(poFeature.get());
2702
30.0k
    }
2703
2704
61.9k
    poFeature->ApplyOCSTransformer(poFinalGeom.get());
2705
61.9k
    poFeature->SetGeometryDirectly(poFinalGeom.release());
2706
2707
61.9k
    return poFeature.release();
2708
61.9k
}
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.27k
{
2867
4.27k
    char szLineBuf[257];
2868
4.27k
    int nCode;
2869
4.27k
    auto poFeature = std::make_unique<OGRDXFFeature>(poFeatureDefn);
2870
4.27k
    double dfX = 0.0, dfY = 0.0, dfXOffset = 0.0, dfYOffset = 0.0;
2871
4.27k
    double dfXscale = 1.0, dfYscale = 1.0;
2872
2873
4.27k
    int nNumVertices = 0;
2874
4.27k
    int nBoundaryVertexCount = 0;
2875
4.27k
    int nFormat = 0;
2876
2877
4.27k
    DXFSmoothPolyline smoothPolyline;
2878
2879
4.27k
    smoothPolyline.setCoordinateDimension(2);
2880
2881
    /* Read main feature properties as class, insertion point (in WCS) */
2882
38.4k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
2883
34.8k
    {
2884
34.8k
        if (nBoundaryVertexCount > nNumVertices)
2885
729
        {
2886
729
            CPLError(CE_Failure, CPLE_AppDefined,
2887
729
                     "Too many vertices found in WIPEOUT.");
2888
729
            return nullptr;
2889
729
        }
2890
2891
34.1k
        switch (nCode)
2892
34.1k
        {
2893
                /* Group codes 10, 20 control the insertion point of the lower
2894
                   left corner of your image. */
2895
3.24k
            case 10:
2896
3.24k
                dfXOffset = CPLAtof(szLineBuf);
2897
3.24k
                break;
2898
2899
9.59k
            case 20:
2900
9.59k
                dfYOffset = CPLAtof(szLineBuf);
2901
9.59k
                smoothPolyline.AddPoint(dfXOffset, dfYOffset, 0.0, 0.0);
2902
9.59k
                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
113
            case 11:
2912
113
                dfXscale = CPLAtof(szLineBuf);
2913
113
                break;
2914
2915
126
            case 22:
2916
126
                dfYscale = CPLAtof(szLineBuf);
2917
126
                break;
2918
2919
82
            case 31:
2920
82
                break;
2921
2922
            /* Read image properties and set them in feature style (contrast...) */
2923
0
            case 281:
2924
0
                break;
2925
2926
186
            case 282:
2927
186
                break;
2928
2929
0
            case 293:
2930
0
                break;
2931
2932
217
            case 71:
2933
217
                nFormat = atoi(szLineBuf);
2934
217
                if (nFormat == 1)
2935
4
                {
2936
                    // Here ignore feature because point format set to 1 is not supported
2937
4
                    CPLError(
2938
4
                        CE_Warning, CPLE_AppDefined,
2939
4
                        "Format of points in WIPEOUT entity not supported.");
2940
4
                    return nullptr;
2941
4
                }
2942
213
                break;
2943
2944
848
            case 91:
2945
848
                nNumVertices = atoi(szLineBuf);
2946
848
                break;
2947
2948
            /* -------------------------------------------------------------------- */
2949
            /*      Read clipping boundary properties and set them feature geometry */
2950
            /*      Collect vertices as a smooth polyline.                          */
2951
            /* -------------------------------------------------------------------- */
2952
2
            case 14:
2953
2
                dfX = CPLAtof(szLineBuf);
2954
2
                break;
2955
2956
892
            case 24:
2957
892
                dfY = CPLAtof(szLineBuf);
2958
892
                smoothPolyline.AddPoint(dfXOffset + (0.5 + dfX) * dfXscale,
2959
892
                                        dfYOffset + (0.5 - dfY) * dfYscale, 0.0,
2960
892
                                        0.0);
2961
892
                nBoundaryVertexCount++;
2962
892
                break;
2963
2964
18.8k
            default:
2965
18.8k
                TranslateGenericProperty(poFeature.get(), nCode, szLineBuf);
2966
18.8k
                break;
2967
34.1k
        }
2968
34.1k
    }
2969
3.54k
    if (nCode < 0)
2970
24
    {
2971
24
        DXF_LAYER_READER_ERROR();
2972
24
        return nullptr;
2973
24
    }
2974
2975
3.51k
    poDS->UnreadValue();
2976
2977
3.51k
    if (smoothPolyline.IsEmpty())
2978
1.72k
    {
2979
1.72k
        return nullptr;
2980
1.72k
    }
2981
2982
    /* -------------------------------------------------------------------- */
2983
    /*      Close polyline to output polygon geometry.                      */
2984
    /* -------------------------------------------------------------------- */
2985
1.79k
    smoothPolyline.Close();
2986
2987
1.79k
    OGRGeometry *poGeom = smoothPolyline.Tessellate(TRUE);
2988
2989
1.79k
    poFeature->SetGeometryDirectly(poGeom);
2990
2991
    // Set style pen color
2992
1.79k
    PrepareLineStyle(poFeature.get());
2993
2994
1.79k
    return poFeature.release();
2995
3.51k
}
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
70.1k
{
3069
    /* -------------------------------------------------------------------- */
3070
    /*      Set up protection against excessive recursion on this layer.    */
3071
    /* -------------------------------------------------------------------- */
3072
70.1k
    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
70.1k
    OGRPoint oInsertionPoint(oTransformer.dfXOffset, oTransformer.dfYOffset,
3083
70.1k
                             oTransformer.dfZOffset);
3084
3085
70.1k
    poFeature->ApplyOCSTransformer(&oInsertionPoint);
3086
3087
70.1k
    oTransformer.dfXOffset = oInsertionPoint.getX();
3088
70.1k
    oTransformer.dfYOffset = oInsertionPoint.getY();
3089
70.1k
    oTransformer.dfZOffset = oInsertionPoint.getZ();
3090
3091
    /* -------------------------------------------------------------------- */
3092
    /*      Lookup the block.                                               */
3093
    /* -------------------------------------------------------------------- */
3094
70.1k
    DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName);
3095
3096
70.1k
    if (poBlock == nullptr)
3097
70.1k
    {
3098
        // CPLDebug( "DXF", "Attempt to insert missing block %s", osBlockName );
3099
70.1k
        poDS->PopBlockInsertion();
3100
70.1k
        throw std::invalid_argument("osBlockName");
3101
70.1k
    }
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
39.6k
{
3342
39.6k
    char szLineBuf[257];
3343
39.6k
    int nCode = 0;
3344
3345
39.6k
    m_oInsertState.m_poTemplateFeature.reset(new OGRDXFFeature(poFeatureDefn));
3346
39.6k
    m_oInsertState.m_oTransformer = OGRDXFInsertTransformer();
3347
39.6k
    m_oInsertState.m_osBlockName.clear();
3348
39.6k
    m_oInsertState.m_nColumnCount = 1;
3349
39.6k
    m_oInsertState.m_nRowCount = 1;
3350
39.6k
    m_oInsertState.m_iCurCol = 0;
3351
39.6k
    m_oInsertState.m_iCurRow = 0;
3352
39.6k
    m_oInsertState.m_dfColumnSpacing = 0.0;
3353
39.6k
    m_oInsertState.m_dfRowSpacing = 0.0;
3354
3355
39.6k
    bool bHasAttribs = false;
3356
39.6k
    m_oInsertState.m_apoAttribs.clear();
3357
39.6k
    m_oInsertState.m_aosAttribs.Clear();
3358
3359
    /* -------------------------------------------------------------------- */
3360
    /*      Process values.                                                 */
3361
    /* -------------------------------------------------------------------- */
3362
243k
    while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3363
203k
    {
3364
203k
        switch (nCode)
3365
203k
        {
3366
8.73k
            case 10:
3367
8.73k
                m_oInsertState.m_oTransformer.dfXOffset = CPLAtof(szLineBuf);
3368
8.73k
                break;
3369
3370
12.5k
            case 20:
3371
12.5k
                m_oInsertState.m_oTransformer.dfYOffset = CPLAtof(szLineBuf);
3372
12.5k
                break;
3373
3374
14.0k
            case 30:
3375
14.0k
                m_oInsertState.m_oTransformer.dfZOffset = CPLAtof(szLineBuf);
3376
14.0k
                break;
3377
3378
9.02k
            case 41:
3379
9.02k
                m_oInsertState.m_oTransformer.dfXScale = CPLAtof(szLineBuf);
3380
9.02k
                break;
3381
3382
1.15k
            case 42:
3383
1.15k
                m_oInsertState.m_oTransformer.dfYScale = CPLAtof(szLineBuf);
3384
1.15k
                break;
3385
3386
3.95k
            case 43:
3387
3.95k
                m_oInsertState.m_oTransformer.dfZScale = CPLAtof(szLineBuf);
3388
3.95k
                break;
3389
3390
2.99k
            case 44:
3391
2.99k
                m_oInsertState.m_dfColumnSpacing = CPLAtof(szLineBuf);
3392
2.99k
                break;
3393
3394
1.39k
            case 45:
3395
1.39k
                m_oInsertState.m_dfRowSpacing = CPLAtof(szLineBuf);
3396
1.39k
                break;
3397
3398
4.81k
            case 50:
3399
                // We want to transform this to radians.
3400
                // It is apparently always in degrees regardless of $AUNITS
3401
4.81k
                m_oInsertState.m_oTransformer.dfAngle =
3402
4.81k
                    CPLAtof(szLineBuf) * M_PI / 180.0;
3403
4.81k
                break;
3404
3405
3.03k
            case 66:
3406
3.03k
                bHasAttribs = atoi(szLineBuf) == 1;
3407
3.03k
                break;
3408
3409
5.11k
            case 70:
3410
5.11k
                m_oInsertState.m_nColumnCount = atoi(szLineBuf);
3411
5.11k
                if (m_oInsertState.m_nColumnCount < 0)
3412
5
                {
3413
5
                    DXF_LAYER_READER_ERROR();
3414
5
                    m_oInsertState.m_nRowCount = 0;
3415
5
                    m_oInsertState.m_nColumnCount = 0;
3416
5
                    return false;
3417
5
                }
3418
5.11k
                break;
3419
3420
7.86k
            case 71:
3421
7.86k
                m_oInsertState.m_nRowCount = atoi(szLineBuf);
3422
7.86k
                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
7.86k
                break;
3430
3431
30.4k
            case 2:
3432
30.4k
                m_oInsertState.m_osBlockName = szLineBuf;
3433
30.4k
                break;
3434
3435
98.1k
            default:
3436
98.1k
                TranslateGenericProperty(
3437
98.1k
                    m_oInsertState.m_poTemplateFeature.get(), nCode, szLineBuf);
3438
98.1k
                break;
3439
203k
        }
3440
203k
    }
3441
39.6k
    if (nCode < 0)
3442
95
    {
3443
95
        DXF_LAYER_READER_ERROR();
3444
95
        m_oInsertState.m_nRowCount = 0;
3445
95
        m_oInsertState.m_nColumnCount = 0;
3446
95
        return false;
3447
95
    }
3448
3449
39.5k
    if (m_oInsertState.m_nRowCount == 0 || m_oInsertState.m_nColumnCount == 0)
3450
1.44k
    {
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.44k
        m_oInsertState.m_nRowCount = 1;
3454
1.44k
        m_oInsertState.m_nColumnCount = 1;
3455
1.44k
    }
3456
3457
    /* -------------------------------------------------------------------- */
3458
    /*      Process any attribute entities.                                 */
3459
    /* -------------------------------------------------------------------- */
3460
3461
39.5k
    if (bHasAttribs)
3462
2
    {
3463
2
        while (nCode == 0 && !EQUAL(szLineBuf, "SEQEND"))
3464
2
        {
3465
2
            if (!EQUAL(szLineBuf, "ATTRIB"))
3466
2
            {
3467
2
                DXF_LAYER_READER_ERROR();
3468
2
                m_oInsertState.m_nRowCount = 0;
3469
2
                m_oInsertState.m_nColumnCount = 0;
3470
2
                return false;
3471
2
            }
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
2
    }
3485
39.5k
    else if (nCode == 0)
3486
39.5k
    {
3487
39.5k
        poDS->UnreadValue();
3488
39.5k
    }
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
39.5k
    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
39.5k
    return true;
3510
39.5k
}
3511
3512
/************************************************************************/
3513
/*                       GenerateINSERTFeatures()                       */
3514
/************************************************************************/
3515
3516
bool OGRDXFLayer::GenerateINSERTFeatures()
3517
39.5k
{
3518
39.5k
    OGRDXFFeature *poFeature =
3519
39.5k
        m_oInsertState.m_poTemplateFeature->CloneDXFFeature();
3520
3521
39.5k
    const double dfExtraXOffset =
3522
39.5k
        m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3523
39.5k
            cos(m_oInsertState.m_oTransformer.dfAngle) +
3524
39.5k
        m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3525
39.5k
            -sin(m_oInsertState.m_oTransformer.dfAngle);
3526
39.5k
    const double dfExtraYOffset =
3527
39.5k
        m_oInsertState.m_iCurCol * m_oInsertState.m_dfColumnSpacing *
3528
39.5k
            sin(m_oInsertState.m_oTransformer.dfAngle) +
3529
39.5k
        m_oInsertState.m_iCurRow * m_oInsertState.m_dfRowSpacing *
3530
39.5k
            cos(m_oInsertState.m_oTransformer.dfAngle);
3531
3532
39.5k
    OGRDXFInsertTransformer oTransformer(m_oInsertState.m_oTransformer);
3533
39.5k
    oTransformer.dfXOffset += dfExtraXOffset;
3534
39.5k
    oTransformer.dfYOffset += dfExtraYOffset;
3535
3536
    // If we are not inlining blocks, just insert a point that refers
3537
    // to this block
3538
39.5k
    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
39.5k
    else
3553
39.5k
    {
3554
39.5k
        OGRDXFFeatureQueue apoExtraFeatures;
3555
39.5k
        try
3556
39.5k
        {
3557
39.5k
            poFeature = InsertBlockInline(
3558
39.5k
                CPLGetErrorCounter(), m_oInsertState.m_osBlockName,
3559
39.5k
                std::move(oTransformer), poFeature, apoExtraFeatures, true,
3560
39.5k
                poDS->ShouldMergeBlockGeometries());
3561
39.5k
        }
3562
39.5k
        catch (const std::invalid_argument &)
3563
39.5k
        {
3564
            // Block doesn't exist
3565
39.5k
            CPLError(CE_Warning, CPLE_AppDefined, "Block %s does not exist",
3566
39.5k
                     m_oInsertState.m_osBlockName.c_str());
3567
39.5k
            delete poFeature;
3568
39.5k
            return false;
3569
39.5k
        }
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
39.5k
}
3603
3604
/************************************************************************/
3605
/*                      GetNextUnfilteredFeature()                      */
3606
/************************************************************************/
3607
3608
OGRDXFFeature *OGRDXFLayer::GetNextUnfilteredFeature()
3609
3610
861k
{
3611
861k
    OGRDXFFeature *poFeature = nullptr;
3612
9.50M
    while (poFeature == nullptr)
3613
8.80M
    {
3614
        /* --------------------------------------------------------------------
3615
         */
3616
        /*      If we have pending features, return one of them. */
3617
        /* --------------------------------------------------------------------
3618
         */
3619
8.80M
        if (!apoPendingFeatures.empty())
3620
150k
        {
3621
150k
            poFeature = apoPendingFeatures.front();
3622
150k
            apoPendingFeatures.pop();
3623
3624
150k
            poFeature->SetFID(iNextFID++);
3625
150k
            return poFeature;
3626
150k
        }
3627
3628
        /* --------------------------------------------------------------------
3629
         */
3630
        /*      Emit INSERT features. */
3631
        /* --------------------------------------------------------------------
3632
         */
3633
8.65M
        if (m_oInsertState.m_iCurRow < m_oInsertState.m_nRowCount)
3634
39.5k
        {
3635
39.5k
            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
39.5k
            if (GenerateINSERTFeatures())
3647
0
            {
3648
0
                m_oInsertState.m_iCurCol++;
3649
0
            }
3650
39.5k
            else
3651
39.5k
            {
3652
39.5k
                m_oInsertState.m_nRowCount = 0;
3653
39.5k
                m_oInsertState.m_nColumnCount = 0;
3654
39.5k
            }
3655
39.5k
            continue;
3656
39.5k
        }
3657
3658
        // read ahead to an entity.
3659
8.61M
        char szLineBuf[257];
3660
8.61M
        int nCode = 0;
3661
21.5M
        while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
3662
12.9M
        {
3663
12.9M
        }
3664
8.61M
        if (nCode < 0)
3665
10.6k
        {
3666
10.6k
            DXF_LAYER_READER_ERROR();
3667
10.6k
            return nullptr;
3668
10.6k
        }
3669
3670
8.60M
        if (EQUAL(szLineBuf, "ENDSEC"))
3671
4
        {
3672
            // CPLDebug( "DXF", "Clean end of features at ENDSEC." );
3673
4
            poDS->UnreadValue();
3674
4
            return nullptr;
3675
4
        }
3676
3677
8.60M
        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.60M
        if (EQUAL(szLineBuf, "POINT"))
3690
11.3k
        {
3691
11.3k
            poFeature = TranslatePOINT();
3692
11.3k
        }
3693
8.58M
        else if (EQUAL(szLineBuf, "MTEXT"))
3694
51.3k
        {
3695
51.3k
            poFeature = TranslateMTEXT();
3696
51.3k
        }
3697
8.53M
        else if (EQUAL(szLineBuf, "TEXT"))
3698
62.6k
        {
3699
62.6k
            poFeature = TranslateTEXT(false);
3700
62.6k
        }
3701
8.47M
        else if (EQUAL(szLineBuf, "ATTDEF"))
3702
30.6k
        {
3703
30.6k
            poFeature = TranslateTEXT(true);
3704
30.6k
        }
3705
8.44M
        else if (EQUAL(szLineBuf, "LINE"))
3706
13.5k
        {
3707
13.5k
            poFeature = TranslateLINE();
3708
13.5k
        }
3709
8.43M
        else if (EQUAL(szLineBuf, "POLYLINE"))
3710
11.8k
        {
3711
11.8k
            poFeature = TranslatePOLYLINE();
3712
11.8k
        }
3713
8.41M
        else if (EQUAL(szLineBuf, "LWPOLYLINE"))
3714
30.6k
        {
3715
30.6k
            poFeature = TranslateLWPOLYLINE();
3716
30.6k
        }
3717
8.38M
        else if (EQUAL(szLineBuf, "MLINE"))
3718
39.0k
        {
3719
39.0k
            poFeature = TranslateMLINE();
3720
39.0k
        }
3721
8.34M
        else if (EQUAL(szLineBuf, "CIRCLE"))
3722
35.0k
        {
3723
35.0k
            poFeature = TranslateCIRCLE();
3724
35.0k
        }
3725
8.31M
        else if (EQUAL(szLineBuf, "ELLIPSE"))
3726
28.9k
        {
3727
28.9k
            poFeature = TranslateELLIPSE();
3728
28.9k
        }
3729
8.28M
        else if (EQUAL(szLineBuf, "ARC"))
3730
26.0k
        {
3731
26.0k
            poFeature = TranslateARC();
3732
26.0k
        }
3733
8.25M
        else if (EQUAL(szLineBuf, "SPLINE") || EQUAL(szLineBuf, "HELIX"))
3734
84.8k
        {
3735
84.8k
            poFeature = TranslateSPLINE();
3736
84.8k
        }
3737
8.17M
        else if (EQUAL(szLineBuf, "3DFACE"))
3738
34.5k
        {
3739
34.5k
            poFeature = Translate3DFACE();
3740
34.5k
        }
3741
8.14M
        else if (EQUAL(szLineBuf, "INSERT"))
3742
39.6k
        {
3743
39.6k
            if (!TranslateINSERT())
3744
103
                return nullptr;
3745
39.6k
        }
3746
8.10M
        else if (EQUAL(szLineBuf, "DIMENSION"))
3747
78.6k
        {
3748
78.6k
            poFeature = TranslateDIMENSION();
3749
78.6k
        }
3750
8.02M
        else if (EQUAL(szLineBuf, "HATCH"))
3751
75.6k
        {
3752
75.6k
            poFeature = TranslateHATCH();
3753
75.6k
        }
3754
7.94M
        else if (EQUAL(szLineBuf, "SOLID") || EQUAL(szLineBuf, "TRACE"))
3755
63.2k
        {
3756
63.2k
            poFeature = TranslateSOLID();
3757
63.2k
        }
3758
7.88M
        else if (EQUAL(szLineBuf, "LEADER"))
3759
66.9k
        {
3760
66.9k
            poFeature = TranslateLEADER();
3761
66.9k
        }
3762
7.81M
        else if (EQUAL(szLineBuf, "MLEADER") || EQUAL(szLineBuf, "MULTILEADER"))
3763
81.6k
        {
3764
81.6k
            poFeature = TranslateMLEADER();
3765
81.6k
        }
3766
7.73M
        else if (EQUAL(szLineBuf, "WIPEOUT"))
3767
4.27k
        {
3768
4.27k
            poFeature = TranslateWIPEOUT();
3769
4.27k
        }
3770
7.73M
        else if (EQUAL(szLineBuf, "3DSOLID") || EQUAL(szLineBuf, "BODY") ||
3771
7.72M
                 EQUAL(szLineBuf, "REGION") || EQUAL(szLineBuf, "SURFACE"))
3772
14.8k
        {
3773
14.8k
            if (poDS->In3DExtensibleMode())
3774
0
            {
3775
0
                poFeature = TranslateASMEntity();
3776
0
            }
3777
14.8k
            else if (oIgnoredEntities.count(szLineBuf) == 0)
3778
516
            {
3779
516
                oIgnoredEntities.insert(szLineBuf);
3780
516
                CPLDebug("DXF", "3D mode is off; ignoring all '%s' entities.",
3781
516
                         szLineBuf);
3782
516
            }
3783
14.8k
        }
3784
7.71M
        else
3785
7.71M
        {
3786
7.71M
            if (oIgnoredEntities.count(szLineBuf) == 0)
3787
257k
            {
3788
257k
                oIgnoredEntities.insert(szLineBuf);
3789
257k
                CPLDebug("DXF", "Ignoring one or more of entity '%s'.",
3790
257k
                         szLineBuf);
3791
257k
            }
3792
7.71M
        }
3793
8.60M
    }
3794
3795
    /* -------------------------------------------------------------------- */
3796
    /*      Set FID.                                                        */
3797
    /* -------------------------------------------------------------------- */
3798
700k
    poFeature->SetFID(iNextFID++);
3799
700k
    m_nFeaturesRead++;
3800
3801
700k
    return poFeature;
3802
861k
}
3803
3804
/************************************************************************/
3805
/*                           GetNextFeature()                           */
3806
/************************************************************************/
3807
3808
OGRFeature *OGRDXFLayer::GetNextFeature()
3809
3810
861k
{
3811
861k
    while (true)
3812
861k
    {
3813
861k
        OGRFeature *poFeature = GetNextUnfilteredFeature();
3814
3815
861k
        if (poFeature == nullptr)
3816
10.7k
            return nullptr;
3817
3818
850k
        if ((m_poFilterGeom == nullptr ||
3819
0
             FilterGeometry(poFeature->GetGeometryRef())) &&
3820
850k
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
3821
850k
        {
3822
850k
            return poFeature;
3823
850k
        }
3824
3825
0
        delete poFeature;
3826
0
    }
3827
861k
}
3828
3829
/************************************************************************/
3830
/*                           TestCapability()                           */
3831
/************************************************************************/
3832
3833
int 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
}