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