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 : }
|