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