LCOV - code coverage report
Current view: top level - frmts/pdf - pdfwritabledataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 138 171 80.7 %
Date: 2024-11-21 22:18:42 Functions: 8 9 88.9 %

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

Generated by: LCOV version 1.14