LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxfdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 109 130 83.8 %
Date: 2025-08-01 10:10:57 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14