LCOV - code coverage report
Current view: top level - frmts/pdf - pdfwritabledataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 141 174 81.0 %
Date: 2025-09-10 17:48:50 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PDF driver
       4             :  * Purpose:  GDALDataset driver for PDF dataset (writable vector dataset)
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_pdf.h"
      14             : #include "pdfcreatecopy.h"
      15             : #include "memdataset.h"
      16             : #include "pdfcreatefromcomposition.h"
      17             : 
      18             : #include <cmath>
      19             : 
      20             : /************************************************************************/
      21             : /*                      PDFWritableVectorDataset()                      */
      22             : /************************************************************************/
      23             : 
      24          38 : PDFWritableVectorDataset::PDFWritableVectorDataset()
      25          38 :     : papszOptions(nullptr), nLayers(0), papoLayers(nullptr), bModified(FALSE)
      26             : {
      27          38 : }
      28             : 
      29             : /************************************************************************/
      30             : /*                      ~PDFWritableVectorDataset()                     */
      31             : /************************************************************************/
      32             : 
      33          76 : PDFWritableVectorDataset::~PDFWritableVectorDataset()
      34             : {
      35          38 :     PDFWritableVectorDataset::Close();
      36             : 
      37          38 :     CSLDestroy(papszOptions);
      38          92 :     for (int i = 0; i < nLayers; i++)
      39          54 :         delete papoLayers[i];
      40          38 :     CPLFree(papoLayers);
      41          76 : }
      42             : 
      43             : /************************************************************************/
      44             : /*                               Create()                               */
      45             : /************************************************************************/
      46             : 
      47          71 : GDALDataset *PDFWritableVectorDataset::Create(const char *pszName, int nXSize,
      48             :                                               int nYSize, int nBandsIn,
      49             :                                               GDALDataType eType,
      50             :                                               char **papszOptions)
      51             : {
      52          71 :     if (nBandsIn == 0 && nXSize == 0 && nYSize == 0 && eType == GDT_Unknown)
      53             :     {
      54             :         const char *pszFilename =
      55          71 :             CSLFetchNameValue(papszOptions, "COMPOSITION_FILE");
      56          71 :         if (pszFilename)
      57             :         {
      58          33 :             if (CSLCount(papszOptions) != 1)
      59             :             {
      60           0 :                 CPLError(
      61             :                     CE_Warning, CPLE_AppDefined,
      62             :                     "All others options than COMPOSITION_FILE are ignored");
      63             :             }
      64          33 :             return GDALPDFCreateFromCompositionFile(pszName, pszFilename);
      65             :         }
      66             :     }
      67             : 
      68          38 :     if (nBandsIn != 0)
      69             :     {
      70           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      71             :                  "PDFWritableVectorDataset::Create() can only be called with "
      72             :                  "nBands = 0 to create a vector-only PDF");
      73           0 :         return nullptr;
      74             :     }
      75          38 :     PDFWritableVectorDataset *poDataset = new PDFWritableVectorDataset();
      76             : 
      77          38 :     poDataset->SetDescription(pszName);
      78          38 :     poDataset->papszOptions = CSLDuplicate(papszOptions);
      79             : 
      80          38 :     return poDataset;
      81             : }
      82             : 
      83             : /************************************************************************/
      84             : /*                           ICreateLayer()                             */
      85             : /************************************************************************/
      86             : 
      87             : OGRLayer *
      88          54 : PDFWritableVectorDataset::ICreateLayer(const char *pszLayerName,
      89             :                                        const OGRGeomFieldDefn *poGeomFieldDefn,
      90             :                                        CSLConstList /*papszOptions*/)
      91             : {
      92          54 :     const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
      93             :     const auto poSRS =
      94          54 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
      95             : 
      96             :     /* -------------------------------------------------------------------- */
      97             :     /*      Create the layer object.                                        */
      98             :     /* -------------------------------------------------------------------- */
      99          54 :     OGRSpatialReference *poSRSClone = nullptr;
     100          54 :     if (poSRS)
     101             :     {
     102           5 :         poSRSClone = poSRS->Clone();
     103           5 :         poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     104             :     }
     105             :     OGRLayer *poLayer =
     106          54 :         new OGRPDFWritableLayer(this, pszLayerName, poSRSClone, eType);
     107          54 :     if (poSRSClone)
     108           5 :         poSRSClone->Release();
     109             : 
     110          54 :     papoLayers = static_cast<OGRLayer **>(
     111          54 :         CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer *)));
     112          54 :     papoLayers[nLayers] = poLayer;
     113          54 :     nLayers++;
     114             : 
     115          54 :     return poLayer;
     116             : }
     117             : 
     118             : /************************************************************************/
     119             : /*                           TestCapability()                           */
     120             : /************************************************************************/
     121             : 
     122          48 : int PDFWritableVectorDataset::TestCapability(const char *pszCap) const
     123             : 
     124             : {
     125          48 :     if (EQUAL(pszCap, ODsCCreateLayer))
     126          32 :         return TRUE;
     127             :     else
     128          16 :         return FALSE;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                              GetLayer()                              */
     133             : /************************************************************************/
     134             : 
     135          35 : const OGRLayer *PDFWritableVectorDataset::GetLayer(int iLayer) const
     136             : 
     137             : {
     138          35 :     if (iLayer < 0 || iLayer >= nLayers)
     139           0 :         return nullptr;
     140             : 
     141          35 :     return papoLayers[iLayer];
     142             : }
     143             : 
     144             : /************************************************************************/
     145             : /*                            GetLayerCount()                           */
     146             : /************************************************************************/
     147             : 
     148           0 : int PDFWritableVectorDataset::GetLayerCount() const
     149             : {
     150           0 :     return nLayers;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                              Close()                                 */
     155             : /************************************************************************/
     156             : 
     157          76 : CPLErr PDFWritableVectorDataset::Close()
     158             : {
     159          76 :     return PDFWritableVectorDataset::FlushCache(true);
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                            FlushCache()                              */
     164             : /************************************************************************/
     165             : 
     166          76 : CPLErr PDFWritableVectorDataset::FlushCache(bool /* bAtClosing*/)
     167             : {
     168          76 :     if (nLayers == 0 || !bModified)
     169          55 :         return CE_None;
     170             : 
     171          21 :     bModified = FALSE;
     172             : 
     173          21 :     OGREnvelope sGlobalExtent;
     174          21 :     int bHasExtent = FALSE;
     175          58 :     for (int i = 0; i < nLayers; i++)
     176             :     {
     177          37 :         OGREnvelope sExtent;
     178          37 :         if (papoLayers[i]->GetExtent(&sExtent) == OGRERR_NONE)
     179             :         {
     180          35 :             bHasExtent = TRUE;
     181          35 :             sGlobalExtent.Merge(sExtent);
     182             :         }
     183             :     }
     184          21 :     if (!bHasExtent || sGlobalExtent.MinX == sGlobalExtent.MaxX ||
     185          20 :         sGlobalExtent.MinY == sGlobalExtent.MaxY)
     186             :     {
     187           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     188             :                  "Cannot compute spatial extent of features");
     189           1 :         return CE_Failure;
     190             :     }
     191             : 
     192          20 :     double dfRatio = (sGlobalExtent.MaxY - sGlobalExtent.MinY) /
     193          20 :                      (sGlobalExtent.MaxX - sGlobalExtent.MinX);
     194             : 
     195             :     int nWidth, nHeight;
     196             : 
     197          20 :     if (dfRatio < 1.0)
     198             :     {
     199           0 :         nWidth = 1024;
     200           0 :         const double dfHeight = nWidth * dfRatio;
     201           0 :         if (dfHeight < 1 || dfHeight > INT_MAX || std::isnan(dfHeight))
     202             :         {
     203           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid image dimensions");
     204           0 :             return CE_Failure;
     205             :         }
     206           0 :         nHeight = static_cast<int>(dfHeight);
     207             :     }
     208             :     else
     209             :     {
     210          20 :         nHeight = 1024;
     211          20 :         const double dfWidth = nHeight / dfRatio;
     212          20 :         if (dfWidth < 1 || dfWidth > INT_MAX || std::isnan(dfWidth))
     213             :         {
     214           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid image dimensions");
     215           0 :             return CE_Failure;
     216             :         }
     217          20 :         nWidth = static_cast<int>(dfWidth);
     218             :     }
     219             : 
     220          20 :     GDALGeoTransform gt;
     221          20 :     gt[0] = sGlobalExtent.MinX;
     222          20 :     gt[1] = (sGlobalExtent.MaxX - sGlobalExtent.MinX) / nWidth;
     223          20 :     gt[2] = 0;
     224          20 :     gt[3] = sGlobalExtent.MaxY;
     225          20 :     gt[4] = 0;
     226          20 :     gt[5] = -(sGlobalExtent.MaxY - sGlobalExtent.MinY) / nHeight;
     227             : 
     228             :     // Do again a check against 0, because the above divisions might
     229             :     // transform a difference close to 0, to plain 0.
     230          20 :     if (gt[1] == 0 || gt[5] == 0)
     231             :     {
     232           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     233             :                  "Cannot compute spatial extent of features");
     234           0 :         return CE_Failure;
     235             :     }
     236             : 
     237          20 :     PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE;
     238             :     const char *pszStreamCompressMethod =
     239          20 :         CSLFetchNameValue(papszOptions, "STREAM_COMPRESS");
     240          20 :     if (pszStreamCompressMethod)
     241             :     {
     242           5 :         if (EQUAL(pszStreamCompressMethod, "NONE"))
     243           5 :             eStreamCompressMethod = COMPRESS_NONE;
     244           0 :         else if (EQUAL(pszStreamCompressMethod, "DEFLATE"))
     245           0 :             eStreamCompressMethod = COMPRESS_DEFLATE;
     246             :         else
     247             :         {
     248           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     249             :                      "Unsupported value for STREAM_COMPRESS.");
     250             :         }
     251             :     }
     252             : 
     253             :     const char *pszGEO_ENCODING =
     254          20 :         CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000");
     255             : 
     256          20 :     const char *pszDPI = CSLFetchNameValue(papszOptions, "DPI");
     257          20 :     double dfDPI = DEFAULT_DPI;
     258          20 :     if (pszDPI != nullptr)
     259             :     {
     260           0 :         dfDPI = CPLAtof(pszDPI);
     261           0 :         if (dfDPI < DEFAULT_DPI)
     262           0 :             dfDPI = DEFAULT_DPI;
     263             :     }
     264             :     else
     265             :     {
     266          20 :         dfDPI = DEFAULT_DPI;
     267             :     }
     268             : 
     269             :     const char *pszWriteUserUnit =
     270          20 :         CSLFetchNameValue(papszOptions, "WRITE_USERUNIT");
     271             :     bool bWriteUserUnit;
     272          20 :     if (pszWriteUserUnit != nullptr)
     273           0 :         bWriteUserUnit = CPLTestBool(pszWriteUserUnit);
     274             :     else
     275          20 :         bWriteUserUnit = (pszDPI == nullptr);
     276             : 
     277          20 :     const char *pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE");
     278             : 
     279          20 :     int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0"));
     280             : 
     281          20 :     PDFMargins sMargins;
     282          20 :     sMargins.nLeft = nMargin;
     283          20 :     sMargins.nRight = nMargin;
     284          20 :     sMargins.nTop = nMargin;
     285          20 :     sMargins.nBottom = nMargin;
     286             : 
     287          20 :     const char *pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN");
     288          20 :     if (pszLeftMargin)
     289           0 :         sMargins.nLeft = atoi(pszLeftMargin);
     290             : 
     291             :     const char *pszRightMargin =
     292          20 :         CSLFetchNameValue(papszOptions, "RIGHT_MARGIN");
     293          20 :     if (pszRightMargin)
     294           0 :         sMargins.nRight = atoi(pszRightMargin);
     295             : 
     296          20 :     const char *pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN");
     297          20 :     if (pszTopMargin)
     298           0 :         sMargins.nTop = atoi(pszTopMargin);
     299             : 
     300             :     const char *pszBottomMargin =
     301          20 :         CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN");
     302          20 :     if (pszBottomMargin)
     303           0 :         sMargins.nBottom = atoi(pszBottomMargin);
     304             : 
     305             :     const char *pszExtraImages =
     306          20 :         CSLFetchNameValue(papszOptions, "EXTRA_IMAGES");
     307             :     const char *pszExtraStream =
     308          20 :         CSLFetchNameValue(papszOptions, "EXTRA_STREAM");
     309             :     const char *pszExtraLayerName =
     310          20 :         CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME");
     311             : 
     312             :     const char *pszOGRDisplayField =
     313          20 :         CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD");
     314             :     const char *pszOGRDisplayLayerNames =
     315          20 :         CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES");
     316             :     const bool bWriteOGRAttributes =
     317          20 :         CPLFetchBool(papszOptions, "OGR_WRITE_ATTRIBUTES", true);
     318             :     const char *pszOGRLinkField =
     319          20 :         CSLFetchNameValue(papszOptions, "OGR_LINK_FIELD");
     320             : 
     321          20 :     const char *pszOffLayers = CSLFetchNameValue(papszOptions, "OFF_LAYERS");
     322             :     const char *pszExclusiveLayers =
     323          20 :         CSLFetchNameValue(papszOptions, "EXCLUSIVE_LAYERS");
     324             : 
     325          20 :     const char *pszJavascript = CSLFetchNameValue(papszOptions, "JAVASCRIPT");
     326             :     const char *pszJavascriptFile =
     327          20 :         CSLFetchNameValue(papszOptions, "JAVASCRIPT_FILE");
     328             : 
     329             :     /* -------------------------------------------------------------------- */
     330             :     /*      Create file.                                                    */
     331             :     /* -------------------------------------------------------------------- */
     332          20 :     VSILFILE *fp = VSIFOpenL(GetDescription(), "wb");
     333          20 :     if (fp == nullptr)
     334             :     {
     335           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create PDF file %s.\n",
     336           0 :                  GetDescription());
     337           0 :         return CE_Failure;
     338             :     }
     339             : 
     340          20 :     GDALPDFWriter oWriter(fp);
     341             : 
     342             :     GDALDataset *poSrcDS =
     343          20 :         MEMDataset::Create("MEM:::", nWidth, nHeight, 0, GDT_Byte, nullptr);
     344             : 
     345          20 :     poSrcDS->SetGeoTransform(gt);
     346             : 
     347          20 :     const OGRSpatialReference *poSRS = papoLayers[0]->GetSpatialRef();
     348          20 :     if (poSRS)
     349             :     {
     350           5 :         char *pszWKT = nullptr;
     351           5 :         poSRS->exportToWkt(&pszWKT);
     352           5 :         poSrcDS->SetProjection(pszWKT);
     353           5 :         CPLFree(pszWKT);
     354             :     }
     355             : 
     356          20 :     oWriter.SetInfo(poSrcDS, papszOptions);
     357             : 
     358          20 :     oWriter.StartPage(poSrcDS, dfDPI, bWriteUserUnit, pszGEO_ENCODING,
     359             :                       pszNEATLINE, &sMargins, eStreamCompressMethod,
     360             :                       bWriteOGRAttributes);
     361             : 
     362          20 :     int iObj = 0;
     363             : 
     364             :     char **papszLayerNames =
     365          20 :         CSLTokenizeString2(pszOGRDisplayLayerNames, ",", 0);
     366             : 
     367          55 :     for (int i = 0; i < nLayers; i++)
     368             :     {
     369          70 :         CPLString osLayerName;
     370          35 :         if (CSLCount(papszLayerNames) < nLayers)
     371          35 :             osLayerName = papoLayers[i]->GetName();
     372             :         else
     373           0 :             osLayerName = papszLayerNames[i];
     374             : 
     375          35 :         oWriter.WriteOGRLayer(GDALDataset::ToHandle(this), i,
     376             :                               pszOGRDisplayField, pszOGRLinkField, osLayerName,
     377             :                               bWriteOGRAttributes, iObj);
     378             :     }
     379             : 
     380          20 :     CSLDestroy(papszLayerNames);
     381             : 
     382          20 :     oWriter.EndPage(pszExtraImages, pszExtraStream, pszExtraLayerName,
     383             :                     pszOffLayers, pszExclusiveLayers);
     384             : 
     385          20 :     if (pszJavascript)
     386           0 :         oWriter.WriteJavascript(pszJavascript);
     387          20 :     else if (pszJavascriptFile)
     388           0 :         oWriter.WriteJavascriptFile(pszJavascriptFile);
     389             : 
     390          20 :     oWriter.Close();
     391             : 
     392          20 :     delete poSrcDS;
     393             : 
     394          20 :     return CE_None;
     395             : }

Generated by: LCOV version 1.14