/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdriver.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: DXF Translator |
4 | | * Purpose: Implements OGRDXFDriver. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "ogr_dxf.h" |
14 | | #include "cpl_conv.h" |
15 | | #include "cpl_vsi_virtual.h" |
16 | | |
17 | | #include <algorithm> |
18 | | |
19 | | /************************************************************************/ |
20 | | /* OGRDXFDriverIdentify() */ |
21 | | /************************************************************************/ |
22 | | |
23 | | static int OGRDXFDriverIdentify(GDALOpenInfo *poOpenInfo) |
24 | | |
25 | 119k | { |
26 | 119k | if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes == 0) |
27 | 36.4k | return FALSE; |
28 | 82.7k | if (poOpenInfo->IsExtensionEqualToCI("dxf")) |
29 | 72 | return TRUE; |
30 | | |
31 | 82.6k | const char *pszIter = |
32 | 82.6k | reinterpret_cast<const char *>(poOpenInfo->pabyHeader); |
33 | 82.6k | if (STARTS_WITH(pszIter, AUTOCAD_BINARY_DXF_SIGNATURE.data())) |
34 | 238 | return true; |
35 | | |
36 | 82.3k | bool bFoundZero = false; |
37 | 82.3k | int i = 0; // Used after for. |
38 | 38.8M | for (; pszIter[i]; i++) |
39 | 38.8M | { |
40 | 38.8M | if (pszIter[i] == '0') |
41 | 655k | { |
42 | 655k | int j = i - 1; // Used after for. |
43 | 681k | for (; j >= 0; j--) |
44 | 647k | { |
45 | 647k | if (pszIter[j] != ' ') |
46 | 621k | break; |
47 | 647k | } |
48 | 655k | if (j < 0 || pszIter[j] == '\n' || pszIter[j] == '\r') |
49 | 52.5k | { |
50 | 52.5k | bFoundZero = true; |
51 | 52.5k | break; |
52 | 52.5k | } |
53 | 655k | } |
54 | 38.8M | } |
55 | 82.3k | if (!bFoundZero) |
56 | 29.8k | return FALSE; |
57 | 52.5k | i++; |
58 | 53.6k | while (pszIter[i] == ' ') |
59 | 1.08k | i++; |
60 | 91.3k | while (pszIter[i] == '\n' || pszIter[i] == '\r') |
61 | 38.7k | i++; |
62 | 52.5k | if (!STARTS_WITH_CI(pszIter + i, "SECTION")) |
63 | 2.13k | return FALSE; |
64 | 50.4k | i += static_cast<int>(strlen("SECTION")); |
65 | 50.4k | return pszIter[i] == '\n' || pszIter[i] == '\r'; |
66 | 52.5k | } |
67 | | |
68 | | /************************************************************************/ |
69 | | /* Open() */ |
70 | | /************************************************************************/ |
71 | | |
72 | | static GDALDataset *OGRDXFDriverOpen(GDALOpenInfo *poOpenInfo) |
73 | | |
74 | 25.3k | { |
75 | 25.3k | if (!OGRDXFDriverIdentify(poOpenInfo)) |
76 | 0 | return nullptr; |
77 | | |
78 | 25.3k | auto poDS = std::make_unique<OGRDXFDataSource>(); |
79 | | |
80 | 25.3k | VSILFILE *fp = nullptr; |
81 | 25.3k | std::swap(fp, poOpenInfo->fpL); |
82 | | |
83 | 25.3k | if (!poDS->Open(poOpenInfo->pszFilename, fp, false, |
84 | 25.3k | poOpenInfo->papszOpenOptions)) |
85 | 7.30k | { |
86 | 7.30k | poDS.reset(); |
87 | 7.30k | } |
88 | | |
89 | 25.3k | return poDS.release(); |
90 | 25.3k | } |
91 | | |
92 | | /************************************************************************/ |
93 | | /* Create() */ |
94 | | /************************************************************************/ |
95 | | |
96 | | static GDALDataset * |
97 | | OGRDXFDriverCreate(const char *pszName, CPL_UNUSED int nBands, |
98 | | CPL_UNUSED int nXSize, CPL_UNUSED int nYSize, |
99 | | CPL_UNUSED GDALDataType eDT, char **papszOptions) |
100 | 0 | { |
101 | 0 | OGRDXFWriterDS *poDS = new OGRDXFWriterDS(); |
102 | |
|
103 | 0 | if (poDS->Open(pszName, papszOptions)) |
104 | 0 | return poDS; |
105 | 0 | else |
106 | 0 | { |
107 | 0 | delete poDS; |
108 | 0 | return nullptr; |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | /************************************************************************/ |
113 | | /* OGRDXFDriverCanVectorTranslateFrom() */ |
114 | | /************************************************************************/ |
115 | | |
116 | | static bool OGRDXFDriverCanVectorTranslateFrom( |
117 | | const char * /*pszDestName*/, GDALDataset *poSourceDS, |
118 | | CSLConstList papszVectorTranslateArguments, char ***ppapszFailureReasons) |
119 | 0 | { |
120 | 0 | VSIVirtualHandleUniquePtr fpSrc; |
121 | 0 | auto poSrcDriver = poSourceDS->GetDriver(); |
122 | 0 | if (poSrcDriver && EQUAL(poSrcDriver->GetDescription(), "DXF")) |
123 | 0 | { |
124 | 0 | fpSrc.reset(VSIFOpenL(poSourceDS->GetDescription(), "rb")); |
125 | 0 | } |
126 | 0 | if (!fpSrc) |
127 | 0 | { |
128 | 0 | if (ppapszFailureReasons) |
129 | 0 | *ppapszFailureReasons = CSLAddString( |
130 | 0 | *ppapszFailureReasons, "Source driver is not binary DXF"); |
131 | 0 | return false; |
132 | 0 | } |
133 | 0 | std::string osBuffer; |
134 | 0 | constexpr size_t nBinarySignatureLen = AUTOCAD_BINARY_DXF_SIGNATURE.size(); |
135 | 0 | osBuffer.resize(nBinarySignatureLen); |
136 | 0 | if (!(fpSrc->Read(osBuffer.data(), 1, osBuffer.size()) == osBuffer.size() && |
137 | 0 | memcmp(osBuffer.data(), AUTOCAD_BINARY_DXF_SIGNATURE.data(), |
138 | 0 | nBinarySignatureLen) == 0)) |
139 | 0 | { |
140 | 0 | if (ppapszFailureReasons) |
141 | 0 | *ppapszFailureReasons = CSLAddString( |
142 | 0 | *ppapszFailureReasons, "Source driver is not binary DXF"); |
143 | 0 | return false; |
144 | 0 | } |
145 | | |
146 | 0 | if (papszVectorTranslateArguments) |
147 | 0 | { |
148 | 0 | const int nArgs = CSLCount(papszVectorTranslateArguments); |
149 | 0 | for (int i = 0; i < nArgs; ++i) |
150 | 0 | { |
151 | 0 | if (i + 1 < nArgs && |
152 | 0 | (strcmp(papszVectorTranslateArguments[i], "-f") == 0 || |
153 | 0 | strcmp(papszVectorTranslateArguments[i], "-of") == 0)) |
154 | 0 | { |
155 | 0 | ++i; |
156 | 0 | } |
157 | 0 | else |
158 | 0 | { |
159 | 0 | if (ppapszFailureReasons) |
160 | 0 | *ppapszFailureReasons = |
161 | 0 | CSLAddString(*ppapszFailureReasons, |
162 | 0 | "Direct copy from binary DXF does not " |
163 | 0 | "support GDALVectorTranslate() options"); |
164 | 0 | return false; |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | return true; |
170 | 0 | } |
171 | | |
172 | | /************************************************************************/ |
173 | | /* OGRDXFDriverVectorTranslateFrom() */ |
174 | | /************************************************************************/ |
175 | | |
176 | | static GDALDataset *OGRDXFDriverVectorTranslateFrom( |
177 | | const char *pszDestName, GDALDataset *poSourceDS, |
178 | | CSLConstList papszVectorTranslateArguments, |
179 | | GDALProgressFunc /* pfnProgress */, void * /* pProgressData */) |
180 | 0 | { |
181 | 0 | if (!OGRDXFDriverCanVectorTranslateFrom( |
182 | 0 | pszDestName, poSourceDS, papszVectorTranslateArguments, nullptr)) |
183 | 0 | { |
184 | 0 | return nullptr; |
185 | 0 | } |
186 | | |
187 | 0 | CPLDebug("DXF", |
188 | 0 | "Doing direct translation from AutoCAD DXF Binary to DXF ASCII"); |
189 | |
|
190 | 0 | VSIVirtualHandleUniquePtr fpSrc( |
191 | 0 | VSIFOpenL(poSourceDS->GetDescription(), "rb")); |
192 | 0 | if (!fpSrc) |
193 | 0 | { |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | | |
197 | 0 | VSIVirtualHandleUniquePtr fpDst(VSIFOpenL(pszDestName, "wb")); |
198 | 0 | if (!fpDst) |
199 | 0 | { |
200 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s ", pszDestName); |
201 | 0 | return nullptr; |
202 | 0 | } |
203 | | |
204 | 0 | OGRDXFReaderBinary reader; |
205 | 0 | reader.Initialize(fpSrc.get()); |
206 | |
|
207 | 0 | constexpr const int BUFFER_SIZE = 4096; |
208 | 0 | std::string osBuffer; |
209 | 0 | osBuffer.resize(BUFFER_SIZE); |
210 | |
|
211 | 0 | bool bOK = true; |
212 | 0 | int nCode; |
213 | 0 | while (bOK && (nCode = reader.ReadValue(&osBuffer[0], BUFFER_SIZE)) >= 0) |
214 | 0 | { |
215 | 0 | bOK = fpDst->Printf("%d\n%s\n", nCode, osBuffer.c_str()) != 0; |
216 | 0 | if (nCode == 0 && osBuffer.compare(0, 3, "EOF", 3) == 0) |
217 | 0 | break; |
218 | 0 | } |
219 | |
|
220 | 0 | if (!bOK || VSIFCloseL(fpDst.release()) != 0) |
221 | 0 | { |
222 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Error while writing file"); |
223 | 0 | return nullptr; |
224 | 0 | } |
225 | | |
226 | 0 | GDALOpenInfo oOpenInfo(pszDestName, GA_ReadOnly); |
227 | 0 | return OGRDXFDriverOpen(&oOpenInfo); |
228 | 0 | } |
229 | | |
230 | | /************************************************************************/ |
231 | | /* RegisterOGRDXF() */ |
232 | | /************************************************************************/ |
233 | | |
234 | | void RegisterOGRDXF() |
235 | | |
236 | 24 | { |
237 | 24 | if (GDALGetDriverByName("DXF") != nullptr) |
238 | 0 | return; |
239 | | |
240 | 24 | GDALDriver *poDriver = new GDALDriver(); |
241 | | |
242 | 24 | poDriver->SetDescription("DXF"); |
243 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); |
244 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES"); |
245 | 24 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "AutoCAD DXF"); |
246 | 24 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "dxf"); |
247 | 24 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/dxf.html"); |
248 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); |
249 | 24 | poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE"); |
250 | | |
251 | 24 | poDriver->SetMetadataItem( |
252 | 24 | GDAL_DMD_CREATIONOPTIONLIST, |
253 | 24 | "<CreationOptionList>" |
254 | 24 | " <Option name='HEADER' type='string' description='Template header " |
255 | 24 | "file' default='header.dxf'/>" |
256 | 24 | " <Option name='TRAILER' type='string' description='Template trailer " |
257 | 24 | "file' default='trailer.dxf'/>" |
258 | 24 | " <Option name='FIRST_ENTITY' type='int' description='Identifier of " |
259 | 24 | "first entity'/>" |
260 | 24 | " <Option name='INSUNITS' type='string-select' " |
261 | 24 | "description='Drawing units for the model space ($INSUNITS system " |
262 | 24 | "variable)' default='AUTO'>" |
263 | 24 | " <Value>AUTO</Value>" |
264 | 24 | " <Value>HEADER_VALUE</Value>" |
265 | 24 | " <Value alias='0'>UNITLESS</Value>" |
266 | 24 | " <Value alias='1'>INCHES</Value>" |
267 | 24 | " <Value alias='2'>FEET</Value>" |
268 | 24 | " <Value alias='4'>MILLIMETERS</Value>" |
269 | 24 | " <Value alias='5'>CENTIMETERS</Value>" |
270 | 24 | " <Value alias='6'>METERS</Value>" |
271 | 24 | " <Value alias='21'>US_SURVEY_FEET</Value>" |
272 | 24 | " </Option>" |
273 | 24 | " <Option name='MEASUREMENT' type='string-select' " |
274 | 24 | "description='Whether the current drawing uses imperial or metric " |
275 | 24 | "hatch " |
276 | 24 | "pattern and linetype ($MEASUREMENT system variable)' " |
277 | 24 | "default='HEADER_VALUE'>" |
278 | 24 | " <Value>HEADER_VALUE</Value>" |
279 | 24 | " <Value alias='0'>IMPERIAL</Value>" |
280 | 24 | " <Value alias='1'>METRIC</Value>" |
281 | 24 | " </Option>" |
282 | 24 | "</CreationOptionList>"); |
283 | | |
284 | 24 | poDriver->SetMetadataItem( |
285 | 24 | GDAL_DMD_OPENOPTIONLIST, |
286 | 24 | "<OpenOptionList>" |
287 | 24 | " <Option name='CLOSED_LINE_AS_POLYGON' type='boolean' description=" |
288 | 24 | "'Whether to expose closed POLYLINE/LWPOLYLINE as polygons' " |
289 | 24 | "default='NO'/>" |
290 | 24 | " <Option name='INLINE_BLOCKS' type='boolean' description=" |
291 | 24 | "'Whether INSERT entities are exploded with the geometry of the BLOCK " |
292 | 24 | "they reference' default='YES'/>" |
293 | 24 | " <Option name='MERGE_BLOCK_GEOMETRIES' type='boolean' description=" |
294 | 24 | "'Whether blocks should be merged into a compound geometry' " |
295 | 24 | "default='YES'/>" |
296 | 24 | " <Option name='TRANSLATE_ESCAPE_SEQUENCES' type='boolean' " |
297 | 24 | "description=" |
298 | 24 | "'Whether character escapes are honored where applicable, and MTEXT " |
299 | 24 | "control sequences are stripped' default='YES'/>" |
300 | 24 | " <Option name='INCLUDE_RAW_CODE_VALUES' type='boolean' description=" |
301 | 24 | "'Whether a RawCodeValues field should be added to contain all group " |
302 | 24 | "codes and values' default='NO'/>" |
303 | 24 | " <Option name='3D_EXTENSIBLE_MODE' type='boolean' description=" |
304 | 24 | "'Whether to include ASM entities with the raw ASM data stored in a " |
305 | 24 | "field' default='NO'/>" |
306 | 24 | " <Option name='HATCH_TOLEARNCE' type='float' description=" |
307 | 24 | "'Tolerance used when looking for the next component to add to the " |
308 | 24 | "hatch boundary.'/>" |
309 | 24 | " <Option name='ENCODING' type='string' description=" |
310 | 24 | "'Encoding name, as supported by iconv, to override $DWGCODEPAGE'/>" |
311 | 24 | "</OpenOptionList>"); |
312 | | |
313 | 24 | poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, |
314 | 24 | "<LayerCreationOptionList/>"); |
315 | | |
316 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
317 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES, "YES"); |
318 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_READ, "YES"); |
319 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_WRITE, "YES"); |
320 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES"); |
321 | | |
322 | 24 | poDriver->pfnOpen = OGRDXFDriverOpen; |
323 | 24 | poDriver->pfnIdentify = OGRDXFDriverIdentify; |
324 | 24 | poDriver->pfnCreate = OGRDXFDriverCreate; |
325 | 24 | poDriver->pfnCanVectorTranslateFrom = OGRDXFDriverCanVectorTranslateFrom; |
326 | 24 | poDriver->pfnVectorTranslateFrom = OGRDXFDriverVectorTranslateFrom; |
327 | | |
328 | 24 | GetGDALDriverManager()->RegisterDriver(poDriver); |
329 | 24 | } |