/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 | } |