Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}