Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_blockmap.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  DXF Translator
4
 * Purpose:  Implements BlockMap reading and management portion of
5
 *           OGRDXFDataSource class
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ogr_dxf.h"
15
#include "cpl_conv.h"
16
#include "cpl_string.h"
17
#include "cpl_csv.h"
18
19
#include <algorithm>
20
21
/************************************************************************/
22
/*                          ReadBlockSection()                          */
23
/************************************************************************/
24
25
bool OGRDXFDataSource::ReadBlocksSection()
26
27
5.77k
{
28
    // Force inlining of blocks to false, for when OGRDXFLayer processes
29
    // INSERT entities
30
5.77k
    const bool bOldInlineBlocks = bInlineBlocks;
31
5.77k
    bInlineBlocks = false;
32
33
5.77k
    OGRDXFLayer *poReaderLayer =
34
5.77k
        cpl::down_cast<OGRDXFLayer *>(GetLayerByName("Entities"));
35
36
5.77k
    iEntitiesOffset = poReader->GetCurrentFilePos();
37
5.77k
    iEntitiesLineNumber = poReader->nLineNumber;
38
39
5.77k
    char szLineBuf[257];
40
5.77k
    int nCode = 0;
41
38.0k
    while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 &&
42
35.1k
           !EQUAL(szLineBuf, "ENDSEC"))
43
32.5k
    {
44
        // We are only interested in extracting blocks.
45
32.5k
        if (nCode != 0 || !EQUAL(szLineBuf, "BLOCK"))
46
20.3k
            continue;
47
48
        // Process contents of BLOCK definition till we find the
49
        // first entity.
50
12.1k
        CPLString osBlockName;
51
12.1k
        CPLString osBlockRecordHandle;
52
12.1k
        OGRDXFInsertTransformer oBasePointTransformer;
53
54
30.3k
        while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
55
18.1k
        {
56
18.1k
            switch (nCode)
57
18.1k
            {
58
7.07k
                case 2:
59
7.07k
                    osBlockName = szLineBuf;
60
7.07k
                    break;
61
62
2.60k
                case 330:
63
                    // get the block record handle as well, for arrowheads
64
2.60k
                    osBlockRecordHandle = szLineBuf;
65
2.60k
                    break;
66
67
892
                case 10:
68
892
                    oBasePointTransformer.dfXOffset = -CPLAtof(szLineBuf);
69
892
                    break;
70
71
1.23k
                case 20:
72
1.23k
                    oBasePointTransformer.dfYOffset = -CPLAtof(szLineBuf);
73
1.23k
                    break;
74
75
486
                case 30:
76
486
                    oBasePointTransformer.dfZOffset = -CPLAtof(szLineBuf);
77
486
                    break;
78
18.1k
            }
79
18.1k
        }
80
12.1k
        if (nCode < 0)
81
186
        {
82
186
            bInlineBlocks = bOldInlineBlocks;
83
186
            DXF_READER_ERROR();
84
186
            return false;
85
186
        }
86
87
        // store the block record handle mapping even if the block is empty
88
11.9k
        oBlockRecordHandles[osBlockRecordHandle] = osBlockName;
89
90
11.9k
        if (EQUAL(szLineBuf, "ENDBLK"))
91
587
            continue;
92
93
11.3k
        UnreadValue();
94
95
11.3k
        if (oBlockMap.find(osBlockName) != oBlockMap.end())
96
22
        {
97
22
            bInlineBlocks = bOldInlineBlocks;
98
22
            DXF_READER_ERROR();
99
22
            return false;
100
22
        }
101
102
        // Now we will process entities till we run out at the ENDBLK code.
103
104
11.3k
        PushBlockInsertion(osBlockName);
105
106
11.3k
        OGRDXFFeature *poFeature = nullptr;
107
11.3k
        int nIters = 0;
108
11.3k
        const int nMaxIters =
109
11.3k
            atoi(CPLGetConfigOption("DXF_FEATURE_LIMIT_PER_BLOCK", "10000"));
110
6.39M
        while ((poFeature = poReaderLayer->GetNextUnfilteredFeature()) !=
111
6.39M
               nullptr)
112
6.38M
        {
113
6.38M
            if (nMaxIters >= 0 && nIters == nMaxIters)
114
451
            {
115
451
                delete poFeature;
116
451
                CPLError(CE_Warning, CPLE_AppDefined,
117
451
                         "Limit of %d features for block %s reached. "
118
451
                         "If you need more, set the "
119
451
                         "DXF_FEATURE_LIMIT_PER_BLOCK configuration "
120
451
                         "option to the maximum value (or -1 for no limit)",
121
451
                         nMaxIters, osBlockName.c_str());
122
451
                break;
123
451
            }
124
125
            // Apply the base point translation
126
6.38M
            OGRGeometry *poFeatureGeom = poFeature->GetGeometryRef();
127
6.38M
            if (poFeatureGeom)
128
6.38M
                poFeatureGeom->transform(&oBasePointTransformer);
129
130
            // Also apply the base point translation to the original
131
            // coordinates of block references
132
6.38M
            if (poFeature->IsBlockReference())
133
6.01M
            {
134
6.01M
                DXFTriple oTriple = poFeature->GetInsertOCSCoords();
135
6.01M
                OGRPoint oPoint(oTriple.dfX, oTriple.dfY, oTriple.dfZ);
136
6.01M
                oPoint.transform(&oBasePointTransformer);
137
6.01M
                poFeature->SetInsertOCSCoords(
138
6.01M
                    DXFTriple(oPoint.getX(), oPoint.getY(), oPoint.getZ()));
139
6.01M
            }
140
141
6.38M
            oBlockMap[osBlockName].apoFeatures.push_back(poFeature);
142
6.38M
            nIters++;
143
6.38M
        }
144
145
11.3k
        PopBlockInsertion();
146
11.3k
    }
