Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_feature.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  DXF Translator
4
 * Purpose:  Provides additional functionality for DXF features
5
 * Author:   Alan Thomas, alant@outlook.com.au
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2017, Alan Thomas <alant@outlook.com.au>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_dxf.h"
14
#include "cpl_string.h"
15
16
/************************************************************************/
17
/*                            OGRDXFFeature()                           */
18
/************************************************************************/
19
20
OGRDXFFeature::OGRDXFFeature(OGRFeatureDefn *poFeatureDefn)
21
27.9M
    : OGRFeature(poFeatureDefn), oOCS(0.0, 0.0, 1.0), bIsBlockReference(false),
22
27.9M
      dfBlockAngle(0.0), oBlockScale(1.0, 1.0, 1.0),
23
27.9M
      oOriginalCoords(0.0, 0.0, 0.0)
24
27.9M
{
25
27.9M
}
26
27
27.9M
OGRDXFFeature::~OGRDXFFeature() = default;
28
29
/************************************************************************/
30
/*                          CloneDXFFeature()                           */
31
/*                                                                      */
32
/*      Replacement for OGRFeature::Clone() for DXF features.           */
33
/************************************************************************/
34
35
OGRDXFFeature *OGRDXFFeature::CloneDXFFeature()
36
26.9M
{
37
26.9M
    OGRDXFFeature *poNew = new OGRDXFFeature(GetDefnRef());
38
26.9M
    if (poNew == nullptr)
39
0
        return nullptr;
40
41
26.9M
    if (!CopySelfTo(poNew))
42
0
    {
43
0
        delete poNew;
44
0
        return nullptr;
45
0
    }
46
47
26.9M
    poNew->oOCS = oOCS;
48
26.9M
    poNew->bIsBlockReference = bIsBlockReference;
49
26.9M
    poNew->osBlockName = osBlockName;
50
26.9M
    poNew->dfBlockAngle = dfBlockAngle;
51
26.9M
    poNew->oBlockScale = oBlockScale;
52
26.9M
    poNew->oOriginalCoords = oOriginalCoords;
53
26.9M
    poNew->osAttributeTag = osAttributeTag;
54
26.9M
    poNew->oStyleProperties = oStyleProperties;
55
56
26.9M
    if (poASMTransform)
57
0
    {
58
0
        poNew->poASMTransform = std::unique_ptr<OGRDXFAffineTransform>(
59
0
            new OGRDXFAffineTransform(*poASMTransform));
60
0
    }
61
62
26.9M
    for (const std::unique_ptr<OGRDXFFeature> &poAttribFeature :
63
26.9M
         apoAttribFeatures)
64
6.99k
    {
65
6.99k
        poNew->apoAttribFeatures.emplace_back(
66
6.99k
            poAttribFeature->CloneDXFFeature());
67
6.99k
    }
68
69
26.9M
    return poNew;
70
26.9M
}
71
72
/************************************************************************/
73
/*                        ApplyOCSTransformer()                         */
74
/*                                                                      */
75
/*      Applies the OCS transformation stored in this feature to        */
76
/*      the specified geometry.                                         */
77
/************************************************************************/
78
79
void OGRDXFFeature::ApplyOCSTransformer(OGRGeometry *const poGeometry) const
80
24.2M
{
81
24.2M
    if (poGeometry == nullptr)
82
0
        return;
83
84
24.2M
    double adfN[3];
85
24.2M
    oOCS.ToArray(adfN);
86
87
24.2M
    OGRDXFOCSTransformer oTransformer(adfN);
88
89
    // Promote to 3D, in case the OCS transformation introduces a
90
    // third dimension to the geometry.
91
24.2M
    const bool bInitially2D = !poGeometry->Is3D();
92
24.2M
    if (bInitially2D)
93
11.0M
        poGeometry->set3D(TRUE);
94
95
24.2M
    poGeometry->transform(&oTransformer);
96
97
    // If the geometry was 2D to begin with, and is still 2D after the
98
    // OCS transformation, flatten it back to 2D.
99
24.2M
    if (bInitially2D)
100
11.0M
    {
101
11.0M
        OGREnvelope3D oEnvelope;
102
11.0M
        poGeometry->getEnvelope(&oEnvelope);
103
11.0M
        if (oEnvelope.MaxZ == 0.0 && oEnvelope.MinZ == 0.0)
104
8.45M
            poGeometry->flattenTo2D();
105
11.0M
    }
106
24.2M
}
107
108
/************************************************************************/
109
/*                        ApplyOCSTransformer()                         */
110
/************************************************************************/
111
112
void OGRDXFFeature::ApplyOCSTransformer(OGRDXFAffineTransform *const poCT) const
113
0
{
114
0
    if (!poCT)
115
0
        return;
116
117
0
    double adfN[3];
118
0
    oOCS.ToArray(adfN);
119
120
0
    OGRDXFOCSTransformer oTransformer(adfN);
121
122
0
    oTransformer.ComposeOnto(*poCT);
123
0
}
124
125
/************************************************************************/
126
/*                              GetColor()                              */
127
/*                                                                      */
128
/*      Gets the hex color string for this feature, using the given     */
129
/*      data source to fetch layer properties.                          */
130
/*                                                                      */
131
/*      For usage info about poBlockFeature, see                        */
132
/*      OGRDXFLayer::PrepareFeatureStyle.                               */
133
/************************************************************************/
134
135
const CPLString
136
OGRDXFFeature::GetColor(OGRDXFDataSource *const poDS,
137
                        OGRDXFFeature *const poBlockFeature /* = NULL */)
