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