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

Generated by: LCOV version 1.14