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