Line data Source code
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 125 : bool OGRDXFDataSource::ReadBlocksSection() 26 : 27 : { 28 : // Force inlining of blocks to false, for when OGRDXFLayer processes 29 : // INSERT entities 30 125 : const bool bOldInlineBlocks = bInlineBlocks; 31 125 : bInlineBlocks = false; 32 : 33 : OGRDXFLayer *poReaderLayer = 34 125 : static_cast<OGRDXFLayer *>(GetLayerByName("Entities")); 35 : 36 125 : iEntitiesOffset = oReader.iSrcBufferFileOffset + oReader.iSrcBufferOffset; 37 125 : iEntitiesLineNumber = oReader.nLineNumber; 38 : 39 : char szLineBuf[257]; 40 125 : int nCode = 0; 41 4303 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > -1 && 42 2151 : !EQUAL(szLineBuf, "ENDSEC")) 43 : { 44 : // We are only interested in extracting blocks. 45 2027 : if (nCode != 0 || !EQUAL(szLineBuf, "BLOCK")) 46 1952 : continue; 47 : 48 : // Process contents of BLOCK definition till we find the 49 : // first entity. 50 337 : CPLString osBlockName; 51 337 : CPLString osBlockRecordHandle; 52 337 : OGRDXFInsertTransformer oBasePointTransformer; 53 : 54 4257 : while ((nCode = ReadValue(szLineBuf, sizeof(szLineBuf))) > 0) 55 : { 56 3920 : switch (nCode) 57 : { 58 336 : case 2: 59 336 : osBlockName = szLineBuf; 60 336 : break; 61 : 62 238 : case 330: 63 : // get the block record handle as well, for arrowheads 64 238 : osBlockRecordHandle = szLineBuf; 65 238 : break; 66 : 67 334 : case 10: 68 334 : oBasePointTransformer.dfXOffset = -CPLAtof(szLineBuf); 69 334 : break; 70 : 71 334 : case 20: 72 334 : oBasePointTransformer.dfYOffset = -CPLAtof(szLineBuf); 73 334 : break; 74 : 75 334 : case 30: 76 334 : oBasePointTransformer.dfZOffset = -CPLAtof(szLineBuf); 77 334 : break; 78 : } 79 : } 80 337 : if (nCode < 0) 81 : { 82 0 : bInlineBlocks = bOldInlineBlocks; 83 0 : DXF_READER_ERROR(); 84 0 : return false; 85 : } 86 : 87 : // store the block record handle mapping even if the block is empty 88 337 : oBlockRecordHandles[osBlockRecordHandle] = osBlockName; 89 : 90 337 : if (EQUAL(szLineBuf, "ENDBLK")) 91 262 : continue; 92 : 93 75 : UnreadValue(); 94 : 95 75 : if (oBlockMap.find(osBlockName) != oBlockMap.end()) 96 : { 97 0 : bInlineBlocks = bOldInlineBlocks; 98 0 : DXF_READER_ERROR(); 99 0 : return false; 100 : } 101 : 102 : // Now we will process entities till we run out at the ENDBLK code. 103 : 104 75 : PushBlockInsertion(osBlockName); 105 : 106 75 : OGRDXFFeature *poFeature = nullptr; 107 75 : int nIters = 0; 108 : const int nMaxIters = 109 75 : atoi(CPLGetConfigOption("DXF_FEATURE_LIMIT_PER_BLOCK", "10000")); 110 1081 : while ((poFeature = poReaderLayer->GetNextUnfilteredFeature()) != 111 : nullptr) 112 : { 113 1006 : if (nMaxIters >= 0 && nIters == nMaxIters) 114 : { 115 0 : delete poFeature; 116 0 : CPLError(CE_Warning, CPLE_AppDefined, 117 : "Limit of %d features for block %s reached. " 118 : "If you need more, set the " 119 : "DXF_FEATURE_LIMIT_PER_BLOCK configuration " 120 : "option to the maximum value (or -1 for no limit)", 121 : nMaxIters, osBlockName.c_str()); 122 0 : break; 123 : } 124 : 125 : // Apply the base point translation 126 1006 : OGRGeometry *poFeatureGeom = poFeature->GetGeometryRef(); 127 1006 : if (poFeatureGeom) 128 1005 : poFeatureGeom->transform(&oBasePointTransformer); 129 : 130 : // Also apply the base point translation to the original 131 : // coordinates of block references 132 1006 : if (poFeature->IsBlockReference()) 133 : { 134 793 : DXFTriple oTriple = poFeature->GetInsertOCSCoords(); 135 793 : OGRPoint oPoint(oTriple.dfX, oTriple.dfY, oTriple.dfZ); 136 793 : oPoint.transform(&oBasePointTransformer); 137 793 : poFeature->SetInsertOCSCoords( 138 1586 : DXFTriple(oPoint.getX(), oPoint.getY(), oPoint.getZ())); 139 : } 140 : 141 1006 : oBlockMap[osBlockName].apoFeatures.push_back(poFeature); 142 1006 : nIters++; 143 : } 144 : 145 75 : PopBlockInsertion(); 146 : } 147 125 : if (nCode < 0) 148 : { 149 1 : bInlineBlocks = bOldInlineBlocks; 150 1 : DXF_READER_ERROR(); 151 1 : return false; 152 : } 153 : 154 124 : CPLDebug("DXF", "Read %d blocks with meaningful geometry.", 155 124 : (int)oBlockMap.size()); 156 : 157 : // Restore old inline blocks setting 158 124 : bInlineBlocks = bOldInlineBlocks; 159 : 160 124 : return true; 161 : } 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 354 : DXFBlockDefinition *OGRDXFDataSource::LookupBlock(const char *pszName) 173 : 174 : { 175 708 : CPLString l_osName = pszName; 176 : 177 354 : if (oBlockMap.count(l_osName) == 0) 178 134 : return nullptr; 179 : else 180 220 : return &(oBlockMap[l_osName]); 181 : } 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 18 : CPLString OGRDXFDataSource::GetBlockNameByRecordHandle(const char *pszID) 191 : 192 : { 193 36 : CPLString l_osID = pszID; 194 : 195 18 : if (oBlockRecordHandles.count(l_osID) == 0) 196 2 : return ""; 197 : else 198 16 : return oBlockRecordHandles[l_osID]; 199 : } 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 1299 : bool OGRDXFDataSource::PushBlockInsertion(const CPLString &osBlockName) 209 : 210 : { 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 2598 : if (aosBlockInsertionStack.size() > 128 || 215 0 : std::find(aosBlockInsertionStack.begin(), aosBlockInsertionStack.end(), 216 2598 : osBlockName) != aosBlockInsertionStack.end()) 217 : { 218 1003 : CPLError(CE_Warning, CPLE_AppDefined, 219 : "Dangerous block recursion detected. " 220 : "Some blocks have not been inserted."); 221 1003 : return false; 222 : } 223 : 224 296 : aosBlockInsertionStack.push_back(osBlockName); 225 296 : return true; 226 : } 227 : 228 : /************************************************************************/ 229 : /* ~DXFBlockDefinition() */ 230 : /* */ 231 : /* Safe cleanup of a block definition. */ 232 : /************************************************************************/ 233 : 234 75 : DXFBlockDefinition::~DXFBlockDefinition() 235 : { 236 1081 : while (!apoFeatures.empty()) 237 : { 238 1006 : delete apoFeatures.back(); 239 1006 : apoFeatures.pop_back(); 240 : } 241 75 : }