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