147
5.56k
    if (nCode < 0)
148
2.91k
    {
149
2.91k
        bInlineBlocks = bOldInlineBlocks;
150
2.91k
        DXF_READER_ERROR();
151
2.91k
        return false;
152
2.91k
    }
153
154
2.65k
    CPLDebug("DXF", "Read %d blocks with meaningful geometry.",
155
2.65k
             (int)oBlockMap.size());
156
157
    // Restore old inline blocks setting
158
2.65k
    bInlineBlocks = bOldInlineBlocks;
159
160
2.65k
    return true;
161
5.56k
}
162
163
/************************************************************************/
164
/*                            LookupBlock()                             */
165
/*                                                                      */
166
/*      Find the geometry collection corresponding to a name if it      */
167
/*      exists.  Note that the returned geometry pointer is to a        */
168
/*      geometry that continues to be owned by the datasource.  It      */
169
/*      should be cloned for use.                                       */
170
/************************************************************************/
171
172
DXFBlockDefinition *OGRDXFDataSource::LookupBlock(const char *pszName)
173
174
4.06M
{
175
4.06M
    const CPLString l_osName(pszName);
176
177
4.06M
    if (oBlockMap.count(l_osName) == 0)
178
324k
        return nullptr;
179
3.74M
    else
180
3.74M
        return &(oBlockMap[l_osName]);
181
4.06M
}
182
183
/************************************************************************/
184
/*                     GetBlockNameByRecordHandle()                     */
185
/*                                                                      */
186
/*      Find the name of the block with the given BLOCK_RECORD handle.  */
187
/*      If there is no such block, an empty string is returned.         */
188
/************************************************************************/
189
190
CPLString OGRDXFDataSource::GetBlockNameByRecordHandle(const char *pszID)
191
192
10.0k
{
193
10.0k
    const CPLString l_osID(pszID);
194
195
10.0k
    if (oBlockRecordHandles.count(l_osID) == 0)
196
5.92k
        return "";
197
4.09k
    else
198
4.09k
        return oBlockRecordHandles[l_osID];
199
10.0k
}
200
201
/************************************************************************/
202
/*                         PushBlockInsertion()                         */
203
/*                                                                      */
204
/*      Add a block name to the stack of blocks being inserted.         */
205
/*      Returns false if we are already inserting this block.           */
206
/************************************************************************/
207
208
bool OGRDXFDataSource::PushBlockInsertion(const CPLString &osBlockName)
209
210
13.9M
{
211
    // Make sure we are not recursing too deeply (avoid stack overflows) or
212
    // inserting a block within itself (avoid billion-laughs type issues).
213
    // 128 is a totally arbitrary limit
214
13.9M
    if (aosBlockInsertionStack.size() > 128 ||
215
13.9M
        std::find(aosBlockInsertionStack.begin(), aosBlockInsertionStack.end(),
216
13.9M
                  osBlockName) != aosBlockInsertionStack.end())
217
9.88M
    {
218
9.88M
        CPLError(CE_Warning, CPLE_AppDefined,
219
9.88M
                 "Dangerous block recursion detected. "
220
9.88M
                 "Some blocks have not been inserted.");
221
9.88M
        return false;
222
9.88M
    }
223
224
4.07M
    aosBlockInsertionStack.push_back(osBlockName);
225
4.07M
    return true;
226
13.9M
}
227
228
/************************************************************************/
229
/*                        ~DXFBlockDefinition()                         */
230
/*                                                                      */
231
/*      Safe cleanup of a block definition.                             */
232
/************************************************************************/
233
234
DXFBlockDefinition::~DXFBlockDefinition()
235
6.84k
{
236
6.39M
    while (!apoFeatures.empty())
237
6.38M
    {
238
6.38M
        delete apoFeatures.back();
239
6.38M
        apoFeatures.pop_back();
240
6.38M
    }
241
6.84k
}