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/ogrdxf_feature.cpp
Line
Count
Source
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(const OGRFeatureDefn *poFeatureDefn)
21
1.13M
    : OGRFeature(poFeatureDefn), oOCS(0.0, 0.0, 1.0), bIsBlockReference(false),
22
1.13M
      dfBlockAngle(0.0), oBlockScale(1.0, 1.0, 1.0),
23
1.13M
      oOriginalCoords(0.0, 0.0, 0.0)
24
1.13M
{
25
1.13M
}
26
27
1.13M
OGRDXFFeature::~OGRDXFFeature() = default;
28
29
/************************************************************************/
30
/*                          CloneDXFFeature()                           */
31
/*                                                                      */
32
/*      Replacement for OGRFeature::Clone() for DXF features.           */
33
/************************************************************************/
34
35
OGRDXFFeature *OGRDXFFeature::CloneDXFFeature()
36
268k
{
37
268k
    OGRDXFFeature *poNew = new OGRDXFFeature(GetDefnRef());
38
268k
    if (poNew == nullptr)
39
0
        return nullptr;
40
41
268k
    if (!CopySelfTo(poNew))
42
0
    {
43
0
        delete poNew;
44
0
        return nullptr;
45
0
    }
46
47
268k
    poNew->oOCS = oOCS;
48
268k
    poNew->bIsBlockReference = bIsBlockReference;
49
268k
    poNew->osBlockName = osBlockName;
50
268k
    poNew->dfBlockAngle = dfBlockAngle;
51
268k
    poNew->oBlockScale = oBlockScale;
52
268k
    poNew->oOriginalCoords = oOriginalCoords;
53
268k
    poNew->osAttributeTag = osAttributeTag;
54
268k
    poNew->oStyleProperties = oStyleProperties;
55
56
268k
    if (poASMTransform)
57
0
    {
58
0
        poNew->poASMTransform = std::unique_ptr<OGRDXFAffineTransform>(
59
0
            new OGRDXFAffineTransform(*poASMTransform));
60
0
    }
61
62
268k
    for (const std::unique_ptr<OGRDXFFeature> &poAttribFeature :
63
268k
         apoAttribFeatures)
64
0
    {
65
0
        poNew->apoAttribFeatures.emplace_back(
66
0
            poAttribFeature->CloneDXFFeature());
67
0
    }
68
69
268k
    return poNew;
70
268k
}
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
406k
{
81
406k
    if (poGeometry == nullptr)
82
0
        return;
83
84
406k
    double adfN[3];
85
406k
    oOCS.ToArray(adfN);
86
87
406k
    OGRDXFOCSTransformer oTransformer(adfN);
88
89
    // Promote to 3D, in case the OCS transformation introduces a
90
    // third dimension to the geometry.
91
406k
    const bool bInitially2D = !poGeometry->Is3D();
92
406k
    if (bInitially2D)
93
230k
        poGeometry->set3D(TRUE);
94
95
406k
    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
406k
    if (bInitially2D)
100
230k
    {
101
230k
        OGREnvelope3D oEnvelope;
102
230k
        poGeometry->getEnvelope(&oEnvelope);
103
230k
        if (oEnvelope.MaxZ == 0.0 && oEnvelope.MinZ == 0.0)
104
221k
            poGeometry->flattenTo2D();
105
230k
    }
106
406k
}
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
850k
{
139
850k
    CPLString osLayer = GetFieldAsString("Layer");
140
141
    /* -------------------------------------------------------------------- */
142
    /*      Is the layer or object hidden/off (1) or frozen (2)?            */
143
    /* -------------------------------------------------------------------- */
144
145
850k
    int iHidden = 0;
146
147
850k
    if (oStyleProperties.count("Hidden") > 0 ||
148
794k
        (poBlockFeature &&
149
139k
         poBlockFeature->oStyleProperties.count("Hidden") > 0))
150
56.1k
    {
151
        // Hidden objects should never be shown no matter what happens
152
56.1k
        iHidden = 1;
153
56.1k
        oStyleProperties["Hidden"] = "1";
154
56.1k
    }
155
794k
    else
156
794k
    {
157
794k
        auto osHidden = poDS->LookupLayerProperty(osLayer, "Hidden");
158
794k
        if (osHidden)
159
0
            iHidden = atoi(osHidden->c_str());
160
161
        // Is the block feature on a frozen layer? If so, hide this feature
162
794k
        if (!iHidden && poBlockFeature)
163
139k
        {
164
139k
            const CPLString osBlockLayer =
165
139k
                poBlockFeature->GetFieldAsString("Layer");
166
139k
            auto osBlockHidden =
167
139k
                poDS->LookupLayerProperty(osBlockLayer, "Hidden");
168
139k
            if (osBlockHidden && atoi(osBlockHidden->c_str()) == 2)
169
0
                iHidden = 2;
170
139k
        }
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
794k
        if (iHidden == 2 && !EQUAL(GetFieldAsString("Layer"), "0"))
176
0
            oStyleProperties["Hidden"] = "1";
177
794k
    }
178
179
    // Helpful constants
180
850k
    const int C_BYLAYER = 256;
181
850k
    const int C_BYBLOCK = 0;
182
850k
    const int C_TRUECOLOR = -100;  // not used in DXF - for our purposes only
183
850k
    const int C_BYLAYER_FORCE0 =
184
850k
        -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
850k
    int nColor = C_BYLAYER;
192
850k
    unsigned int nTrueColor = 0;
193
194
850k
    if (oStyleProperties.count("TrueColor") > 0)
195
17.5k
    {
196
17.5k
        nTrueColor = atoi(oStyleProperties["TrueColor"]);
197
17.5k
        nColor = C_TRUECOLOR;
198
17.5k
    }
199
833k
    else if (oStyleProperties.count("Color") > 0)
200
199k
    {
201
199k
        nColor = atoi(oStyleProperties["Color"]);
202
199k
    }
203
204
850k
    const unsigned char byColorMethod = (nColor & 0xFF000000) >> 24;
205
850k
    switch (byColorMethod)
206
850k
    {
207
        // ByLayer
208
0
        case 0xC0:
209
0
            nColor = C_BYLAYER;
210
0
            break;
211
212
        // ByBlock
213
0
        case 0xC1:
214
0
            nColor = C_BYBLOCK;
215
0
            break;
216
217
        // RGB true color
218
0
        case 0xC2:
219
0
            nTrueColor = nColor & 0xFFFFFF;
220
0
            nColor = C_TRUECOLOR;
221
0
            break;
222
223
        // Indexed color
224
7
        case 0xC3:
225
7
            nColor &= 0xFF;
226
7
            break;
227
850k
    }
228
229
    /* -------------------------------------------------------------------- */
230
    /*      Work out the indexed color for this feature.                    */
231
    /* -------------------------------------------------------------------- */
232
233
    // Use ByBlock color?
234
850k
    if (nColor == C_BYBLOCK && poBlockFeature)
235
131k
    {
236
131k
        if (poBlockFeature->oStyleProperties.count("TrueColor") > 0)
237
0
        {
238
            // Inherit true color from the owning block
239
0
            nTrueColor = atoi(poBlockFeature->oStyleProperties["TrueColor"]);
240
0
            nColor = C_TRUECOLOR;
241
242
            // Use the inherited color if we regenerate the style string
243
            // again during block insertion
244
0
            oStyleProperties["TrueColor"] =
245
0
                poBlockFeature->oStyleProperties["TrueColor"];
246
0
        }
247
131k
        else if (poBlockFeature->oStyleProperties.count("Color") > 0)
248
14.8k
        {
249
            // Inherit color from the owning block
250
14.8k
            nColor = atoi(poBlockFeature->oStyleProperties["Color"]);
251
252
            // Use the inherited color if we regenerate the style string
253
            // again during block insertion
254
14.8k
            oStyleProperties["Color"] =
255
14.8k
                poBlockFeature->oStyleProperties["Color"];
256
14.8k
        }
257
116k
        else
258
116k
        {
259
            // If the owning block has no explicit color, assume ByLayer,
260
            // but take the color from the owning block's layer
261
116k
            nColor = C_BYLAYER;
262
116k
            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
116k
            oStyleProperties["Color"] = std::to_string(C_BYLAYER_FORCE0);
268
116k
        }
269
131k
    }
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
850k
    if (nColor == C_BYLAYER_FORCE0)
282
0
    {
283
0
        if (poBlockFeature)
284
0
            osLayer = poBlockFeature->GetFieldAsString("Layer");
285
0
        else
286
0
            osLayer = "0";
287
288
0
        nColor = C_BYLAYER;
289
0
    }
290
291
    // Use layer color?
292
850k
    if (nColor == C_BYLAYER)
293
750k
    {
294
750k
        auto osTrueColor = poDS->LookupLayerProperty(osLayer, "TrueColor");
295
750k
        if (osTrueColor)
296
0
        {
297
0
            nTrueColor = atoi(osTrueColor->c_str());
298
0
            nColor = C_TRUECOLOR;
299
300
0
            if (poBlockFeature && osLayer != "0")
301
0
            {
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
0
                oStyleProperties["TrueColor"] = *osTrueColor;
306
0
            }
307
0
        }
308
750k
        else
309
750k
        {
310
750k
            auto osColor = poDS->LookupLayerProperty(osLayer, "Color");
311
750k
            if (osColor)
312
0
            {
313
0
                nColor = atoi(osColor->c_str());
314
315
0
                if (poBlockFeature && osLayer != "0")
316
0
                {
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
0
                    oStyleProperties["Color"] = *osColor;
321
0
                }
322
0
            }
323
750k
        }
324
750k
    }
325
326
    // If no color is available, use the default black/white color
327
850k
    if (nColor != C_TRUECOLOR && (nColor < 1 || nColor > 255))
328
783k
        nColor = 7;
329
330
    /* -------------------------------------------------------------------- */
331
    /*      Translate the DWG/DXF color index to a hex color string.        */
332
    /* -------------------------------------------------------------------- */
333
334
850k
    CPLString osResult;
335
336
850k
    if (nColor == C_TRUECOLOR)
337
17.5k
    {
338
17.5k
        osResult.Printf("#%06x", nTrueColor);
339
17.5k
    }
340
833k
    else
341
833k
    {
342
833k
        const unsigned char *pabyDXFColors = ACGetColorTable();
343
344
833k
        osResult.Printf("#%02x%02x%02x", pabyDXFColors[nColor * 3 + 0],
345
833k
                        pabyDXFColors[nColor * 3 + 1],
346
833k
                        pabyDXFColors[nColor * 3 + 2]);
347
833k
    }
348
349
850k
    if (iHidden)
350
56.1k
        osResult += "00";
351
794k
    else
352
794k
    {
353
794k
        int nOpacity = -1;
354
355
794k
        if (oStyleProperties.count("Transparency") > 0)
356
2.80k
        {
357
2.80k
            int nTransparency = atoi(oStyleProperties["Transparency"]);
358
2.80k
            if ((nTransparency & 0x02000000) != 0)
359
16
            {
360
16
                nOpacity = nTransparency & 0xFF;
361
16
            }
362
2.79k
            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
2.80k
        }
378
791k
        else
379
791k
        {
380
791k
            auto osTransparency =
381
791k
                poDS->LookupLayerProperty(osLayer, "Transparency");
382
791k
            if (osTransparency)
383
0
            {
384
0
                nOpacity = atoi(osTransparency->c_str()) & 0xFF;
385
386
0
                if (poBlockFeature && osLayer != "0")
387
0
                {
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
0
                    oStyleProperties["Transparency"] = *osTransparency;
392
0
                }
393
0
            }
394
791k
        }
395
396
794k
        if (nOpacity >= 0)
397
16
            osResult += CPLSPrintf("%02x", nOpacity & 0xFF);
398
794k
    }
399
400
850k
    return osResult;
401
850k
}