138
11.6M
{
139
11.6M
    CPLString osLayer = GetFieldAsString("Layer");
140
141
    /* -------------------------------------------------------------------- */
142
    /*      Is the layer or object hidden/off (1) or frozen (2)?            */
143
    /* -------------------------------------------------------------------- */
144
145
11.6M
    int iHidden = 0;
146
147
11.6M
    if (oStyleProperties.count("Hidden") > 0 ||
148
11.6M
        (poBlockFeature &&
149
10.7M
         poBlockFeature->oStyleProperties.count("Hidden") > 0))
150
1.01M
    {
151
        // Hidden objects should never be shown no matter what happens
152
1.01M
        iHidden = 1;
153
1.01M
        oStyleProperties["Hidden"] = "1";
154
1.01M
    }
155
10.5M
    else
156
10.5M
    {
157
10.5M
        auto osHidden = poDS->LookupLayerProperty(osLayer, "Hidden");
158
10.5M
        if (osHidden)
159
57.6k
            iHidden = atoi(osHidden->c_str());
160
161
        // Is the block feature on a frozen layer? If so, hide this feature
162
10.5M
        if (!iHidden && poBlockFeature)
163
7.56M
        {
164
7.56M
            const CPLString osBlockLayer =
165
7.56M
                poBlockFeature->GetFieldAsString("Layer");
166
7.56M
            auto osBlockHidden =
167
7.56M
                poDS->LookupLayerProperty(osBlockLayer, "Hidden");
168
7.56M
            if (osBlockHidden && atoi(osBlockHidden->c_str()) == 2)
169
1.67k
                iHidden = 2;
170
7.56M
        }
171
172
        // If this feature is on a frozen layer (other than layer 0), make the
173
        // object totally hidden so it won't reappear if we regenerate the style
174
        // string again during block insertion
175
10.5M
        if (iHidden == 2 && !EQUAL(GetFieldAsString("Layer"), "0"))
176
3.40k
            oStyleProperties["Hidden"] = "1";
177
10.5M
    }
178
179
    // Helpful constants
180
11.6M
    const int C_BYLAYER = 256;
181
11.6M
    const int C_BYBLOCK = 0;
182
11.6M
    const int C_TRUECOLOR = -100;  // not used in DXF - for our purposes only
183
11.6M
    const int C_BYLAYER_FORCE0 =
184
11.6M
        -101;  // not used in DXF - for our purposes only
185
186
    /* -------------------------------------------------------------------- */
187
    /*      MULTILEADER entities store colors by directly outputting        */
188
    /*      the AcCmEntityColor struct as a 32-bit integer.                 */
189
    /* -------------------------------------------------------------------- */
190
191
11.6M
    int nColor = C_BYLAYER;
192
11.6M
    unsigned int nTrueColor = 0;
193
194
11.6M
    if (oStyleProperties.count("TrueColor") > 0)
195
30.2k
    {
196
30.2k
        nTrueColor = atoi(oStyleProperties["TrueColor"]);
197
30.2k
        nColor = C_TRUECOLOR;
198
30.2k
    }
199
11.5M
    else if (oStyleProperties.count("Color") > 0)
200
557k
    {
201
557k
        nColor = atoi(oStyleProperties["Color"]);
202
557k
    }
203
204
11.6M
    const unsigned char byColorMethod = (nColor & 0xFF000000) >> 24;
205
11.6M
    switch (byColorMethod)
206
11.6M
    {
207
        // ByLayer
208
167
        case 0xC0:
209
167
            nColor = C_BYLAYER;
210
167
            break;
211
212
        // ByBlock
213
482
        case 0xC1:
214
482
            nColor = C_BYBLOCK;
215
482
            break;
216
217
        // RGB true color
218
358
        case 0xC2:
219
358
            nTrueColor = nColor & 0xFFFFFF;
220
358
            nColor = C_TRUECOLOR;
221
358
            break;
222
223
        // Indexed color
224
649
        case 0xC3:
225
649
            nColor &= 0xFF;
226
649
            break;
227
11.6M
    }
228
229
    /* -------------------------------------------------------------------- */
230
    /*      Work out the indexed color for this feature.                    */
231
    /* -------------------------------------------------------------------- */
232
233
    // Use ByBlock color?
234
11.6M
    if (nColor == C_BYBLOCK && poBlockFeature)
235
176k
    {
236
176k
        if (poBlockFeature->oStyleProperties.count("TrueColor") > 0)
237
834
        {
238
            // Inherit true color from the owning block
239
834
            nTrueColor = atoi(poBlockFeature->oStyleProperties["TrueColor"]);
240
834
            nColor = C_TRUECOLOR;
241
242
            // Use the inherited color if we regenerate the style string
243
            // again during block insertion
244
834
            oStyleProperties["TrueColor"] =
245
834
                poBlockFeature->oStyleProperties["TrueColor"];
246
834
        }
247
175k
        else if (poBlockFeature->oStyleProperties.count("Color") > 0)
248
15.9k
        {
249
            // Inherit color from the owning block
250
15.9k
            nColor = atoi(poBlockFeature->oStyleProperties["Color"]);
251
252
            // Use the inherited color if we regenerate the style string
253
            // again during block insertion
254
15.9k
            oStyleProperties["Color"] =
255
15.9k
                poBlockFeature->oStyleProperties["Color"];
256
15.9k
        }
257
159k
        else
258
159k
        {
259
            // If the owning block has no explicit color, assume ByLayer,
260
            // but take the color from the owning block's layer
261
159k
            nColor = C_BYLAYER;
262
159k
            osLayer = poBlockFeature->GetFieldAsString("Layer");
263
264
            // If we regenerate the style string again during
265
            // block insertion, treat as ByLayer, but when
266
            // not in block insertion, treat as layer 0
267
159k
            oStyleProperties["Color"] = std::to_string(C_BYLAYER_FORCE0);
268
159k
        }
269
176k
    }
270
271
    // Strange special case: consider the following scenario:
272
    //
273
    //                             Block  Color    Layer
274
    //                             -----  -------  -------
275
    //  Drawing contains:  INSERT  BLK1   ByBlock  MYLAYER
276
    //     BLK1 contains:  INSERT  BLK2   ByLayer  0
277
    //     BLK2 contains:  LINE           ByBlock  0
278
    //
279
    // When viewing the drawing, the line is displayed in
280
    // MYLAYER's layer colour, not black as might be expected.
281
11.6M
    if (nColor == C_BYLAYER_FORCE0)
282
301k
    {
283
301k
        if (poBlockFeature)
284
301k
            osLayer = poBlockFeature->GetFieldAsString("Layer");
285
1
        else
286
1
            osLayer = "0";
287
288
301k
        nColor = C_BYLAYER;
289
301k
    }
290
291
    // Use layer color?
292
11.6M
    if (nColor == C_BYLAYER)
293
11.4M
    {
294
11.4M
        auto osTrueColor = poDS->LookupLayerProperty(osLayer, "TrueColor");
295
11.4M
        if (osTrueColor)
296
12.5k
        {
297
12.5k
            nTrueColor = atoi(osTrueColor->c_str());
298
12.5k
            nColor = C_TRUECOLOR;
299
300
12.5k
            if (poBlockFeature && osLayer != "0")
301
8.05k
            {
302
                // Use the inherited color if we regenerate the style string
303
                // again during block insertion (except when the entity is
304
                // on layer 0)
305
8.05k
                oStyleProperties["TrueColor"] = *osTrueColor;
306
8.05k
            }
307
12.5k
        }
308
11.4M
        else
309
11.4M
        {
310
11.4M
            auto osColor = poDS->LookupLayerProperty(osLayer, "Color");
311
11.4M
            if (osColor)
312
3.83k
            {
313
3.83k
                nColor = atoi(osColor->c_str());
314
315
3.83k
                if (poBlockFeature && osLayer != "0")
316
2.00k
                {
317
                    // Use the inherited color if we regenerate the style string
318
                    // again during block insertion (except when the entity is
319
                    // on layer 0)
320
2.00k
                    oStyleProperties["Color"] = *osColor;
321
2.00k
                }
322
3.83k
            }
323
11.4M
        }
324
11.4M
    }
325
326
    // If no color is available, use the default black/white color
327
11.6M
    if (nColor != C_TRUECOLOR && (nColor < 1 || nColor > 255))
328
11.5M
        nColor = 7;
329
330
    /* -------------------------------------------------------------------- */
331
    /*      Translate the DWG/DXF color index to a hex color string.        */
332
    /* -------------------------------------------------------------------- */
333
334
11.6M
    CPLString osResult;
335
336
11.6M
    if (nColor == C_TRUECOLOR)
337
44.0k
    {
338
44.0k
        osResult.Printf("#%06x", nTrueColor);
339
44.0k
    }
340
11.5M
    else
341
11.5M
    {
342
11.5M
        const unsigned char *pabyDXFColors = ACGetColorTable();
343
344
11.5M
        osResult.Printf("#%02x%02x%02x", pabyDXFColors[nColor * 3 + 0],
345
11.5M
                        pabyDXFColors[nColor * 3 + 1],
346
11.5M
                        pabyDXFColors[nColor * 3 + 2]);
347
11.5M
    }
348
349
11.6M
    if (iHidden)
350
1.03M
        osResult += "00";
351
10.5M
    else
352
10.5M
    {
353
10.5M
        int nOpacity = -1;
354
355
10.5M
        if (oStyleProperties.count("Transparency") > 0)
356
5.02k
        {
357
5.02k
            int nTransparency = atoi(oStyleProperties["Transparency"]);
358
5.02k
            if ((nTransparency & 0x02000000) != 0)
359
1.28k
            {
360
1.28k
                nOpacity = nTransparency & 0xFF;
361
1.28k
            }
362
3.73k
            else if ((nTransparency & 0x01000000) != 0)  // By block ?
363
0
            {
364
0
                if (poBlockFeature &&
365
0
                    poBlockFeature->oStyleProperties.count("Transparency") > 0)
366
0
                {
367
0
                    nOpacity =
368
0
                        atoi(poBlockFeature->oStyleProperties["Transparency"]) &
369
0
                        0xFF;
370
371
                    // Use the inherited transparency if we regenerate the style string
372
                    // again during block insertion
373
0
                    oStyleProperties["Transparency"] =
374
0
                        poBlockFeature->oStyleProperties["Transparency"];
375
0
                }
376
0
            }
377
5.02k
        }
378
10.5M
        else
379
10.5M
        {
380
10.5M
            auto osTransparency =
381
10.5M
                poDS->LookupLayerProperty(osLayer, "Transparency");
382
10.5M
            if (osTransparency)
383
2.04k
            {
384
2.04k
                nOpacity = atoi(osTransparency->c_str()) & 0xFF;
385
386
2.04k
                if (poBlockFeature && osLayer != "0")
387
1.05k
                {
388
                    // Use the inherited transparency if we regenerate the style string
389
                    // again during block insertion (except when the entity is
390
                    // on layer 0)
391
1.05k
                    oStyleProperties["Transparency"] = *osTransparency;
392
1.05k
                }
393
2.04k
            }
394
10.5M
        }
395
396
10.5M
        if (nOpacity >= 0)
397
3.32k
            osResult += CPLSPrintf("%02x", nOpacity & 0xFF);
398
10.5M
    }
399
400
11.6M
    return osResult;
401
11.6M
}