/src/gdal/frmts/pdf/pdfdataset.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: PDF driver |
4 | | * Purpose: GDALDataset driver for PDF dataset. |
5 | | * Author: Even Rouault, <even dot rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * |
9 | | * Support for open-source PDFium library |
10 | | * |
11 | | * Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/) |
12 | | * Author: Martin Mikita <martin.mikita@klokantech.com>, xmikit00 @ FIT VUT Brno |
13 | | * |
14 | | ****************************************************************************** |
15 | | * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com> |
16 | | * |
17 | | * SPDX-License-Identifier: MIT |
18 | | ****************************************************************************/ |
19 | | |
20 | | #include "gdal_pdf.h" |
21 | | |
22 | | #include "cpl_json_streaming_writer.h" |
23 | | #include "cpl_vsi_virtual.h" |
24 | | #include "cpl_spawn.h" |
25 | | #include "cpl_string.h" |
26 | | #include "gdal_frmts.h" |
27 | | #include "gdalalgorithm.h" |
28 | | #include "ogr_spatialref.h" |
29 | | #include "ogr_geometry.h" |
30 | | |
31 | | #ifdef HAVE_POPPLER |
32 | | #include "cpl_multiproc.h" |
33 | | #include "pdfio.h" |
34 | | #endif // HAVE_POPPLER |
35 | | |
36 | | #include "pdfcreatecopy.h" |
37 | | |
38 | | #include "pdfdrivercore.h" |
39 | | |
40 | | #include <algorithm> |
41 | | #include <array> |
42 | | #include <cassert> |
43 | | #include <cmath> |
44 | | #include <limits> |
45 | | #include <set> |
46 | | |
47 | | #ifdef HAVE_PDFIUM |
48 | | // To be able to use |
49 | | // https://github.com/rouault/pdfium_build_gdal_3_5/releases/download/v1_pdfium_5106/install-win10-vs2019-x64-rev5106.zip |
50 | | // with newer Visual Studio versions. |
51 | | // Trick from https://github.com/conan-io/conan-center-index/issues/4826 |
52 | | #if _MSC_VER >= 1932 // Visual Studio 2022 version 17.2+ |
53 | | #pragma comment( \ |
54 | | linker, \ |
55 | | "/alternatename:__imp___std_init_once_complete=__imp_InitOnceComplete") |
56 | | #pragma comment( \ |
57 | | linker, \ |
58 | | "/alternatename:__imp___std_init_once_begin_initialize=__imp_InitOnceBeginInitialize") |
59 | | #endif |
60 | | #endif |
61 | | |
62 | | /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport |
63 | | * -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */ |
64 | | |
65 | | #ifdef HAVE_PDF_READ_SUPPORT |
66 | | |
67 | | static double Get(GDALPDFObject *poObj, int nIndice = -1); |
68 | | |
69 | | #ifdef HAVE_POPPLER |
70 | | |
71 | | static CPLMutex *hGlobalParamsMutex = nullptr; |
72 | | |
73 | | /************************************************************************/ |
74 | | /* GDALPDFOutputDev */ |
75 | | /************************************************************************/ |
76 | | |
77 | | class GDALPDFOutputDev final : public SplashOutputDev |
78 | | { |
79 | | private: |
80 | | int bEnableVector; |
81 | | int bEnableText; |
82 | | int bEnableBitmap; |
83 | | |
84 | | void skipBytes(Stream *str, int width, int height, int nComps, int nBits) |
85 | 0 | { |
86 | 0 | int nVals = width * nComps; |
87 | 0 | int nLineSize = (nVals * nBits + 7) >> 3; |
88 | 0 | int nBytes = nLineSize * height; |
89 | 0 | for (int i = 0; i < nBytes; i++) |
90 | 0 | { |
91 | 0 | if (str->getChar() == EOF) |
92 | 0 | break; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | public: |
97 | | GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, |
98 | | [[maybe_unused]] bool reverseVideoA, |
99 | | SplashColorPtr paperColorA) |
100 | 7.86k | : SplashOutputDev(colorModeA, bitmapRowPadA, |
101 | 7.86k | #if POPPLER_MAJOR_VERSION < 26 || \ |
102 | 7.86k | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION < 2) |
103 | 7.86k | reverseVideoA, |
104 | 7.86k | #endif |
105 | 7.86k | paperColorA), |
106 | 7.86k | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE) |
107 | 7.86k | { |
108 | 7.86k | } |
109 | | |
110 | | void SetEnableVector(int bFlag) |
111 | 15.7k | { |
112 | 15.7k | bEnableVector = bFlag; |
113 | 15.7k | } |
114 | | |
115 | | void SetEnableText(int bFlag) |
116 | 7.86k | { |
117 | 7.86k | bEnableText = bFlag; |
118 | 7.86k | } |
119 | | |
120 | | void SetEnableBitmap(int bFlag) |
121 | 15.7k | { |
122 | 15.7k | bEnableBitmap = bFlag; |
123 | 15.7k | } |
124 | | |
125 | | void startPage(int pageNum, GfxState *state, XRef *xrefIn) override; |
126 | | |
127 | | void stroke(GfxState *state) override |
128 | 1.17M | { |
129 | 1.17M | if (bEnableVector) |
130 | 1.17M | SplashOutputDev::stroke(state); |
131 | 1.17M | } |
132 | | |
133 | | void fill(GfxState *state) override |
134 | 6.98M | { |
135 | 6.98M | if (bEnableVector) |
136 | 6.98M | SplashOutputDev::fill(state); |
137 | 6.98M | } |
138 | | |
139 | | void eoFill(GfxState *state) override |
140 | 7.68k | { |
141 | 7.68k | if (bEnableVector) |
142 | 7.68k | SplashOutputDev::eoFill(state); |
143 | 7.68k | } |
144 | | |
145 | | virtual void drawChar(GfxState *state, double x, double y, double dx, |
146 | | double dy, double originX, double originY, |
147 | | CharCode code, int nBytes, const Unicode *u, |
148 | | int uLen) override |
149 | 1.63M | { |
150 | 1.63M | if (bEnableText) |
151 | 0 | SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY, |
152 | 0 | code, nBytes, u, uLen); |
153 | 1.63M | } |
154 | | |
155 | | void beginTextObject(GfxState *state) override |
156 | 163k | { |
157 | 163k | if (bEnableText) |
158 | 0 | SplashOutputDev::beginTextObject(state); |
159 | 163k | } |
160 | | |
161 | | void endTextObject(GfxState *state) override |
162 | 167k | { |
163 | 167k | if (bEnableText) |
164 | 0 | SplashOutputDev::endTextObject(state); |
165 | 167k | } |
166 | | |
167 | | virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, |
168 | | int width, int height, bool invert, |
169 | | bool interpolate, bool inlineImg) override |
170 | 29.2k | { |
171 | 29.2k | if (bEnableBitmap) |
172 | 29.2k | SplashOutputDev::drawImageMask(state, ref, str, width, height, |
173 | 29.2k | invert, interpolate, inlineImg); |
174 | 0 | else |
175 | 0 | { |
176 | 0 | VSIPDFFileStream::resetNoCheckReturnValue(str); |
177 | 0 | if (inlineImg) |
178 | 0 | { |
179 | 0 | skipBytes(str, width, height, 1, 1); |
180 | 0 | } |
181 | 0 | str->close(); |
182 | 0 | } |
183 | 29.2k | } |
184 | | |
185 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
186 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION > 5) || \ |
187 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION == 5 && \ |
188 | | POPPLER_MICRO_VERSION > 0) |
189 | | bool setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
190 | | int width, int height, bool invert, |
191 | | bool inlineImg, |
192 | | std::array<double, 6> &baseMatrix) override |
193 | | { |
194 | | if (bEnableBitmap) |
195 | | return SplashOutputDev::setSoftMaskFromImageMask( |
196 | | state, ref, str, width, height, invert, inlineImg, baseMatrix); |
197 | | else |
198 | | str->close(); |
199 | | return true; |
200 | | } |
201 | | #else |
202 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
203 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
204 | | void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
205 | | int width, int height, bool invert, |
206 | | bool inlineImg, |
207 | | std::array<double, 6> &baseMatrix) override |
208 | | #else |
209 | | void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
210 | | int width, int height, bool invert, |
211 | | bool inlineImg, double *baseMatrix) override |
212 | | #endif |
213 | 1.96M | { |
214 | 1.96M | if (bEnableBitmap) |
215 | 1.96M | SplashOutputDev::setSoftMaskFromImageMask( |
216 | 1.96M | state, ref, str, width, height, invert, inlineImg, baseMatrix); |
217 | 0 | else |
218 | 0 | str->close(); |
219 | 1.96M | } |
220 | | #endif |
221 | | |
222 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
223 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
224 | | void unsetSoftMaskFromImageMask(GfxState *state, |
225 | | std::array<double, 6> &baseMatrix) override |
226 | | #else |
227 | | void unsetSoftMaskFromImageMask(GfxState *state, |
228 | | double *baseMatrix) override |
229 | | #endif |
230 | 1.96M | { |
231 | 1.96M | if (bEnableBitmap) |
232 | 1.96M | SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix); |
233 | 1.96M | } |
234 | | |
235 | | virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, |
236 | | int height, GfxImageColorMap *colorMap, |
237 | | bool interpolate, const int *maskColors, |
238 | | bool inlineImg) override |
239 | 3.85k | { |
240 | 3.85k | if (bEnableBitmap) |
241 | 3.85k | SplashOutputDev::drawImage(state, ref, str, width, height, colorMap, |
242 | 3.85k | interpolate, maskColors, inlineImg); |
243 | 0 | else |
244 | 0 | { |
245 | 0 | VSIPDFFileStream::resetNoCheckReturnValue(str); |
246 | 0 | if (inlineImg) |
247 | 0 | { |
248 | 0 | skipBytes(str, width, height, colorMap->getNumPixelComps(), |
249 | 0 | colorMap->getBits()); |
250 | 0 | } |
251 | 0 | str->close(); |
252 | 0 | } |
253 | 3.85k | } |
254 | | |
255 | | virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, |
256 | | int width, int height, |
257 | | GfxImageColorMap *colorMap, bool interpolate, |
258 | | Stream *maskStr, int maskWidth, int maskHeight, |
259 | | bool maskInvert, bool maskInterpolate) override |
260 | 88 | { |
261 | 88 | if (bEnableBitmap) |
262 | 88 | SplashOutputDev::drawMaskedImage( |
263 | 88 | state, ref, str, width, height, colorMap, interpolate, maskStr, |
264 | 88 | maskWidth, maskHeight, maskInvert, maskInterpolate); |
265 | 0 | else |
266 | 0 | str->close(); |
267 | 88 | } |
268 | | |
269 | | virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, |
270 | | int width, int height, |
271 | | GfxImageColorMap *colorMap, |
272 | | bool interpolate, Stream *maskStr, |
273 | | int maskWidth, int maskHeight, |
274 | | GfxImageColorMap *maskColorMap, |
275 | | bool maskInterpolate) override |
276 | 3.67k | { |
277 | 3.67k | if (bEnableBitmap) |
278 | 3.67k | { |
279 | 3.67k | if (maskColorMap->getBits() <= |
280 | 3.67k | 0) /* workaround poppler bug (robustness) */ |
281 | 0 | { |
282 | 0 | str->close(); |
283 | 0 | return; |
284 | 0 | } |
285 | 3.67k | SplashOutputDev::drawSoftMaskedImage( |
286 | 3.67k | state, ref, str, width, height, colorMap, interpolate, maskStr, |
287 | 3.67k | maskWidth, maskHeight, maskColorMap, maskInterpolate); |
288 | 3.67k | } |
289 | 0 | else |
290 | 0 | str->close(); |
291 | 3.67k | } |
292 | | }; |
293 | | |
294 | | void GDALPDFOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefIn) |
295 | 7.86k | { |
296 | 7.86k | SplashOutputDev::startPage(pageNum, state, xrefIn); |
297 | 7.86k | SplashBitmap *poBitmap = getBitmap(); |
298 | 7.86k | memset(poBitmap->getDataPtr(), 255, |
299 | 7.86k | static_cast<size_t>(poBitmap->getRowSize()) * poBitmap->getHeight()); |
300 | 7.86k | } |
301 | | |
302 | | #endif // ~ HAVE_POPPLER |
303 | | |
304 | | /************************************************************************/ |
305 | | /* Dump routines */ |
306 | | /************************************************************************/ |
307 | | |
308 | | class GDALPDFDumper |
309 | | { |
310 | | private: |
311 | | FILE *f = nullptr; |
312 | | const int nDepthLimit; |
313 | | std::set<int> aoSetObjectExplored{}; |
314 | | const bool bDumpParent; |
315 | | |
316 | | void DumpSimplified(GDALPDFObject *poObj); |
317 | | |
318 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFDumper) |
319 | | |
320 | | public: |
321 | | GDALPDFDumper(const char *pszFilename, const char *pszDumpFile, |
322 | | int nDepthLimitIn = -1) |
323 | 0 | : nDepthLimit(nDepthLimitIn), |
324 | | bDumpParent( |
325 | 0 | CPLTestBool(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE"))) |
326 | 0 | { |
327 | 0 | if (strcmp(pszDumpFile, "stderr") == 0) |
328 | 0 | f = stderr; |
329 | 0 | else if (EQUAL(pszDumpFile, "YES")) |
330 | 0 | f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), |
331 | 0 | "wt"); |
332 | 0 | else |
333 | 0 | f = fopen(pszDumpFile, "wt"); |
334 | 0 | if (f == nullptr) |
335 | 0 | f = stderr; |
336 | 0 | } |
337 | | |
338 | | ~GDALPDFDumper() |
339 | 0 | { |
340 | 0 | if (f != stderr) |
341 | 0 | fclose(f); |
342 | 0 | } |
343 | | |
344 | | void Dump(GDALPDFObject *poObj, int nDepth = 0); |
345 | | void Dump(GDALPDFDictionary *poDict, int nDepth = 0); |
346 | | void Dump(GDALPDFArray *poArray, int nDepth = 0); |
347 | | }; |
348 | | |
349 | | void GDALPDFDumper::Dump(GDALPDFArray *poArray, int nDepth) |
350 | 0 | { |
351 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
352 | 0 | return; |
353 | | |
354 | 0 | int nLength = poArray->GetLength(); |
355 | 0 | int i; |
356 | 0 | CPLString osIndent; |
357 | 0 | for (i = 0; i < nDepth; i++) |
358 | 0 | osIndent += " "; |
359 | 0 | for (i = 0; i < nLength; i++) |
360 | 0 | { |
361 | 0 | fprintf(f, "%sItem[%d]:", osIndent.c_str(), i); |
362 | 0 | GDALPDFObject *poObj = nullptr; |
363 | 0 | if ((poObj = poArray->Get(i)) != nullptr) |
364 | 0 | { |
365 | 0 | if (poObj->GetType() == PDFObjectType_String || |
366 | 0 | poObj->GetType() == PDFObjectType_Null || |
367 | 0 | poObj->GetType() == PDFObjectType_Bool || |
368 | 0 | poObj->GetType() == PDFObjectType_Int || |
369 | 0 | poObj->GetType() == PDFObjectType_Real || |
370 | 0 | poObj->GetType() == PDFObjectType_Name) |
371 | 0 | { |
372 | 0 | fprintf(f, " "); |
373 | 0 | DumpSimplified(poObj); |
374 | 0 | fprintf(f, "\n"); |
375 | 0 | } |
376 | 0 | else |
377 | 0 | { |
378 | 0 | fprintf(f, "\n"); |
379 | 0 | Dump(poObj, nDepth + 1); |
380 | 0 | } |
381 | 0 | } |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | void GDALPDFDumper::DumpSimplified(GDALPDFObject *poObj) |
386 | 0 | { |
387 | 0 | switch (poObj->GetType()) |
388 | 0 | { |
389 | 0 | case PDFObjectType_String: |
390 | 0 | fprintf(f, "%s (string)", poObj->GetString().c_str()); |
391 | 0 | break; |
392 | | |
393 | 0 | case PDFObjectType_Null: |
394 | 0 | fprintf(f, "null"); |
395 | 0 | break; |
396 | | |
397 | 0 | case PDFObjectType_Bool: |
398 | 0 | fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false"); |
399 | 0 | break; |
400 | | |
401 | 0 | case PDFObjectType_Int: |
402 | 0 | fprintf(f, "%d (int)", poObj->GetInt()); |
403 | 0 | break; |
404 | | |
405 | 0 | case PDFObjectType_Real: |
406 | 0 | fprintf(f, "%f (real)", poObj->GetReal()); |
407 | 0 | break; |
408 | | |
409 | 0 | case PDFObjectType_Name: |
410 | 0 | fprintf(f, "%s (name)", poObj->GetName().c_str()); |
411 | 0 | break; |
412 | | |
413 | 0 | default: |
414 | 0 | fprintf(f, "unknown !"); |
415 | 0 | break; |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | | void GDALPDFDumper::Dump(GDALPDFObject *poObj, int nDepth) |
420 | 0 | { |
421 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
422 | 0 | return; |
423 | | |
424 | 0 | int i; |
425 | 0 | CPLString osIndent; |
426 | 0 | for (i = 0; i < nDepth; i++) |
427 | 0 | osIndent += " "; |
428 | 0 | fprintf(f, "%sType = %s", osIndent.c_str(), poObj->GetTypeName()); |
429 | 0 | int nRefNum = poObj->GetRefNum().toInt(); |
430 | 0 | if (nRefNum != 0) |
431 | 0 | fprintf(f, ", Num = %d, Gen = %d", nRefNum, poObj->GetRefGen()); |
432 | 0 | fprintf(f, "\n"); |
433 | |
|
434 | 0 | if (nRefNum != 0) |
435 | 0 | { |
436 | 0 | if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end()) |
437 | 0 | return; |
438 | 0 | aoSetObjectExplored.insert(nRefNum); |
439 | 0 | } |
440 | | |
441 | 0 | switch (poObj->GetType()) |
442 | 0 | { |
443 | 0 | case PDFObjectType_Array: |
444 | 0 | Dump(poObj->GetArray(), nDepth + 1); |
445 | 0 | break; |
446 | | |
447 | 0 | case PDFObjectType_Dictionary: |
448 | 0 | Dump(poObj->GetDictionary(), nDepth + 1); |
449 | 0 | break; |
450 | | |
451 | 0 | case PDFObjectType_String: |
452 | 0 | case PDFObjectType_Null: |
453 | 0 | case PDFObjectType_Bool: |
454 | 0 | case PDFObjectType_Int: |
455 | 0 | case PDFObjectType_Real: |
456 | 0 | case PDFObjectType_Name: |
457 | 0 | fprintf(f, "%s", osIndent.c_str()); |
458 | 0 | DumpSimplified(poObj); |
459 | 0 | fprintf(f, "\n"); |
460 | 0 | break; |
461 | | |
462 | 0 | default: |
463 | 0 | fprintf(f, "%s", osIndent.c_str()); |
464 | 0 | fprintf(f, "unknown !\n"); |
465 | 0 | break; |
466 | 0 | } |
467 | | |
468 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
469 | 0 | if (poStream != nullptr) |
470 | 0 | { |
471 | 0 | fprintf(f, |
472 | 0 | "%sHas stream (" CPL_FRMT_GIB |
473 | 0 | " uncompressed bytes, " CPL_FRMT_GIB " raw bytes)\n", |
474 | 0 | osIndent.c_str(), static_cast<GIntBig>(poStream->GetLength()), |
475 | 0 | static_cast<GIntBig>(poStream->GetRawLength())); |
476 | 0 | } |
477 | 0 | } |
478 | | |
479 | | void GDALPDFDumper::Dump(GDALPDFDictionary *poDict, int nDepth) |
480 | 0 | { |
481 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
482 | 0 | return; |
483 | | |
484 | 0 | CPLString osIndent; |
485 | 0 | for (int i = 0; i < nDepth; i++) |
486 | 0 | osIndent += " "; |
487 | 0 | int i = 0; |
488 | 0 | const auto &oMap = poDict->GetValues(); |
489 | 0 | for (const auto &[osKey, poObj] : oMap) |
490 | 0 | { |
491 | 0 | fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, osKey.c_str()); |
492 | 0 | ++i; |
493 | 0 | if (osKey == "Parent" && !bDumpParent) |
494 | 0 | { |
495 | 0 | if (poObj->GetRefNum().toBool()) |
496 | 0 | fprintf(f, ", Num = %d, Gen = %d", poObj->GetRefNum().toInt(), |
497 | 0 | poObj->GetRefGen()); |
498 | 0 | fprintf(f, "\n"); |
499 | 0 | continue; |
500 | 0 | } |
501 | 0 | if (poObj != nullptr) |
502 | 0 | { |
503 | 0 | if (poObj->GetType() == PDFObjectType_String || |
504 | 0 | poObj->GetType() == PDFObjectType_Null || |
505 | 0 | poObj->GetType() == PDFObjectType_Bool || |
506 | 0 | poObj->GetType() == PDFObjectType_Int || |
507 | 0 | poObj->GetType() == PDFObjectType_Real || |
508 | 0 | poObj->GetType() == PDFObjectType_Name) |
509 | 0 | { |
510 | 0 | fprintf(f, " = "); |
511 | 0 | DumpSimplified(poObj); |
512 | 0 | fprintf(f, "\n"); |
513 | 0 | } |
514 | 0 | else |
515 | 0 | { |
516 | 0 | fprintf(f, "\n"); |
517 | 0 | Dump(poObj, nDepth + 1); |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | /************************************************************************/ |
524 | | /* PDFRasterBand() */ |
525 | | /************************************************************************/ |
526 | | |
527 | | PDFRasterBand::PDFRasterBand(PDFDataset *poDSIn, int nBandIn, |
528 | | int nResolutionLevelIn) |
529 | 114k | : nResolutionLevel(nResolutionLevelIn) |
530 | 114k | { |
531 | 114k | poDS = poDSIn; |
532 | 114k | nBand = nBandIn; |
533 | | |
534 | 114k | eDataType = GDT_UInt8; |
535 | 114k | } |
536 | | |
537 | | /************************************************************************/ |
538 | | /* SetSize() */ |
539 | | /************************************************************************/ |
540 | | |
541 | | void PDFRasterBand::SetSize(int nXSize, int nYSize) |
542 | 113k | { |
543 | 113k | nRasterXSize = nXSize; |
544 | 113k | nRasterYSize = nYSize; |
545 | | |
546 | 113k | const auto poPDFDS = cpl::down_cast<const PDFDataset *>(poDS); |
547 | 113k | if (nResolutionLevel > 0) |
548 | 0 | { |
549 | 0 | nBlockXSize = 256; |
550 | 0 | nBlockYSize = 256; |
551 | 0 | poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", |
552 | 0 | GDAL_MDD_IMAGE_STRUCTURE); |
553 | 0 | } |
554 | 113k | else if (poPDFDS->m_nBlockXSize) |
555 | 60.7k | { |
556 | 60.7k | nBlockXSize = poPDFDS->m_nBlockXSize; |
557 | 60.7k | nBlockYSize = poPDFDS->m_nBlockYSize; |
558 | 60.7k | poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", |
559 | 60.7k | GDAL_MDD_IMAGE_STRUCTURE); |
560 | 60.7k | } |
561 | 53.2k | else if (nRasterXSize < 64 * 1024 * 1024 / nRasterYSize) |
562 | 53.1k | { |
563 | 53.1k | nBlockXSize = nRasterXSize; |
564 | 53.1k | nBlockYSize = 1; |
565 | 53.1k | } |
566 | 132 | else |
567 | 132 | { |
568 | 132 | nBlockXSize = std::min(1024, nRasterXSize); |
569 | 132 | nBlockYSize = std::min(1024, nRasterYSize); |
570 | 132 | poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", |
571 | 132 | GDAL_MDD_IMAGE_STRUCTURE); |
572 | 132 | } |
573 | 113k | } |
574 | | |
575 | | /************************************************************************/ |
576 | | /* InitOverviews() */ |
577 | | /************************************************************************/ |
578 | | |
579 | | void PDFDataset::InitOverviews() |
580 | 28.7k | { |
581 | | #ifdef HAVE_PDFIUM |
582 | | // Only if used pdfium, make "arbitrary overviews" |
583 | | // Blocks are 256x256 |
584 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
585 | | m_apoOvrDSBackup.empty()) |
586 | | { |
587 | | int nXSize = nRasterXSize; |
588 | | int nYSize = nRasterYSize; |
589 | | constexpr int minSize = 256; |
590 | | int nDiscard = 1; |
591 | | while (nXSize > minSize || nYSize > minSize) |
592 | | { |
593 | | nXSize = (nXSize + 1) / 2; |
594 | | nYSize = (nYSize + 1) / 2; |
595 | | |
596 | | auto poOvrDS = std::make_unique<PDFDataset>(this, nXSize, nYSize); |
597 | | |
598 | | for (int i = 0; i < nBands; i++) |
599 | | { |
600 | | auto poBand = std::make_unique<PDFRasterBand>(poOvrDS.get(), |
601 | | i + 1, nDiscard); |
602 | | poBand->SetSize(nXSize, nYSize); |
603 | | poOvrDS->SetBand(i + 1, std::move(poBand)); |
604 | | } |
605 | | |
606 | | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
607 | | ++nDiscard; |
608 | | } |
609 | | } |
610 | | #endif |
611 | 28.7k | #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) |
612 | 28.7k | if (!m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
613 | 7.90k | m_apoOvrDSBackup.empty() && m_osUserPwd != "ASK_INTERACTIVE") |
614 | 7.90k | { |
615 | 7.90k | int nXSize = nRasterXSize; |
616 | 7.90k | int nYSize = nRasterYSize; |
617 | 7.90k | constexpr int minSize = 256; |
618 | 7.90k | double dfDPI = m_dfDPI; |
619 | 28.1k | while (nXSize > minSize || nYSize > minSize) |
620 | 20.2k | { |
621 | 20.2k | nXSize = (nXSize + 1) / 2; |
622 | 20.2k | nYSize = (nYSize + 1) / 2; |
623 | 20.2k | dfDPI /= 2; |
624 | | |
625 | 20.2k | GDALOpenInfo oOpenInfo(GetDescription(), GA_ReadOnly); |
626 | 20.2k | CPLStringList aosOpenOptions(CSLDuplicate(papszOpenOptions)); |
627 | 20.2k | aosOpenOptions.SetNameValue("DPI", CPLSPrintf("%g", dfDPI)); |
628 | 20.2k | aosOpenOptions.SetNameValue("BANDS", CPLSPrintf("%d", nBands)); |
629 | 20.2k | aosOpenOptions.SetNameValue("@OPEN_FOR_OVERVIEW", "YES"); |
630 | 20.2k | if (!m_osUserPwd.empty()) |
631 | 0 | aosOpenOptions.SetNameValue("USER_PWD", m_osUserPwd.c_str()); |
632 | 20.2k | oOpenInfo.papszOpenOptions = aosOpenOptions.List(); |
633 | 20.2k | auto poOvrDS = std::unique_ptr<PDFDataset>(Open(&oOpenInfo)); |
634 | 20.2k | if (!poOvrDS || poOvrDS->nBands != nBands) |
635 | 19 | break; |
636 | 20.2k | poOvrDS->m_bIsOvrDS = true; |
637 | 20.2k | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
638 | 20.2k | } |
639 | 7.90k | } |
640 | 28.7k | #endif |
641 | 28.7k | } |
642 | | |
643 | | /************************************************************************/ |
644 | | /* GetColorInterpretation() */ |
645 | | /************************************************************************/ |
646 | | |
647 | | GDALColorInterp PDFRasterBand::GetColorInterpretation() |
648 | 0 | { |
649 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
650 | 0 | if (poGDS->nBands == 1) |
651 | 0 | return GCI_GrayIndex; |
652 | 0 | else |
653 | 0 | return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1)); |
654 | 0 | } |
655 | | |
656 | | /************************************************************************/ |
657 | | /* GetOverviewCount() */ |
658 | | /************************************************************************/ |
659 | | |
660 | | int PDFRasterBand::GetOverviewCount() |
661 | 30.9k | { |
662 | 30.9k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
663 | 30.9k | if (poGDS->m_bIsOvrDS) |
664 | 0 | return 0; |
665 | 30.9k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
666 | 2.25k | return GDALPamRasterBand::GetOverviewCount(); |
667 | 28.7k | else |
668 | 28.7k | { |
669 | 28.7k | poGDS->InitOverviews(); |
670 | 28.7k | return static_cast<int>(poGDS->m_apoOvrDS.size()); |
671 | 28.7k | } |
672 | 30.9k | } |
673 | | |
674 | | /************************************************************************/ |
675 | | /* GetOverview() */ |
676 | | /************************************************************************/ |
677 | | |
678 | | GDALRasterBand *PDFRasterBand::GetOverview(int iOverviewIndex) |
679 | 21.5k | { |
680 | 21.5k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
681 | 1.12k | return GDALPamRasterBand::GetOverview(iOverviewIndex); |
682 | | |
683 | 20.3k | else if (iOverviewIndex < 0 || iOverviewIndex >= GetOverviewCount()) |
684 | 0 | return nullptr; |
685 | 20.3k | else |
686 | 20.3k | { |
687 | 20.3k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
688 | 20.3k | return poGDS->m_apoOvrDS[iOverviewIndex]->GetRasterBand(nBand); |
689 | 20.3k | } |
690 | 21.5k | } |
691 | | |
692 | | /************************************************************************/ |
693 | | /* ~PDFRasterBand() */ |
694 | | /************************************************************************/ |
695 | | |
696 | | PDFRasterBand::~PDFRasterBand() |
697 | 114k | { |
698 | 114k | } |
699 | | |
700 | | /************************************************************************/ |
701 | | /* IReadBlockFromTile() */ |
702 | | /************************************************************************/ |
703 | | |
704 | | CPLErr PDFRasterBand::IReadBlockFromTile(int nBlockXOff, int nBlockYOff, |
705 | | void *pImage) |
706 | | |
707 | 0 | { |
708 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
709 | |
|
710 | 0 | const int nXOff = nBlockXOff * nBlockXSize; |
711 | 0 | const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff); |
712 | 0 | const int nYOff = nBlockYOff * nBlockYSize; |
713 | 0 | const int nReqYSize = std::min(nBlockYSize, nRasterYSize - nYOff); |
714 | |
|
715 | 0 | const int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize); |
716 | 0 | int iTile = poGDS->m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff]; |
717 | 0 | if (iTile < 0) |
718 | 0 | { |
719 | 0 | memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
720 | 0 | return CE_None; |
721 | 0 | } |
722 | | |
723 | 0 | GDALPDFTileDesc &sTile = poGDS->m_asTiles[iTile]; |
724 | 0 | GDALPDFObject *poImage = sTile.poImage; |
725 | |
|
726 | 0 | if (nBand == 4) |
727 | 0 | { |
728 | 0 | GDALPDFDictionary *poImageDict = poImage->GetDictionary(); |
729 | 0 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
730 | 0 | if (poSMask != nullptr && |
731 | 0 | poSMask->GetType() == PDFObjectType_Dictionary) |
732 | 0 | { |
733 | 0 | GDALPDFDictionary *poSMaskDict = poSMask->GetDictionary(); |
734 | 0 | GDALPDFObject *poWidth = poSMaskDict->Get("Width"); |
735 | 0 | GDALPDFObject *poHeight = poSMaskDict->Get("Height"); |
736 | 0 | GDALPDFObject *poColorSpace = poSMaskDict->Get("ColorSpace"); |
737 | 0 | GDALPDFObject *poBitsPerComponent = |
738 | 0 | poSMaskDict->Get("BitsPerComponent"); |
739 | 0 | double dfBits = 0; |
740 | 0 | if (poBitsPerComponent) |
741 | 0 | dfBits = Get(poBitsPerComponent); |
742 | 0 | if (poWidth && Get(poWidth) == nReqXSize && poHeight && |
743 | 0 | Get(poHeight) == nReqYSize && poColorSpace && |
744 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
745 | 0 | poColorSpace->GetName() == "DeviceGray" && |
746 | 0 | (dfBits == 1 || dfBits == 8)) |
747 | 0 | { |
748 | 0 | GDALPDFStream *poStream = poSMask->GetStream(); |
749 | 0 | GByte *pabyStream = nullptr; |
750 | |
|
751 | 0 | if (poStream == nullptr) |
752 | 0 | return CE_Failure; |
753 | | |
754 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
755 | 0 | if (pabyStream == nullptr) |
756 | 0 | return CE_Failure; |
757 | | |
758 | 0 | const int nReqXSize1 = (nReqXSize + 7) / 8; |
759 | 0 | if ((dfBits == 8 && |
760 | 0 | static_cast<size_t>(poStream->GetLength()) != |
761 | 0 | static_cast<size_t>(nReqXSize) * nReqYSize) || |
762 | 0 | (dfBits == 1 && |
763 | 0 | static_cast<size_t>(poStream->GetLength()) != |
764 | 0 | static_cast<size_t>(nReqXSize1) * nReqYSize)) |
765 | 0 | { |
766 | 0 | VSIFree(pabyStream); |
767 | 0 | return CE_Failure; |
768 | 0 | } |
769 | | |
770 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
771 | 0 | if (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize) |
772 | 0 | { |
773 | 0 | memset(pabyData, 0, |
774 | 0 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
775 | 0 | } |
776 | |
|
777 | 0 | if (dfBits == 8) |
778 | 0 | { |
779 | 0 | for (int j = 0; j < nReqYSize; j++) |
780 | 0 | { |
781 | 0 | for (int i = 0; i < nReqXSize; i++) |
782 | 0 | { |
783 | 0 | pabyData[j * nBlockXSize + i] = |
784 | 0 | pabyStream[j * nReqXSize + i]; |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
788 | 0 | else |
789 | 0 | { |
790 | 0 | for (int j = 0; j < nReqYSize; j++) |
791 | 0 | { |
792 | 0 | for (int i = 0; i < nReqXSize; i++) |
793 | 0 | { |
794 | 0 | if (pabyStream[j * nReqXSize1 + i / 8] & |
795 | 0 | (1 << (7 - (i % 8)))) |
796 | 0 | pabyData[j * nBlockXSize + i] = 255; |
797 | 0 | else |
798 | 0 | pabyData[j * nBlockXSize + i] = 0; |
799 | 0 | } |
800 | 0 | } |
801 | 0 | } |
802 | |
|
803 | 0 | VSIFree(pabyStream); |
804 | 0 | return CE_None; |
805 | 0 | } |
806 | 0 | } |
807 | | |
808 | 0 | memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
809 | 0 | return CE_None; |
810 | 0 | } |
811 | | |
812 | 0 | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
813 | 0 | poGDS->m_nLastBlockYOff == nBlockYOff && |
814 | 0 | poGDS->m_pabyCachedData != nullptr) |
815 | 0 | { |
816 | | #ifdef DEBUG |
817 | | CPLDebug("PDF", "Using cached block (%d, %d)", nBlockXOff, nBlockYOff); |
818 | | #endif |
819 | | // do nothing |
820 | 0 | } |
821 | 0 | else |
822 | 0 | { |
823 | 0 | if (!poGDS->m_bTried) |
824 | 0 | { |
825 | 0 | poGDS->m_bTried = true; |
826 | 0 | poGDS->m_pabyCachedData = |
827 | 0 | static_cast<GByte *>(VSIMalloc3(3, nBlockXSize, nBlockYSize)); |
828 | 0 | } |
829 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
830 | 0 | return CE_Failure; |
831 | | |
832 | 0 | GDALPDFStream *poStream = poImage->GetStream(); |
833 | 0 | GByte *pabyStream = nullptr; |
834 | |
|
835 | 0 | if (poStream == nullptr) |
836 | 0 | return CE_Failure; |
837 | | |
838 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
839 | 0 | if (pabyStream == nullptr) |
840 | 0 | return CE_Failure; |
841 | | |
842 | 0 | if (static_cast<size_t>(poStream->GetLength()) != |
843 | 0 | static_cast<size_t>(sTile.nBands) * nReqXSize * nReqYSize) |
844 | 0 | { |
845 | 0 | VSIFree(pabyStream); |
846 | 0 | return CE_Failure; |
847 | 0 | } |
848 | | |
849 | 0 | memcpy(poGDS->m_pabyCachedData, pabyStream, |
850 | 0 | static_cast<size_t>(poStream->GetLength())); |
851 | 0 | VSIFree(pabyStream); |
852 | 0 | poGDS->m_nLastBlockXOff = nBlockXOff; |
853 | 0 | poGDS->m_nLastBlockYOff = nBlockYOff; |
854 | 0 | } |
855 | | |
856 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
857 | 0 | if (nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize)) |
858 | 0 | { |
859 | 0 | memset(pabyData, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
860 | 0 | } |
861 | |
|
862 | 0 | if (poGDS->nBands >= 3 && sTile.nBands == 3) |
863 | 0 | { |
864 | 0 | for (int j = 0; j < nReqYSize; j++) |
865 | 0 | { |
866 | 0 | for (int i = 0; i < nReqXSize; i++) |
867 | 0 | { |
868 | 0 | pabyData[j * nBlockXSize + i] = |
869 | 0 | poGDS |
870 | 0 | ->m_pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1]; |
871 | 0 | } |
872 | 0 | } |
873 | 0 | } |
874 | 0 | else if (sTile.nBands == 1) |
875 | 0 | { |
876 | 0 | for (int j = 0; j < nReqYSize; j++) |
877 | 0 | { |
878 | 0 | for (int i = 0; i < nReqXSize; i++) |
879 | 0 | { |
880 | 0 | pabyData[j * nBlockXSize + i] = |
881 | 0 | poGDS->m_pabyCachedData[j * nReqXSize + i]; |
882 | 0 | } |
883 | 0 | } |
884 | 0 | } |
885 | |
|
886 | 0 | return CE_None; |
887 | 0 | } |
888 | | |
889 | | /************************************************************************/ |
890 | | /* GetSuggestedBlockAccessPattern() */ |
891 | | /************************************************************************/ |
892 | | |
893 | | GDALSuggestedBlockAccessPattern |
894 | | PDFRasterBand::GetSuggestedBlockAccessPattern() const |
895 | 0 | { |
896 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
897 | 0 | if (!poGDS->m_aiTiles.empty()) |
898 | 0 | return GSBAP_RANDOM; |
899 | 0 | return GSBAP_LARGEST_CHUNK_POSSIBLE; |
900 | 0 | } |
901 | | |
902 | | /************************************************************************/ |
903 | | /* IReadBlock() */ |
904 | | /************************************************************************/ |
905 | | |
906 | | CPLErr PDFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
907 | | |
908 | 10.1M | { |
909 | 10.1M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
910 | | |
911 | 10.1M | if (!poGDS->m_aiTiles.empty()) |
912 | 0 | { |
913 | 0 | if (IReadBlockFromTile(nBlockXOff, nBlockYOff, pImage) == CE_None) |
914 | 0 | { |
915 | 0 | return CE_None; |
916 | 0 | } |
917 | 0 | else |
918 | 0 | { |
919 | 0 | poGDS->m_aiTiles.resize(0); |
920 | 0 | poGDS->m_bTried = false; |
921 | 0 | CPLFree(poGDS->m_pabyCachedData); |
922 | 0 | poGDS->m_pabyCachedData = nullptr; |
923 | 0 | poGDS->m_nLastBlockXOff = -1; |
924 | 0 | poGDS->m_nLastBlockYOff = -1; |
925 | 0 | } |
926 | 0 | } |
927 | | |
928 | 10.1M | const int nXOff = nBlockXOff * nBlockXSize; |
929 | 10.1M | const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff); |
930 | 10.1M | const int nReqYSize = |
931 | 10.1M | nBlockYSize == 1 |
932 | 10.1M | ? nRasterYSize |
933 | 10.1M | : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize); |
934 | | |
935 | 10.1M | if (!poGDS->m_bTried) |
936 | 7.86k | { |
937 | 7.86k | poGDS->m_bTried = true; |
938 | 7.86k | if (nBlockYSize == 1) |
939 | 7.85k | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
940 | 7.85k | std::max(3, poGDS->nBands), nRasterXSize, nRasterYSize)); |
941 | 16 | else |
942 | 16 | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
943 | 16 | std::max(3, poGDS->nBands), nBlockXSize, nBlockYSize)); |
944 | 7.86k | } |
945 | 10.1M | if (poGDS->m_pabyCachedData == nullptr) |
946 | 0 | return CE_Failure; |
947 | | |
948 | 10.1M | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
949 | 10.1M | (nBlockYSize == 1 || poGDS->m_nLastBlockYOff == nBlockYOff) && |
950 | 10.1M | poGDS->m_pabyCachedData != nullptr) |
951 | 10.1M | { |
952 | | /*CPLDebug("PDF", "Using cached block (%d, %d)", |
953 | | nBlockXOff, nBlockYOff);*/ |
954 | | // do nothing |
955 | 10.1M | } |
956 | 7.86k | else |
957 | 7.86k | { |
958 | | #ifdef HAVE_PODOFO |
959 | | if (poGDS->m_bUseLib.test(PDFLIB_PODOFO) && nBand == 4) |
960 | | { |
961 | | memset(pImage, 255, nBlockXSize * nBlockYSize); |
962 | | return CE_None; |
963 | | } |
964 | | #endif |
965 | | |
966 | 7.86k | const int nReqXOff = nBlockXOff * nBlockXSize; |
967 | 7.86k | const int nReqYOff = (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize; |
968 | 7.86k | const GSpacing nPixelSpace = 1; |
969 | 7.86k | const GSpacing nLineSpace = nBlockXSize; |
970 | 7.86k | const GSpacing nBandSpace = |
971 | 7.86k | static_cast<GSpacing>(nBlockXSize) * |
972 | 7.86k | ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize); |
973 | | |
974 | 7.86k | CPLErr eErr = poGDS->ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
975 | 7.86k | nReqYSize, nPixelSpace, nLineSpace, |
976 | 7.86k | nBandSpace, poGDS->m_pabyCachedData); |
977 | | |
978 | 7.86k | if (eErr == CE_None) |
979 | 7.86k | { |
980 | 7.86k | poGDS->m_nLastBlockXOff = nBlockXOff; |
981 | 7.86k | poGDS->m_nLastBlockYOff = nBlockYOff; |
982 | 7.86k | } |
983 | 0 | else |
984 | 0 | { |
985 | 0 | CPLFree(poGDS->m_pabyCachedData); |
986 | 0 | poGDS->m_pabyCachedData = nullptr; |
987 | 0 | } |
988 | 7.86k | } |
989 | 10.1M | if (poGDS->m_pabyCachedData == nullptr) |
990 | 0 | return CE_Failure; |
991 | | |
992 | 10.1M | if (nBlockYSize == 1) |
993 | 10.1M | memcpy(pImage, |
994 | 10.1M | poGDS->m_pabyCachedData + |
995 | 10.1M | (nBand - 1) * nBlockXSize * nRasterYSize + |
996 | 10.1M | nBlockYOff * nBlockXSize, |
997 | 10.1M | nBlockXSize); |
998 | 16 | else |
999 | 16 | { |
1000 | 16 | memcpy(pImage, |
1001 | 16 | poGDS->m_pabyCachedData + |
1002 | 16 | static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize, |
1003 | 16 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
1004 | | |
1005 | 16 | if (poGDS->m_bCacheBlocksForOtherBands && nBand == 1) |
1006 | 16 | { |
1007 | 48 | for (int iBand = 2; iBand <= poGDS->nBands; ++iBand) |
1008 | 32 | { |
1009 | 32 | auto poOtherBand = cpl::down_cast<PDFRasterBand *>( |
1010 | 32 | poGDS->papoBands[iBand - 1]); |
1011 | 32 | GDALRasterBlock *poBlock = |
1012 | 32 | poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff); |
1013 | 32 | if (poBlock) |
1014 | 0 | { |
1015 | 0 | poBlock->DropLock(); |
1016 | 0 | } |
1017 | 32 | else |
1018 | 32 | { |
1019 | 32 | poBlock = poOtherBand->GetLockedBlockRef(nBlockXOff, |
1020 | 32 | nBlockYOff, TRUE); |
1021 | 32 | if (poBlock) |
1022 | 32 | { |
1023 | 32 | memcpy(poBlock->GetDataRef(), |
1024 | 32 | poGDS->m_pabyCachedData + |
1025 | 32 | static_cast<size_t>(iBand - 1) * |
1026 | 32 | nBlockXSize * nBlockYSize, |
1027 | 32 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
1028 | 32 | poBlock->DropLock(); |
1029 | 32 | } |
1030 | 32 | } |
1031 | 32 | } |
1032 | 16 | } |
1033 | 16 | } |
1034 | | |
1035 | 10.1M | return CE_None; |
1036 | 10.1M | } |
1037 | | |
1038 | | /************************************************************************/ |
1039 | | /* PDFEnterPasswordFromConsoleIfNeeded() */ |
1040 | | /************************************************************************/ |
1041 | | |
1042 | | static const char *PDFEnterPasswordFromConsoleIfNeeded(const char *pszUserPwd) |
1043 | 0 | { |
1044 | 0 | if (EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
1045 | 0 | { |
1046 | 0 | static char szPassword[81]; |
1047 | 0 | printf("Enter password (will be echo'ed in the console): "); /*ok*/ |
1048 | 0 | if (nullptr == fgets(szPassword, sizeof(szPassword), stdin)) |
1049 | 0 | { |
1050 | 0 | fprintf(stderr, "WARNING: Error getting password.\n"); /*ok*/ |
1051 | 0 | } |
1052 | 0 | szPassword[sizeof(szPassword) - 1] = 0; |
1053 | 0 | char *sz10 = strchr(szPassword, '\n'); |
1054 | 0 | if (sz10) |
1055 | 0 | *sz10 = 0; |
1056 | 0 | return szPassword; |
1057 | 0 | } |
1058 | 0 | return pszUserPwd; |
1059 | 0 | } |
1060 | | |
1061 | | #ifdef HAVE_PDFIUM |
1062 | | |
1063 | | /************************************************************************/ |
1064 | | /* Pdfium Load/Unload */ |
1065 | | /* Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/) */ |
1066 | | /* Author: Martin Mikita <martin.mikita@klokantech.com> */ |
1067 | | /************************************************************************/ |
1068 | | |
1069 | | // Flag for calling PDFium Init and Destroy methods |
1070 | | bool PDFDataset::g_bPdfiumInit = false; |
1071 | | |
1072 | | // Pdfium global read mutex - Pdfium is not multi-thread |
1073 | | static CPLMutex *g_oPdfiumReadMutex = nullptr; |
1074 | | static CPLMutex *g_oPdfiumLoadDocMutex = nullptr; |
1075 | | |
1076 | | // Comparison of char* for std::map |
1077 | | struct cmp_str |
1078 | | { |
1079 | | bool operator()(char const *a, char const *b) const |
1080 | | { |
1081 | | return strcmp(a, b) < 0; |
1082 | | } |
1083 | | }; |
1084 | | |
1085 | | static int GDALPdfiumGetBlock(void *param, unsigned long position, |
1086 | | unsigned char *pBuf, unsigned long size) |
1087 | | { |
1088 | | VSILFILE *fp = static_cast<VSILFILE *>(param); |
1089 | | VSIFSeekL(fp, static_cast<vsi_l_offset>(position), SEEK_SET); |
1090 | | return VSIFReadL(pBuf, size, 1, fp) == 1; |
1091 | | } |
1092 | | |
1093 | | // List of all PDF datasets |
1094 | | typedef std::map<const char *, TPdfiumDocumentStruct *, cmp_str> |
1095 | | TMapPdfiumDatasets; |
1096 | | static TMapPdfiumDatasets g_mPdfiumDatasets; |
1097 | | |
1098 | | /** |
1099 | | * Loading PDFIUM page |
1100 | | * - multithreading requires "mutex" |
1101 | | * - one page can require too much RAM |
1102 | | * - we will have one document per filename and one object per page |
1103 | | */ |
1104 | | |
1105 | | static int LoadPdfiumDocumentPage(const char *pszFilename, |
1106 | | const char *pszUserPwd, int pageNum, |
1107 | | TPdfiumDocumentStruct **doc, |
1108 | | TPdfiumPageStruct **page, int *pnPageCount) |
1109 | | { |
1110 | | // Prepare nullptr for error returning |
1111 | | if (doc) |
1112 | | *doc = nullptr; |
1113 | | if (page) |
1114 | | *page = nullptr; |
1115 | | if (pnPageCount) |
1116 | | *pnPageCount = 0; |
1117 | | |
1118 | | // Loading document and page must be only in one thread! |
1119 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1120 | | |
1121 | | // Library can be destroyed if every PDF dataset was closed! |
1122 | | if (!PDFDataset::g_bPdfiumInit) |
1123 | | { |
1124 | | FPDF_InitLibrary(); |
1125 | | PDFDataset::g_bPdfiumInit = TRUE; |
1126 | | } |
1127 | | |
1128 | | TMapPdfiumDatasets::iterator it; |
1129 | | it = g_mPdfiumDatasets.find(pszFilename); |
1130 | | TPdfiumDocumentStruct *poDoc = nullptr; |
1131 | | // Load new document if missing |
1132 | | if (it == g_mPdfiumDatasets.end()) |
1133 | | { |
1134 | | // Try without password (if PDF not requires password it can fail) |
1135 | | |
1136 | | VSILFILE *fp = VSIFOpenL(pszFilename, "rb"); |
1137 | | if (fp == nullptr) |
1138 | | { |
1139 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1140 | | return FALSE; |
1141 | | } |
1142 | | VSIFSeekL(fp, 0, SEEK_END); |
1143 | | const auto nFileLen64 = VSIFTellL(fp); |
1144 | | if constexpr (LONG_MAX < std::numeric_limits<vsi_l_offset>::max()) |
1145 | | { |
1146 | | if (nFileLen64 > LONG_MAX) |
1147 | | { |
1148 | | VSIFCloseL(fp); |
1149 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1150 | | return FALSE; |
1151 | | } |
1152 | | } |
1153 | | |
1154 | | FPDF_FILEACCESS *psFileAccess = new FPDF_FILEACCESS; |
1155 | | psFileAccess->m_Param = fp; |
1156 | | psFileAccess->m_FileLen = static_cast<unsigned long>(nFileLen64); |
1157 | | psFileAccess->m_GetBlock = GDALPdfiumGetBlock; |
1158 | | CPDF_Document *docPdfium = CPDFDocumentFromFPDFDocument( |
1159 | | FPDF_LoadCustomDocument(psFileAccess, nullptr)); |
1160 | | if (docPdfium == nullptr) |
1161 | | { |
1162 | | unsigned long err = FPDF_GetLastError(); |
1163 | | if (err == FPDF_ERR_PASSWORD) |
1164 | | { |
1165 | | if (pszUserPwd) |
1166 | | { |
1167 | | pszUserPwd = |
1168 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
1169 | | docPdfium = CPDFDocumentFromFPDFDocument( |
1170 | | FPDF_LoadCustomDocument(psFileAccess, pszUserPwd)); |
1171 | | if (docPdfium == nullptr) |
1172 | | err = FPDF_GetLastError(); |
1173 | | else |
1174 | | err = FPDF_ERR_SUCCESS; |
1175 | | } |
1176 | | else |
1177 | | { |
1178 | | CPLError(CE_Failure, CPLE_AppDefined, |
1179 | | "A password is needed. You can specify it through " |
1180 | | "the PDF_USER_PWD " |
1181 | | "configuration option / USER_PWD open option " |
1182 | | "(that can be set to ASK_INTERACTIVE)"); |
1183 | | |
1184 | | VSIFCloseL(fp); |
1185 | | delete psFileAccess; |
1186 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1187 | | return FALSE; |
1188 | | } |
1189 | | } // First Error Password [null password given] |
1190 | | if (err != FPDF_ERR_SUCCESS) |
1191 | | { |
1192 | | if (err == FPDF_ERR_PASSWORD) |
1193 | | CPLError(CE_Failure, CPLE_AppDefined, |
1194 | | "PDFium Invalid password."); |
1195 | | else if (err == FPDF_ERR_SECURITY) |
1196 | | CPLError(CE_Failure, CPLE_AppDefined, |
1197 | | "PDFium Unsupported security scheme."); |
1198 | | else if (err == FPDF_ERR_FORMAT) |
1199 | | CPLError(CE_Failure, CPLE_AppDefined, |
1200 | | "PDFium File not in PDF format or corrupted."); |
1201 | | else if (err == FPDF_ERR_FILE) |
1202 | | CPLError(CE_Failure, CPLE_AppDefined, |
1203 | | "PDFium File not found or could not be opened."); |
1204 | | else |
1205 | | CPLError(CE_Failure, CPLE_AppDefined, |
1206 | | "PDFium Unknown PDF error or invalid PDF."); |
1207 | | |
1208 | | VSIFCloseL(fp); |
1209 | | delete psFileAccess; |
1210 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1211 | | return FALSE; |
1212 | | } |
1213 | | } // ~ wrong PDF or password required |
1214 | | |
1215 | | // Create new poDoc |
1216 | | poDoc = new TPdfiumDocumentStruct; |
1217 | | if (!poDoc) |
1218 | | { |
1219 | | CPLError(CE_Failure, CPLE_AppDefined, |
1220 | | "Not enough memory for Pdfium Document object"); |
1221 | | |
1222 | | VSIFCloseL(fp); |
1223 | | delete psFileAccess; |
1224 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1225 | | return FALSE; |
1226 | | } |
1227 | | poDoc->filename = CPLStrdup(pszFilename); |
1228 | | poDoc->doc = docPdfium; |
1229 | | poDoc->psFileAccess = psFileAccess; |
1230 | | |
1231 | | g_mPdfiumDatasets[poDoc->filename] = poDoc; |
1232 | | } |
1233 | | // Document already loaded |
1234 | | else |
1235 | | { |
1236 | | poDoc = it->second; |
1237 | | } |
1238 | | |
1239 | | // Check page num in document |
1240 | | int nPages = poDoc->doc->GetPageCount(); |
1241 | | if (pageNum < 1 || pageNum > nPages) |
1242 | | { |
1243 | | CPLError(CE_Failure, CPLE_AppDefined, |
1244 | | "PDFium Invalid page number (%d/%d) for document %s", pageNum, |
1245 | | nPages, pszFilename); |
1246 | | |
1247 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1248 | | return FALSE; |
1249 | | } |
1250 | | |
1251 | | /* Sanity check to validate page count */ |
1252 | | if (pageNum != nPages) |
1253 | | { |
1254 | | if (poDoc->doc->GetPageDictionary(nPages - 1) == nullptr) |
1255 | | { |
1256 | | CPLError(CE_Failure, CPLE_AppDefined, |
1257 | | "Invalid PDF : invalid page count"); |
1258 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1259 | | return FALSE; |
1260 | | } |
1261 | | } |
1262 | | |
1263 | | TMapPdfiumPages::iterator itPage; |
1264 | | itPage = poDoc->pages.find(pageNum); |
1265 | | TPdfiumPageStruct *poPage = nullptr; |
1266 | | // Page not loaded |
1267 | | if (itPage == poDoc->pages.end()) |
1268 | | { |
1269 | | auto pDict = poDoc->doc->GetMutablePageDictionary(pageNum - 1); |
1270 | | if (pDict == nullptr) |
1271 | | { |
1272 | | CPLError(CE_Failure, CPLE_AppDefined, |
1273 | | "Invalid PDFium : invalid page"); |
1274 | | |
1275 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1276 | | return FALSE; |
1277 | | } |
1278 | | auto pPage = pdfium::MakeRetain<CPDF_Page>(poDoc->doc, pDict); |
1279 | | |
1280 | | poPage = new TPdfiumPageStruct; |
1281 | | if (!poPage) |
1282 | | { |
1283 | | CPLError(CE_Failure, CPLE_AppDefined, |
1284 | | "Not enough memory for Pdfium Page object"); |
1285 | | |
1286 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1287 | | return FALSE; |
1288 | | } |
1289 | | poPage->pageNum = pageNum; |
1290 | | poPage->page = pPage.Leak(); |
1291 | | poPage->readMutex = nullptr; |
1292 | | poPage->sharedNum = 0; |
1293 | | |
1294 | | poDoc->pages[pageNum] = poPage; |
1295 | | } |
1296 | | // Page already loaded |
1297 | | else |
1298 | | { |
1299 | | poPage = itPage->second; |
1300 | | } |
1301 | | |
1302 | | // Increase number of used |
1303 | | ++poPage->sharedNum; |
1304 | | |
1305 | | if (doc) |
1306 | | *doc = poDoc; |
1307 | | if (page) |
1308 | | *page = poPage; |
1309 | | if (pnPageCount) |
1310 | | *pnPageCount = nPages; |
1311 | | |
1312 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1313 | | |
1314 | | return TRUE; |
1315 | | } |
1316 | | |
1317 | | // ~ static int LoadPdfiumDocumentPage() |
1318 | | |
1319 | | static int UnloadPdfiumDocumentPage(TPdfiumDocumentStruct **doc, |
1320 | | TPdfiumPageStruct **page) |
1321 | | { |
1322 | | if (!doc || !page) |
1323 | | return FALSE; |
1324 | | |
1325 | | TPdfiumPageStruct *pPage = *page; |
1326 | | TPdfiumDocumentStruct *pDoc = *doc; |
1327 | | |
1328 | | // Get mutex for loading pdfium |
1329 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1330 | | |
1331 | | // Decrease page use |
1332 | | --pPage->sharedNum; |
1333 | | |
1334 | | #ifdef DEBUG |
1335 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: page shared num %d", |
1336 | | pPage->sharedNum); |
1337 | | #endif |
1338 | | // Page is used (also document) |
1339 | | if (pPage->sharedNum != 0) |
1340 | | { |
1341 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1342 | | return TRUE; |
1343 | | } |
1344 | | |
1345 | | // Get mutex, release and destroy it |
1346 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), PDFIUM_MUTEX_TIMEOUT); |
1347 | | CPLReleaseMutex(pPage->readMutex); |
1348 | | CPLDestroyMutex(pPage->readMutex); |
1349 | | // Close page and remove from map |
1350 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
1351 | | |
1352 | | pDoc->pages.erase(pPage->pageNum); |
1353 | | delete pPage; |
1354 | | pPage = nullptr; |
1355 | | |
1356 | | #ifdef DEBUG |
1357 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: pages %lu", |
1358 | | pDoc->pages.size()); |
1359 | | #endif |
1360 | | // Another page is used |
1361 | | if (!pDoc->pages.empty()) |
1362 | | { |
1363 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1364 | | return TRUE; |
1365 | | } |
1366 | | |
1367 | | // Close document and remove from map |
1368 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
1369 | | g_mPdfiumDatasets.erase(pDoc->filename); |
1370 | | CPLFree(pDoc->filename); |
1371 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
1372 | | delete pDoc->psFileAccess; |
1373 | | delete pDoc; |
1374 | | pDoc = nullptr; |
1375 | | |
1376 | | #ifdef DEBUG |
1377 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: documents %lu", |
1378 | | g_mPdfiumDatasets.size()); |
1379 | | #endif |
1380 | | // Another document is used |
1381 | | if (!g_mPdfiumDatasets.empty()) |
1382 | | { |
1383 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1384 | | return TRUE; |
1385 | | } |
1386 | | |
1387 | | #ifdef DEBUG |
1388 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: Nothing loaded, " |
1389 | | "destroy Library"); |
1390 | | #endif |
1391 | | // No document loaded, destroy pdfium |
1392 | | FPDF_DestroyLibrary(); |
1393 | | PDFDataset::g_bPdfiumInit = FALSE; |
1394 | | |
1395 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1396 | | |
1397 | | return TRUE; |
1398 | | } |
1399 | | |
1400 | | // ~ static int UnloadPdfiumDocumentPage() |
1401 | | |
1402 | | #endif // ~ HAVE_PDFIUM |
1403 | | |
1404 | | /************************************************************************/ |
1405 | | /* GetOption() */ |
1406 | | /************************************************************************/ |
1407 | | |
1408 | | const char *PDFDataset::GetOption(char **papszOpenOptionsIn, |
1409 | | const char *pszOptionName, |
1410 | | const char *pszDefaultVal) |
1411 | 161k | { |
1412 | 161k | CPLErr eLastErrType = CPLGetLastErrorType(); |
1413 | 161k | CPLErrorNum nLastErrno = CPLGetLastErrorNo(); |
1414 | 161k | CPLString osLastErrorMsg(CPLGetLastErrorMsg()); |
1415 | 161k | CPLXMLNode *psNode = CPLParseXMLString(PDFGetOpenOptionList()); |
1416 | 161k | CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg); |
1417 | 161k | if (psNode == nullptr) |
1418 | 0 | return pszDefaultVal; |
1419 | 161k | CPLXMLNode *psIter = psNode->psChild; |
1420 | 725k | while (psIter != nullptr) |
1421 | 725k | { |
1422 | 725k | if (EQUAL(CPLGetXMLValue(psIter, "name", ""), pszOptionName)) |
1423 | 161k | { |
1424 | 161k | const char *pszVal = |
1425 | 161k | CSLFetchNameValue(papszOpenOptionsIn, pszOptionName); |
1426 | 161k | if (pszVal != nullptr) |
1427 | 40.5k | { |
1428 | 40.5k | CPLDestroyXMLNode(psNode); |
1429 | 40.5k | return pszVal; |
1430 | 40.5k | } |
1431 | 121k | const char *pszAltConfigOption = |
1432 | 121k | CPLGetXMLValue(psIter, "alt_config_option", nullptr); |
1433 | 121k | if (pszAltConfigOption != nullptr) |
1434 | 121k | { |
1435 | 121k | pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal); |
1436 | 121k | CPLDestroyXMLNode(psNode); |
1437 | 121k | return pszVal; |
1438 | 121k | } |
1439 | 0 | CPLDestroyXMLNode(psNode); |
1440 | 0 | return pszDefaultVal; |
1441 | 121k | } |
1442 | 564k | psIter = psIter->psNext; |
1443 | 564k | } |
1444 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1445 | 0 | "Requesting an undocumented open option '%s'", pszOptionName); |
1446 | 0 | CPLDestroyXMLNode(psNode); |
1447 | 0 | return pszDefaultVal; |
1448 | 161k | } |
1449 | | |
1450 | | #ifdef HAVE_PDFIUM |
1451 | | |
1452 | | /************************************************************************/ |
1453 | | /* GDALPDFiumOCContext */ |
1454 | | /************************************************************************/ |
1455 | | |
1456 | | class GDALPDFiumOCContext final : public CPDF_OCContextInterface |
1457 | | { |
1458 | | PDFDataset *m_poDS; |
1459 | | RetainPtr<CPDF_OCContext> m_DefaultOCContext; |
1460 | | |
1461 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumOCContext) |
1462 | | |
1463 | | public: |
1464 | | GDALPDFiumOCContext(PDFDataset *poDS, CPDF_Document *pDoc, |
1465 | | CPDF_OCContext::UsageType usage) |
1466 | | : m_poDS(poDS), |
1467 | | m_DefaultOCContext(pdfium::MakeRetain<CPDF_OCContext>(pDoc, usage)) |
1468 | | { |
1469 | | } |
1470 | | |
1471 | | ~GDALPDFiumOCContext() override; |
1472 | | |
1473 | | virtual bool |
1474 | | CheckOCGDictVisible(const CPDF_Dictionary *pOCGDict) const override |
1475 | | { |
1476 | | // CPLDebug("PDF", "CheckOCGDictVisible(%d,%d)", |
1477 | | // pOCGDict->GetObjNum(), pOCGDict->GetGenNum() ); |
1478 | | PDFDataset::VisibilityState eVisibility = |
1479 | | m_poDS->GetVisibilityStateForOGCPdfium(pOCGDict->GetObjNum(), |
1480 | | pOCGDict->GetGenNum()); |
1481 | | if (eVisibility == PDFDataset::VISIBILITY_ON) |
1482 | | return true; |
1483 | | if (eVisibility == PDFDataset::VISIBILITY_OFF) |
1484 | | return false; |
1485 | | return m_DefaultOCContext->CheckOCGDictVisible(pOCGDict); |
1486 | | } |
1487 | | }; |
1488 | | |
1489 | | GDALPDFiumOCContext::~GDALPDFiumOCContext() = default; |
1490 | | |
1491 | | /************************************************************************/ |
1492 | | /* GDALPDFiumRenderDeviceDriver */ |
1493 | | /************************************************************************/ |
1494 | | |
1495 | | class GDALPDFiumRenderDeviceDriver final : public RenderDeviceDriverIface |
1496 | | { |
1497 | | std::unique_ptr<RenderDeviceDriverIface> m_poParent; |
1498 | | CFX_RenderDevice *device_; |
1499 | | |
1500 | | int bEnableVector; |
1501 | | int bEnableText; |
1502 | | int bEnableBitmap; |
1503 | | int bTemporaryEnableVectorForTextStroking; |
1504 | | |
1505 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumRenderDeviceDriver) |
1506 | | |
1507 | | public: |
1508 | | GDALPDFiumRenderDeviceDriver( |
1509 | | std::unique_ptr<RenderDeviceDriverIface> &&poParent, |
1510 | | CFX_RenderDevice *pDevice) |
1511 | | : m_poParent(std::move(poParent)), device_(pDevice), |
1512 | | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE), |
1513 | | bTemporaryEnableVectorForTextStroking(FALSE) |
1514 | | { |
1515 | | } |
1516 | | |
1517 | | ~GDALPDFiumRenderDeviceDriver() override; |
1518 | | |
1519 | | void SetEnableVector(int bFlag) |
1520 | | { |
1521 | | bEnableVector = bFlag; |
1522 | | } |
1523 | | |
1524 | | void SetEnableText(int bFlag) |
1525 | | { |
1526 | | bEnableText = bFlag; |
1527 | | } |
1528 | | |
1529 | | void SetEnableBitmap(int bFlag) |
1530 | | { |
1531 | | bEnableBitmap = bFlag; |
1532 | | } |
1533 | | |
1534 | | DeviceType GetDeviceType() const override |
1535 | | { |
1536 | | return m_poParent->GetDeviceType(); |
1537 | | } |
1538 | | |
1539 | | int GetDeviceCaps(int caps_id) const override |
1540 | | { |
1541 | | return m_poParent->GetDeviceCaps(caps_id); |
1542 | | } |
1543 | | |
1544 | | void SaveState() override |
1545 | | { |
1546 | | m_poParent->SaveState(); |
1547 | | } |
1548 | | |
1549 | | void RestoreState(bool bKeepSaved) override |
1550 | | { |
1551 | | m_poParent->RestoreState(bKeepSaved); |
1552 | | } |
1553 | | |
1554 | | void SetBaseClip(const FX_RECT &rect) override |
1555 | | { |
1556 | | m_poParent->SetBaseClip(rect); |
1557 | | } |
1558 | | |
1559 | | virtual bool |
1560 | | SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1561 | | const CFX_FillRenderOptions &fill_options) override |
1562 | | { |
1563 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1564 | | return true; |
1565 | | return m_poParent->SetClip_PathFill(path, pObject2Device, fill_options); |
1566 | | } |
1567 | | |
1568 | | virtual bool |
1569 | | SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1570 | | const CFX_GraphStateData *pGraphState) override |
1571 | | { |
1572 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1573 | | return true; |
1574 | | return m_poParent->SetClip_PathStroke(path, pObject2Device, |
1575 | | pGraphState); |
1576 | | } |
1577 | | |
1578 | | virtual bool DrawPath(const CFX_Path &path, |
1579 | | const CFX_Matrix *pObject2Device, |
1580 | | const CFX_GraphStateData *pGraphState, |
1581 | | uint32_t fill_color, uint32_t stroke_color, |
1582 | | const CFX_FillRenderOptions &fill_options) override |
1583 | | { |
1584 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1585 | | return true; |
1586 | | return m_poParent->DrawPath(path, pObject2Device, pGraphState, |
1587 | | fill_color, stroke_color, fill_options); |
1588 | | } |
1589 | | |
1590 | | bool FillRect(const FX_RECT &rect, uint32_t fill_color) override |
1591 | | { |
1592 | | return m_poParent->FillRect(rect, fill_color); |
1593 | | } |
1594 | | |
1595 | | virtual bool DrawCosmeticLine(const CFX_PointF &ptMoveTo, |
1596 | | const CFX_PointF &ptLineTo, |
1597 | | uint32_t color) override |
1598 | | { |
1599 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1600 | | return TRUE; |
1601 | | return m_poParent->DrawCosmeticLine(ptMoveTo, ptLineTo, color); |
1602 | | } |
1603 | | |
1604 | | FX_RECT GetClipBox() const override |
1605 | | { |
1606 | | return m_poParent->GetClipBox(); |
1607 | | } |
1608 | | |
1609 | | virtual bool GetDIBits(RetainPtr<CFX_DIBitmap> bitmap, int left, |
1610 | | int top) const override |
1611 | | { |
1612 | | return m_poParent->GetDIBits(std::move(bitmap), left, top); |
1613 | | } |
1614 | | |
1615 | | RetainPtr<const CFX_DIBitmap> GetBackDrop() const override |
1616 | | { |
1617 | | return m_poParent->GetBackDrop(); |
1618 | | } |
1619 | | |
1620 | | virtual bool SetDIBits(RetainPtr<const CFX_DIBBase> bitmap, uint32_t color, |
1621 | | const FX_RECT &src_rect, int dest_left, int dest_top, |
1622 | | BlendMode blend_type) override |
1623 | | { |
1624 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1625 | | return true; |
1626 | | return m_poParent->SetDIBits(std::move(bitmap), color, src_rect, |
1627 | | dest_left, dest_top, blend_type); |
1628 | | } |
1629 | | |
1630 | | virtual bool StretchDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1631 | | uint32_t color, int dest_left, int dest_top, |
1632 | | int dest_width, int dest_height, |
1633 | | const FX_RECT *pClipRect, |
1634 | | const FXDIB_ResampleOptions &options, |
1635 | | BlendMode blend_type) override |
1636 | | { |
1637 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1638 | | return true; |
1639 | | return m_poParent->StretchDIBits(std::move(bitmap), color, dest_left, |
1640 | | dest_top, dest_width, dest_height, |
1641 | | pClipRect, options, blend_type); |
1642 | | } |
1643 | | |
1644 | | virtual StartResult StartDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1645 | | float alpha, uint32_t color, |
1646 | | const CFX_Matrix &matrix, |
1647 | | const FXDIB_ResampleOptions &options, |
1648 | | BlendMode blend_type) override |
1649 | | { |
1650 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1651 | | return StartResult(Result::kSuccess, nullptr); |
1652 | | return m_poParent->StartDIBits(std::move(bitmap), alpha, color, matrix, |
1653 | | options, blend_type); |
1654 | | } |
1655 | | |
1656 | | virtual bool ContinueDIBits(Continuation *continuation, |
1657 | | PauseIndicatorIface *pPause) override |
1658 | | { |
1659 | | return m_poParent->ContinueDIBits(continuation, pPause); |
1660 | | } |
1661 | | |
1662 | | virtual bool DrawDeviceText(pdfium::span<const TextCharPos> pCharPos, |
1663 | | CFX_Font *pFont, |
1664 | | const CFX_Matrix &mtObject2Device, |
1665 | | float font_size, uint32_t color, |
1666 | | const CFX_TextRenderOptions &options) override |
1667 | | { |
1668 | | if (bEnableText) |
1669 | | { |
1670 | | // This is quite tricky. We call again the guy who called us |
1671 | | // (CFX_RenderDevice::DrawNormalText()) but we set a special flag to |
1672 | | // allow vector&raster operations so that the rendering will happen |
1673 | | // in the next phase |
1674 | | if (bTemporaryEnableVectorForTextStroking) |
1675 | | return FALSE; // this is the default behavior of the parent |
1676 | | bTemporaryEnableVectorForTextStroking = true; |
1677 | | bool bRet = device_->DrawNormalText( |
1678 | | pCharPos, pFont, font_size, mtObject2Device, color, options); |
1679 | | bTemporaryEnableVectorForTextStroking = FALSE; |
1680 | | return bRet; |
1681 | | } |
1682 | | else |
1683 | | return true; // pretend that we did the job |
1684 | | } |
1685 | | |
1686 | | int GetDriverType() const override |
1687 | | { |
1688 | | return m_poParent->GetDriverType(); |
1689 | | } |
1690 | | |
1691 | | #if defined(_SKIA_SUPPORT_) |
1692 | | virtual bool DrawShading(const CPDF_ShadingPattern &pattern, |
1693 | | const CFX_Matrix &matrix, const FX_RECT &clip_rect, |
1694 | | int alpha) override |
1695 | | { |
1696 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1697 | | return true; |
1698 | | return m_poParent->DrawShading(pattern, matrix, clip_rect, alpha); |
1699 | | } |
1700 | | #endif |
1701 | | |
1702 | | bool MultiplyAlpha(float alpha) override |
1703 | | { |
1704 | | return m_poParent->MultiplyAlpha(alpha); |
1705 | | } |
1706 | | |
1707 | | bool MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) override |
1708 | | { |
1709 | | return m_poParent->MultiplyAlphaMask(std::move(mask)); |
1710 | | } |
1711 | | |
1712 | | #if defined(_SKIA_SUPPORT_) |
1713 | | virtual bool SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap, |
1714 | | RetainPtr<const CFX_DIBBase> mask, int left, |
1715 | | int top, float alpha, |
1716 | | BlendMode blend_type) override |
1717 | | { |
1718 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1719 | | return true; |
1720 | | return m_poParent->SetBitsWithMask(std::move(bitmap), std::move(mask), |
1721 | | left, top, alpha, blend_type); |
1722 | | } |
1723 | | |
1724 | | void SetGroupKnockout(bool group_knockout) override |
1725 | | { |
1726 | | m_poParent->SetGroupKnockout(group_knockout); |
1727 | | } |
1728 | | #endif |
1729 | | #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ |
1730 | | void Flush() override |
1731 | | { |
1732 | | return m_poParent->Flush(); |
1733 | | } |
1734 | | #endif |
1735 | | }; |
1736 | | |
1737 | | GDALPDFiumRenderDeviceDriver::~GDALPDFiumRenderDeviceDriver() = default; |
1738 | | |
1739 | | /************************************************************************/ |
1740 | | /* PDFiumRenderPageBitmap() */ |
1741 | | /************************************************************************/ |
1742 | | |
1743 | | /* This method is a customization of RenderPageImpl() |
1744 | | from pdfium/fpdfsdk/cpdfsdk_renderpage.cpp to allow selection of which OGC/layer are |
1745 | | active. Thus it inherits the following license */ |
1746 | | // Copyright 2014-2020 PDFium Authors. All rights reserved. |
1747 | | // |
1748 | | // Redistribution and use in source and binary forms, with or without |
1749 | | // modification, are permitted provided that the following conditions are |
1750 | | // met: |
1751 | | // |
1752 | | // * Redistributions of source code must retain the above copyright |
1753 | | // notice, this list of conditions and the following disclaimer. |
1754 | | // * Redistributions in binary form must reproduce the above |
1755 | | // copyright notice, this list of conditions and the following disclaimer |
1756 | | // in the documentation and/or other materials provided with the |
1757 | | // distribution. |
1758 | | // * Neither the name of Google Inc. nor the names of its |
1759 | | // contributors may be used to endorse or promote products derived from |
1760 | | // this software without specific prior written permission. |
1761 | | // |
1762 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
1763 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
1764 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
1765 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
1766 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
1767 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
1768 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
1769 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
1770 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1771 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
1772 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1773 | | |
1774 | | static void myRenderPageImpl(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1775 | | CPDF_Page *pPage, const CFX_Matrix &matrix, |
1776 | | const FX_RECT &clipping_rect, int flags, |
1777 | | const FPDF_COLORSCHEME *color_scheme, |
1778 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1779 | | { |
1780 | | if (!pContext->options_) |
1781 | | pContext->options_ = std::make_unique<CPDF_RenderOptions>(); |
1782 | | |
1783 | | auto &options = pContext->options_->GetOptions(); |
1784 | | options.bClearType = !!(flags & FPDF_LCD_TEXT); |
1785 | | options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT); |
1786 | | options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE); |
1787 | | options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE); |
1788 | | options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT); |
1789 | | options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE); |
1790 | | options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH); |
1791 | | |
1792 | | // Grayscale output |
1793 | | if (flags & FPDF_GRAYSCALE) |
1794 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kGray); |
1795 | | |
1796 | | if (color_scheme) |
1797 | | { |
1798 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kForcedColor); |
1799 | | SetColorFromScheme(color_scheme, pContext->options_.get()); |
1800 | | options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE); |
1801 | | } |
1802 | | |
1803 | | const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING) |
1804 | | ? CPDF_OCContext::kPrint |
1805 | | : CPDF_OCContext::kView; |
1806 | | pContext->options_->SetOCContext(pdfium::MakeRetain<GDALPDFiumOCContext>( |
1807 | | poDS, pPage->GetDocument(), usage)); |
1808 | | |
1809 | | pContext->device_->SaveState(); |
1810 | | pContext->device_->SetBaseClip(clipping_rect); |
1811 | | pContext->device_->SetClip_Rect(clipping_rect); |
1812 | | pContext->context_ = std::make_unique<CPDF_RenderContext>( |
1813 | | pPage->GetDocument(), pPage->GetMutablePageResources(), |
1814 | | pPage->GetPageImageCache()); |
1815 | | |
1816 | | pContext->context_->AppendLayer(pPage, matrix); |
1817 | | |
1818 | | if (flags & FPDF_ANNOT) |
1819 | | { |
1820 | | auto pOwnedList = std::make_unique<CPDF_AnnotList>(pPage); |
1821 | | CPDF_AnnotList *pList = pOwnedList.get(); |
1822 | | pContext->annots_ = std::move(pOwnedList); |
1823 | | bool bPrinting = |
1824 | | pContext->device_->GetDeviceType() != DeviceType::kDisplay; |
1825 | | |
1826 | | // TODO(https://crbug.com/pdfium/993) - maybe pass true here. |
1827 | | const bool bShowWidget = false; |
1828 | | pList->DisplayAnnots(pContext->context_.get(), bPrinting, matrix, |
1829 | | bShowWidget); |
1830 | | } |
1831 | | |
1832 | | pContext->renderer_ = std::make_unique<CPDF_ProgressiveRenderer>( |
1833 | | pContext->context_.get(), pContext->device_.get(), |
1834 | | pContext->options_.get()); |
1835 | | pContext->renderer_->Start(pause); |
1836 | | if (bNeedToRestore) |
1837 | | pContext->device_->RestoreState(false); |
1838 | | } |
1839 | | |
1840 | | static void |
1841 | | myRenderPageWithContext(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1842 | | FPDF_PAGE page, int start_x, int start_y, int size_x, |
1843 | | int size_y, int rotate, int flags, |
1844 | | const FPDF_COLORSCHEME *color_scheme, |
1845 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1846 | | { |
1847 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1848 | | if (!pPage) |
1849 | | return; |
1850 | | |
1851 | | const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); |
1852 | | myRenderPageImpl(poDS, pContext, pPage, |
1853 | | pPage->GetDisplayMatrixForRect(rect, rotate), rect, flags, |
1854 | | color_scheme, bNeedToRestore, pause); |
1855 | | } |
1856 | | |
1857 | | namespace |
1858 | | { |
1859 | | class MyRenderDevice final : public CFX_RenderDevice |
1860 | | { |
1861 | | |
1862 | | public: |
1863 | | ~MyRenderDevice() override; |
1864 | | |
1865 | | // Substitution for CFX_DefaultRenderDevice::Attach |
1866 | | bool Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, bool bRgbByteOrder, |
1867 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1868 | | bool bGroupKnockout, const char *pszRenderingOptions); |
1869 | | }; |
1870 | | |
1871 | | MyRenderDevice::~MyRenderDevice() = default; |
1872 | | |
1873 | | bool MyRenderDevice::Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, |
1874 | | bool bRgbByteOrder, |
1875 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1876 | | bool bGroupKnockout, |
1877 | | const char *pszRenderingOptions) |
1878 | | { |
1879 | | SetBitmap(pBitmap); |
1880 | | |
1881 | | std::unique_ptr<RenderDeviceDriverIface> driver = |
1882 | | std::make_unique<pdfium::CFX_AggDeviceDriver>( |
1883 | | pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout); |
1884 | | if (pszRenderingOptions != nullptr) |
1885 | | { |
1886 | | int bEnableVector = FALSE; |
1887 | | int bEnableText = FALSE; |
1888 | | int bEnableBitmap = FALSE; |
1889 | | |
1890 | | char **papszTokens = CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
1891 | | for (int i = 0; papszTokens[i] != nullptr; i++) |
1892 | | { |
1893 | | if (EQUAL(papszTokens[i], "VECTOR")) |
1894 | | bEnableVector = TRUE; |
1895 | | else if (EQUAL(papszTokens[i], "TEXT")) |
1896 | | bEnableText = TRUE; |
1897 | | else if (EQUAL(papszTokens[i], "RASTER") || |
1898 | | EQUAL(papszTokens[i], "BITMAP")) |
1899 | | bEnableBitmap = TRUE; |
1900 | | else |
1901 | | { |
1902 | | CPLError(CE_Warning, CPLE_NotSupported, |
1903 | | "Value %s is not a valid value for " |
1904 | | "GDAL_PDF_RENDERING_OPTIONS", |
1905 | | papszTokens[i]); |
1906 | | } |
1907 | | } |
1908 | | CSLDestroy(papszTokens); |
1909 | | |
1910 | | if (!bEnableVector || !bEnableText || !bEnableBitmap) |
1911 | | { |
1912 | | std::unique_ptr<GDALPDFiumRenderDeviceDriver> poGDALRDDriver = |
1913 | | std::make_unique<GDALPDFiumRenderDeviceDriver>( |
1914 | | std::move(driver), this); |
1915 | | poGDALRDDriver->SetEnableVector(bEnableVector); |
1916 | | poGDALRDDriver->SetEnableText(bEnableText); |
1917 | | poGDALRDDriver->SetEnableBitmap(bEnableBitmap); |
1918 | | driver = std::move(poGDALRDDriver); |
1919 | | } |
1920 | | } |
1921 | | |
1922 | | SetDeviceDriver(std::move(driver)); |
1923 | | return true; |
1924 | | } |
1925 | | } // namespace |
1926 | | |
1927 | | void PDFDataset::PDFiumRenderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, |
1928 | | int start_x, int start_y, int size_x, |
1929 | | int size_y, |
1930 | | const char *pszRenderingOptions) |
1931 | | { |
1932 | | const int rotate = 0; |
1933 | | const int flags = 0; |
1934 | | |
1935 | | if (!bitmap) |
1936 | | return; |
1937 | | |
1938 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1939 | | if (!pPage) |
1940 | | return; |
1941 | | |
1942 | | auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>(); |
1943 | | CPDF_PageRenderContext *pContext = pOwnedContext.get(); |
1944 | | CPDF_Page::RenderContextClearer clearer(pPage); |
1945 | | pPage->SetRenderContext(std::move(pOwnedContext)); |
1946 | | |
1947 | | auto pOwnedDevice = std::make_unique<MyRenderDevice>(); |
1948 | | auto pDevice = pOwnedDevice.get(); |
1949 | | pContext->device_ = std::move(pOwnedDevice); |
1950 | | |
1951 | | RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); |
1952 | | |
1953 | | pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, |
1954 | | false, pszRenderingOptions); |
1955 | | |
1956 | | myRenderPageWithContext(this, pContext, page, start_x, start_y, size_x, |
1957 | | size_y, rotate, flags, |
1958 | | /*color_scheme=*/nullptr, |
1959 | | /*need_to_restore=*/true, /*pause=*/nullptr); |
1960 | | |
1961 | | #ifdef _SKIA_SUPPORT_PATHS_ |
1962 | | pDevice->Flush(true); |
1963 | | pBitmap->UnPreMultiply(); |
1964 | | #endif |
1965 | | } |
1966 | | |
1967 | | #endif /* HAVE_PDFIUM */ |
1968 | | |
1969 | | /************************************************************************/ |
1970 | | /* ReadPixels() */ |
1971 | | /************************************************************************/ |
1972 | | |
1973 | | CPLErr PDFDataset::ReadPixels(int nReqXOff, int nReqYOff, int nReqXSize, |
1974 | | int nReqYSize, GSpacing nPixelSpace, |
1975 | | GSpacing nLineSpace, GSpacing nBandSpace, |
1976 | | GByte *pabyData) |
1977 | 7.86k | { |
1978 | 7.86k | CPLErr eErr = CE_None; |
1979 | 7.86k | const char *pszRenderingOptions = |
1980 | 7.86k | GetOption(papszOpenOptions, "RENDERING_OPTIONS", nullptr); |
1981 | | |
1982 | 7.86k | #ifdef HAVE_POPPLER |
1983 | 7.86k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
1984 | 7.86k | { |
1985 | 7.86k | SplashColor sColor; |
1986 | 7.86k | sColor[0] = 255; |
1987 | 7.86k | sColor[1] = 255; |
1988 | 7.86k | sColor[2] = 255; |
1989 | 7.86k | GDALPDFOutputDev *poSplashOut = new GDALPDFOutputDev( |
1990 | 7.86k | (nBands < 4) ? splashModeRGB8 : splashModeXBGR8, 4, false, |
1991 | 7.86k | (nBands < 4) ? sColor : nullptr); |
1992 | | |
1993 | 7.86k | if (pszRenderingOptions != nullptr) |
1994 | 7.86k | { |
1995 | 7.86k | poSplashOut->SetEnableVector(FALSE); |
1996 | 7.86k | poSplashOut->SetEnableText(FALSE); |
1997 | 7.86k | poSplashOut->SetEnableBitmap(FALSE); |
1998 | | |
1999 | 7.86k | char **papszTokens = |
2000 | 7.86k | CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
2001 | 23.5k | for (int i = 0; papszTokens[i] != nullptr; i++) |
2002 | 15.7k | { |
2003 | 15.7k | if (EQUAL(papszTokens[i], "VECTOR")) |
2004 | 7.86k | poSplashOut->SetEnableVector(TRUE); |
2005 | 7.86k | else if (EQUAL(papszTokens[i], "TEXT")) |
2006 | 0 | poSplashOut->SetEnableText(TRUE); |
2007 | 7.86k | else if (EQUAL(papszTokens[i], "RASTER") || |
2008 | 0 | EQUAL(papszTokens[i], "BITMAP")) |
2009 | 7.86k | poSplashOut->SetEnableBitmap(TRUE); |
2010 | 0 | else |
2011 | 0 | { |
2012 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
2013 | 0 | "Value %s is not a valid value for " |
2014 | 0 | "GDAL_PDF_RENDERING_OPTIONS", |
2015 | 0 | papszTokens[i]); |
2016 | 0 | } |
2017 | 15.7k | } |
2018 | 7.86k | CSLDestroy(papszTokens); |
2019 | 7.86k | } |
2020 | | |
2021 | 7.86k | PDFDoc *poDoc = m_poDocPoppler; |
2022 | 7.86k | poSplashOut->startDoc(poDoc); |
2023 | | |
2024 | | // Note: Poppler 25.2 is certainly not the lowest version where we can |
2025 | | // avoid the hack. |
2026 | 7.86k | #if !(POPPLER_MAJOR_VERSION > 25 || \ |
2027 | 7.86k | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)) |
2028 | 7.86k | #define USE_OPTCONTENT_HACK |
2029 | 7.86k | #endif |
2030 | | |
2031 | 7.86k | #ifdef USE_OPTCONTENT_HACK |
2032 | | /* EVIL: we modify a private member... */ |
2033 | | /* poppler (at least 0.12 and 0.14 versions) don't render correctly */ |
2034 | | /* some PDFs and display an error message 'Could not find a OCG with |
2035 | | * Ref' */ |
2036 | | /* in those cases. This processing of optional content is an addition of |
2037 | | */ |
2038 | | /* poppler in comparison to original xpdf, which hasn't the issue. All |
2039 | | * in */ |
2040 | | /* all, nullifying optContent removes the error message and improves the |
2041 | | * rendering */ |
2042 | 7.86k | Catalog *poCatalog = poDoc->getCatalog(); |
2043 | 7.86k | OCGs *poOldOCGs = poCatalog->optContent; |
2044 | 7.86k | if (!m_bUseOCG) |
2045 | 7.86k | poCatalog->optContent = nullptr; |
2046 | 7.86k | #endif |
2047 | 7.86k | try |
2048 | 7.86k | { |
2049 | 7.86k | poDoc->displayPageSlice(poSplashOut, m_iPage, m_dfDPI, m_dfDPI, 0, |
2050 | 7.86k | TRUE, false, false, nReqXOff, nReqYOff, |
2051 | 7.86k | nReqXSize, nReqYSize); |
2052 | 7.86k | } |
2053 | 7.86k | catch (const std::exception &e) |
2054 | 7.86k | { |
2055 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2056 | 0 | "PDFDoc::displayPageSlice() failed with %s", e.what()); |
2057 | |
|
2058 | 0 | #ifdef USE_OPTCONTENT_HACK |
2059 | | /* Restore back */ |
2060 | 0 | poCatalog->optContent = poOldOCGs; |
2061 | 0 | #endif |
2062 | 0 | delete poSplashOut; |
2063 | 0 | return CE_Failure; |
2064 | 0 | } |
2065 | | |
2066 | 0 | #ifdef USE_OPTCONTENT_HACK |
2067 | | /* Restore back */ |
2068 | 7.86k | poCatalog->optContent = poOldOCGs; |
2069 | 7.86k | #endif |
2070 | | |
2071 | 7.86k | SplashBitmap *poBitmap = poSplashOut->getBitmap(); |
2072 | 7.86k | if (poBitmap->getWidth() != nReqXSize || |
2073 | 7.86k | poBitmap->getHeight() != nReqYSize) |
2074 | 0 | { |
2075 | 0 | CPLError( |
2076 | 0 | CE_Failure, CPLE_AppDefined, |
2077 | 0 | "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)", |
2078 | 0 | poBitmap->getWidth(), poBitmap->getHeight(), nReqXSize, |
2079 | 0 | nReqYSize); |
2080 | 0 | delete poSplashOut; |
2081 | 0 | return CE_Failure; |
2082 | 0 | } |
2083 | | |
2084 | 7.86k | GByte *pabyDataR = pabyData; |
2085 | 7.86k | GByte *pabyDataG = pabyData + nBandSpace; |
2086 | 7.86k | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2087 | 7.86k | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2088 | 7.86k | GByte *pabySrc = poBitmap->getDataPtr(); |
2089 | 7.86k | GByte *pabyAlphaSrc = |
2090 | 7.86k | reinterpret_cast<GByte *>(poBitmap->getAlphaPtr()); |
2091 | 7.86k | int i, j; |
2092 | 10.2M | for (j = 0; j < nReqYSize; j++) |
2093 | 10.2M | { |
2094 | 13.1G | for (i = 0; i < nReqXSize; i++) |
2095 | 13.1G | { |
2096 | 13.1G | if (nBands < 4) |
2097 | 13.1G | { |
2098 | 13.1G | pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0]; |
2099 | 13.1G | pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1]; |
2100 | 13.1G | pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2]; |
2101 | 13.1G | } |
2102 | 0 | else |
2103 | 0 | { |
2104 | 0 | pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2]; |
2105 | 0 | pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1]; |
2106 | 0 | pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0]; |
2107 | 0 | pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i]; |
2108 | 0 | } |
2109 | 13.1G | } |
2110 | 10.2M | pabyDataR += nLineSpace; |
2111 | 10.2M | pabyDataG += nLineSpace; |
2112 | 10.2M | pabyDataB += nLineSpace; |
2113 | 10.2M | pabyDataA += nLineSpace; |
2114 | 10.2M | pabyAlphaSrc += poBitmap->getAlphaRowSize(); |
2115 | 10.2M | pabySrc += poBitmap->getRowSize(); |
2116 | 10.2M | } |
2117 | 7.86k | delete poSplashOut; |
2118 | 7.86k | } |
2119 | 7.86k | #endif // HAVE_POPPLER |
2120 | | |
2121 | | #ifdef HAVE_PODOFO |
2122 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2123 | | { |
2124 | | if (m_bPdfToPpmFailed) |
2125 | | return CE_Failure; |
2126 | | |
2127 | | if (pszRenderingOptions != nullptr && |
2128 | | !EQUAL(pszRenderingOptions, "RASTER,VECTOR,TEXT")) |
2129 | | { |
2130 | | CPLError(CE_Warning, CPLE_NotSupported, |
2131 | | "GDAL_PDF_RENDERING_OPTIONS only supported " |
2132 | | "when PDF lib is Poppler."); |
2133 | | } |
2134 | | |
2135 | | CPLString osTmpFilename; |
2136 | | int nRet; |
2137 | | |
2138 | | #ifdef notdef |
2139 | | int bUseSpawn = |
2140 | | CPLTestBool(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES")); |
2141 | | if (!bUseSpawn) |
2142 | | { |
2143 | | CPLString osCmd = CPLSPrintf( |
2144 | | "pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"", |
2145 | | dfDPI, nReqXOff, nReqYOff, nReqXSize, nReqYSize, iPage, iPage, |
2146 | | osFilename.c_str()); |
2147 | | |
2148 | | if (!osUserPwd.empty()) |
2149 | | { |
2150 | | osCmd += " -upw \""; |
2151 | | osCmd += osUserPwd; |
2152 | | osCmd += "\""; |
2153 | | } |
2154 | | |
2155 | | CPLString osTmpFilenamePrefix = CPLGenerateTempFilenameSafe("pdf"); |
2156 | | osTmpFilename = |
2157 | | CPLSPrintf("%s-%d.ppm", osTmpFilenamePrefix.c_str(), iPage); |
2158 | | osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str()); |
2159 | | |
2160 | | CPLDebug("PDF", "Running '%s'", osCmd.c_str()); |
2161 | | nRet = CPLSystem(nullptr, osCmd.c_str()); |
2162 | | } |
2163 | | else |
2164 | | #endif // notdef |
2165 | | { |
2166 | | char **papszArgs = nullptr; |
2167 | | papszArgs = CSLAddString(papszArgs, "pdftoppm"); |
2168 | | papszArgs = CSLAddString(papszArgs, "-r"); |
2169 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", m_dfDPI)); |
2170 | | papszArgs = CSLAddString(papszArgs, "-x"); |
2171 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff)); |
2172 | | papszArgs = CSLAddString(papszArgs, "-y"); |
2173 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff)); |
2174 | | papszArgs = CSLAddString(papszArgs, "-W"); |
2175 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize)); |
2176 | | papszArgs = CSLAddString(papszArgs, "-H"); |
2177 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize)); |
2178 | | papszArgs = CSLAddString(papszArgs, "-f"); |
2179 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2180 | | papszArgs = CSLAddString(papszArgs, "-l"); |
2181 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2182 | | if (!m_osUserPwd.empty()) |
2183 | | { |
2184 | | papszArgs = CSLAddString(papszArgs, "-upw"); |
2185 | | papszArgs = CSLAddString(papszArgs, m_osUserPwd.c_str()); |
2186 | | } |
2187 | | papszArgs = CSLAddString(papszArgs, m_osFilename.c_str()); |
2188 | | |
2189 | | osTmpFilename = VSIMemGenerateHiddenFilename("pdf_temp.ppm"); |
2190 | | VSILFILE *fpOut = VSIFOpenL(osTmpFilename, "wb"); |
2191 | | if (fpOut != nullptr) |
2192 | | { |
2193 | | nRet = CPLSpawn(papszArgs, nullptr, fpOut, FALSE); |
2194 | | VSIFCloseL(fpOut); |
2195 | | } |
2196 | | else |
2197 | | nRet = -1; |
2198 | | |
2199 | | CSLDestroy(papszArgs); |
2200 | | } |
2201 | | |
2202 | | if (nRet == 0) |
2203 | | { |
2204 | | auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
2205 | | osTmpFilename, GDAL_OF_RASTER, nullptr, nullptr, nullptr)); |
2206 | | if (poDS) |
2207 | | { |
2208 | | if (poDS->GetRasterCount() == 3) |
2209 | | { |
2210 | | eErr = poDS->RasterIO(GF_Read, 0, 0, nReqXSize, nReqYSize, |
2211 | | pabyData, nReqXSize, nReqYSize, |
2212 | | GDT_UInt8, 3, nullptr, nPixelSpace, |
2213 | | nLineSpace, nBandSpace, nullptr); |
2214 | | } |
2215 | | } |
2216 | | } |
2217 | | else |
2218 | | { |
2219 | | CPLDebug("PDF", "Ret code = %d", nRet); |
2220 | | m_bPdfToPpmFailed = true; |
2221 | | eErr = CE_Failure; |
2222 | | } |
2223 | | VSIUnlink(osTmpFilename); |
2224 | | } |
2225 | | #endif // HAVE_PODOFO |
2226 | | #ifdef HAVE_PDFIUM |
2227 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2228 | | { |
2229 | | if (!m_poPagePdfium) |
2230 | | { |
2231 | | return CE_Failure; |
2232 | | } |
2233 | | |
2234 | | // Pdfium does not support multithreading |
2235 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, PDFIUM_MUTEX_TIMEOUT); |
2236 | | |
2237 | | CPLCreateOrAcquireMutex(&(m_poPagePdfium->readMutex), |
2238 | | PDFIUM_MUTEX_TIMEOUT); |
2239 | | |
2240 | | // Parsing content required before rastering |
2241 | | // can takes too long for PDF with large number of objects/layers |
2242 | | m_poPagePdfium->page->ParseContent(); |
2243 | | |
2244 | | FPDF_BITMAP bitmap = |
2245 | | FPDFBitmap_Create(nReqXSize, nReqYSize, nBands == 4 /*alpha*/); |
2246 | | // As coded now, FPDFBitmap_Create cannot allocate more than 1 GB |
2247 | | if (bitmap == nullptr) |
2248 | | { |
2249 | | // Release mutex - following code is thread-safe |
2250 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2251 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2252 | | |
2253 | | #ifdef notdef |
2254 | | // If the requested area is not too small, then try subdividing |
2255 | | if ((GIntBig)nReqXSize * nReqYSize * 4 > 1024 * 1024) |
2256 | | { |
2257 | | #ifdef DEBUG |
2258 | | CPLDebug( |
2259 | | "PDF", |
2260 | | "Subdividing PDFDataset::ReadPixels(%d, %d, %d, %d, " |
2261 | | "scaleFactor=%d)", |
2262 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2263 | | 1 << ((PDFRasterBand *)GetRasterBand(1))->nResolutionLevel); |
2264 | | #endif |
2265 | | if (nReqXSize >= nReqYSize) |
2266 | | { |
2267 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize / 2, |
2268 | | nReqYSize, nPixelSpace, nLineSpace, |
2269 | | nBandSpace, pabyData); |
2270 | | if (eErr == CE_None) |
2271 | | { |
2272 | | eErr = ReadPixels( |
2273 | | nReqXSize / 2, nReqYOff, nReqXSize - nReqXSize / 2, |
2274 | | nReqYSize, nPixelSpace, nLineSpace, nBandSpace, |
2275 | | pabyData + nPixelSpace * (nReqXSize / 2)); |
2276 | | } |
2277 | | } |
2278 | | else |
2279 | | { |
2280 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
2281 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2282 | | nLineSpace, nBandSpace, pabyData); |
2283 | | if (eErr == CE_None) |
2284 | | { |
2285 | | eErr = |
2286 | | ReadPixels(nReqXOff, nReqYSize / 2, nReqXSize, |
2287 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2288 | | nLineSpace, nBandSpace, |
2289 | | pabyData + nLineSpace * (nReqYSize / 2)); |
2290 | | } |
2291 | | } |
2292 | | return eErr; |
2293 | | } |
2294 | | #endif |
2295 | | |
2296 | | CPLError(CE_Failure, CPLE_AppDefined, |
2297 | | "FPDFBitmap_Create(%d,%d) failed", nReqXSize, nReqYSize); |
2298 | | |
2299 | | return CE_Failure; |
2300 | | } |
2301 | | // alpha is 0% which is transported to FF if not alpha |
2302 | | // Default background color is white |
2303 | | FPDF_DWORD color = 0x00FFFFFF; // A,R,G,B |
2304 | | FPDFBitmap_FillRect(bitmap, 0, 0, nReqXSize, nReqYSize, color); |
2305 | | |
2306 | | #ifdef DEBUG |
2307 | | // start_x, start_y, size_x, size_y, rotate, flags |
2308 | | CPLDebug("PDF", |
2309 | | "PDFDataset::ReadPixels(%d, %d, %d, %d, scaleFactor=%d)", |
2310 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2311 | | 1 << cpl::down_cast<PDFRasterBand *>(GetRasterBand(1)) |
2312 | | ->nResolutionLevel); |
2313 | | |
2314 | | CPLDebug("PDF", "FPDF_RenderPageBitmap(%d, %d, %d, %d)", -nReqXOff, |
2315 | | -nReqYOff, nRasterXSize, nRasterYSize); |
2316 | | #endif |
2317 | | |
2318 | | // Part of PDF is render with -x, -y, page_width, page_height |
2319 | | // (not requested size!) |
2320 | | PDFiumRenderPageBitmap( |
2321 | | bitmap, FPDFPageFromIPDFPage(m_poPagePdfium->page), -nReqXOff, |
2322 | | -nReqYOff, nRasterXSize, nRasterYSize, pszRenderingOptions); |
2323 | | |
2324 | | int stride = FPDFBitmap_GetStride(bitmap); |
2325 | | const GByte *buffer = |
2326 | | reinterpret_cast<const GByte *>(FPDFBitmap_GetBuffer(bitmap)); |
2327 | | |
2328 | | // Release mutex - following code is thread-safe |
2329 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2330 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2331 | | |
2332 | | // Source data is B, G, R, unused. |
2333 | | // Destination data is R, G, B (,A if is alpha) |
2334 | | GByte *pabyDataR = pabyData; |
2335 | | GByte *pabyDataG = pabyData + 1 * nBandSpace; |
2336 | | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2337 | | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2338 | | // Copied from Poppler |
2339 | | int i, j; |
2340 | | for (j = 0; j < nReqYSize; j++) |
2341 | | { |
2342 | | for (i = 0; i < nReqXSize; i++) |
2343 | | { |
2344 | | pabyDataR[i * nPixelSpace] = buffer[(i * 4) + 2]; |
2345 | | pabyDataG[i * nPixelSpace] = buffer[(i * 4) + 1]; |
2346 | | pabyDataB[i * nPixelSpace] = buffer[(i * 4) + 0]; |
2347 | | if (nBands == 4) |
2348 | | { |
2349 | | pabyDataA[i * nPixelSpace] = buffer[(i * 4) + 3]; |
2350 | | } |
2351 | | } |
2352 | | pabyDataR += nLineSpace; |
2353 | | pabyDataG += nLineSpace; |
2354 | | pabyDataB += nLineSpace; |
2355 | | pabyDataA += nLineSpace; |
2356 | | buffer += stride; |
2357 | | } |
2358 | | FPDFBitmap_Destroy(bitmap); |
2359 | | } |
2360 | | #endif // ~ HAVE_PDFIUM |
2361 | | |
2362 | 7.86k | return eErr; |
2363 | 7.86k | } |
2364 | | |
2365 | | /************************************************************************/ |
2366 | | /* ==================================================================== */ |
2367 | | /* PDFImageRasterBand */ |
2368 | | /* ==================================================================== */ |
2369 | | /************************************************************************/ |
2370 | | |
2371 | | class PDFImageRasterBand final : public PDFRasterBand |
2372 | | { |
2373 | | friend class PDFDataset; |
2374 | | |
2375 | | public: |
2376 | | PDFImageRasterBand(PDFDataset *, int); |
2377 | | |
2378 | | CPLErr IReadBlock(int, int, void *) override; |
2379 | | }; |
2380 | | |
2381 | | /************************************************************************/ |
2382 | | /* PDFImageRasterBand() */ |
2383 | | /************************************************************************/ |
2384 | | |
2385 | | PDFImageRasterBand::PDFImageRasterBand(PDFDataset *poDSIn, int nBandIn) |
2386 | 0 | : PDFRasterBand(poDSIn, nBandIn, 0) |
2387 | 0 | { |
2388 | 0 | } |
2389 | | |
2390 | | /************************************************************************/ |
2391 | | /* IReadBlock() */ |
2392 | | /************************************************************************/ |
2393 | | |
2394 | | CPLErr PDFImageRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
2395 | | void *pImage) |
2396 | 0 | { |
2397 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2398 | 0 | CPLAssert(poGDS->m_poImageObj != nullptr); |
2399 | |
|
2400 | 0 | if (!poGDS->m_bTried) |
2401 | 0 | { |
2402 | 0 | int nBands = (poGDS->nBands == 1) ? 1 : 3; |
2403 | 0 | poGDS->m_bTried = true; |
2404 | 0 | if (nBands == 3) |
2405 | 0 | { |
2406 | 0 | poGDS->m_pabyCachedData = static_cast<GByte *>( |
2407 | 0 | VSIMalloc3(nBands, nRasterXSize, nRasterYSize)); |
2408 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2409 | 0 | return CE_Failure; |
2410 | 0 | } |
2411 | | |
2412 | 0 | GDALPDFStream *poStream = poGDS->m_poImageObj->GetStream(); |
2413 | 0 | GByte *pabyStream = nullptr; |
2414 | |
|
2415 | 0 | if (poStream == nullptr || |
2416 | 0 | static_cast<size_t>(poStream->GetLength()) != |
2417 | 0 | static_cast<size_t>(nBands) * nRasterXSize * nRasterYSize || |
2418 | 0 | (pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes())) == |
2419 | 0 | nullptr) |
2420 | 0 | { |
2421 | 0 | VSIFree(poGDS->m_pabyCachedData); |
2422 | 0 | poGDS->m_pabyCachedData = nullptr; |
2423 | 0 | return CE_Failure; |
2424 | 0 | } |
2425 | | |
2426 | 0 | if (nBands == 3) |
2427 | 0 | { |
2428 | | /* pixel interleaved to band interleaved */ |
2429 | 0 | for (size_t i = 0; |
2430 | 0 | i < static_cast<size_t>(nRasterXSize) * nRasterYSize; i++) |
2431 | 0 | { |
2432 | 0 | poGDS->m_pabyCachedData[0 * static_cast<size_t>(nRasterXSize) * |
2433 | 0 | nRasterYSize + |
2434 | 0 | i] = pabyStream[3 * i + 0]; |
2435 | 0 | poGDS->m_pabyCachedData[1 * static_cast<size_t>(nRasterXSize) * |
2436 | 0 | nRasterYSize + |
2437 | 0 | i] = pabyStream[3 * i + 1]; |
2438 | 0 | poGDS->m_pabyCachedData[2 * static_cast<size_t>(nRasterXSize) * |
2439 | 0 | nRasterYSize + |
2440 | 0 | i] = pabyStream[3 * i + 2]; |
2441 | 0 | } |
2442 | 0 | VSIFree(pabyStream); |
2443 | 0 | } |
2444 | 0 | else |
2445 | 0 | poGDS->m_pabyCachedData = pabyStream; |
2446 | 0 | } |
2447 | | |
2448 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2449 | 0 | return CE_Failure; |
2450 | | |
2451 | 0 | if (nBand == 4) |
2452 | 0 | memset(pImage, 255, nRasterXSize); |
2453 | 0 | else |
2454 | 0 | memcpy(pImage, |
2455 | 0 | poGDS->m_pabyCachedData + |
2456 | 0 | static_cast<size_t>(nBand - 1) * nRasterXSize * |
2457 | 0 | nRasterYSize + |
2458 | 0 | static_cast<size_t>(nBlockYOff) * nRasterXSize, |
2459 | 0 | nRasterXSize); |
2460 | |
|
2461 | 0 | return CE_None; |
2462 | 0 | } |
2463 | | |
2464 | | /************************************************************************/ |
2465 | | /* PDFDataset() */ |
2466 | | /************************************************************************/ |
2467 | | |
2468 | | PDFDataset::PDFDataset(PDFDataset *poParentDSIn, int nXSize, int nYSize) |
2469 | 38.0k | : m_bIsOvrDS(poParentDSIn != nullptr), |
2470 | | #ifdef HAVE_PDFIUM |
2471 | | m_poDocPdfium(poParentDSIn ? poParentDSIn->m_poDocPdfium : nullptr), |
2472 | | m_poPagePdfium(poParentDSIn ? poParentDSIn->m_poPagePdfium : nullptr), |
2473 | | #endif |
2474 | 38.0k | m_bSetStyle(CPLTestBool(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES"))) |
2475 | 38.0k | { |
2476 | 38.0k | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
2477 | 38.0k | nRasterXSize = nXSize; |
2478 | 38.0k | nRasterYSize = nYSize; |
2479 | 38.0k | if (poParentDSIn) |
2480 | 0 | m_bUseLib = poParentDSIn->m_bUseLib; |
2481 | | |
2482 | 38.0k | InitMapOperators(); |
2483 | 38.0k | } |
2484 | | |
2485 | | /************************************************************************/ |
2486 | | /* IBuildOverviews() */ |
2487 | | /************************************************************************/ |
2488 | | |
2489 | | CPLErr PDFDataset::IBuildOverviews(const char *pszResampling, int nOverviews, |
2490 | | const int *panOverviewList, int nListBands, |
2491 | | const int *panBandList, |
2492 | | GDALProgressFunc pfnProgress, |
2493 | | void *pProgressData, |
2494 | | CSLConstList papszOptions) |
2495 | | |
2496 | 0 | { |
2497 | | /* -------------------------------------------------------------------- */ |
2498 | | /* In order for building external overviews to work properly we */ |
2499 | | /* discard any concept of internal overviews when the user */ |
2500 | | /* first requests to build external overviews. */ |
2501 | | /* -------------------------------------------------------------------- */ |
2502 | 0 | if (!m_apoOvrDS.empty()) |
2503 | 0 | { |
2504 | 0 | m_apoOvrDSBackup = std::move(m_apoOvrDS); |
2505 | 0 | m_apoOvrDS.clear(); |
2506 | 0 | } |
2507 | | |
2508 | | // Prevents InitOverviews() to run |
2509 | 0 | m_apoOvrDSBackup.emplace_back(nullptr); |
2510 | 0 | const CPLErr eErr = GDALPamDataset::IBuildOverviews( |
2511 | 0 | pszResampling, nOverviews, panOverviewList, nListBands, panBandList, |
2512 | 0 | pfnProgress, pProgressData, papszOptions); |
2513 | 0 | m_apoOvrDSBackup.pop_back(); |
2514 | 0 | return eErr; |
2515 | 0 | } |
2516 | | |
2517 | | /************************************************************************/ |
2518 | | /* PDFFreeDoc() */ |
2519 | | /************************************************************************/ |
2520 | | |
2521 | | #ifdef HAVE_POPPLER |
2522 | | static void PDFFreeDoc(PDFDoc *poDoc) |
2523 | 48.9k | { |
2524 | 48.9k | if (poDoc) |
2525 | 48.9k | { |
2526 | 48.9k | #if POPPLER_MAJOR_VERSION < 26 || \ |
2527 | 48.9k | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION < 2) |
2528 | | /* hack to avoid potential cross heap issues on Win32 */ |
2529 | | /* str is the VSIPDFFileStream object passed in the constructor of |
2530 | | * PDFDoc */ |
2531 | | // NOTE: This is potentially very dangerous. See comment in |
2532 | | // VSIPDFFileStream::FillBuffer() */ |
2533 | 48.9k | delete poDoc->str; |
2534 | 48.9k | poDoc->str = nullptr; |
2535 | 48.9k | #endif |
2536 | | |
2537 | 48.9k | delete poDoc; |
2538 | 48.9k | } |
2539 | 48.9k | } |
2540 | | #endif |
2541 | | |
2542 | | /************************************************************************/ |
2543 | | /* GetCatalog() */ |
2544 | | /************************************************************************/ |
2545 | | |
2546 | | GDALPDFObject *PDFDataset::GetCatalog() |
2547 | 93.1k | { |
2548 | 93.1k | if (m_poCatalogObject) |
2549 | 55.1k | return m_poCatalogObject; |
2550 | | |
2551 | 37.9k | #ifdef HAVE_POPPLER |
2552 | 37.9k | if (m_bUseLib.test(PDFLIB_POPPLER) && m_poDocPoppler) |
2553 | 37.9k | { |
2554 | 37.9k | m_poCatalogObjectPoppler = |
2555 | 37.9k | std::make_unique<Object>(m_poDocPoppler->getXRef()->getCatalog()); |
2556 | 37.9k | if (!m_poCatalogObjectPoppler->isNull()) |
2557 | 37.9k | m_poCatalogObject = |
2558 | 37.9k | new GDALPDFObjectPoppler(m_poCatalogObjectPoppler.get(), FALSE); |
2559 | 37.9k | } |
2560 | 37.9k | #endif |
2561 | | |
2562 | | #ifdef HAVE_PODOFO |
2563 | | if (m_bUseLib.test(PDFLIB_PODOFO) && m_poDocPodofo) |
2564 | | { |
2565 | | int nCatalogNum = 0; |
2566 | | int nCatalogGen = 0; |
2567 | | VSILFILE *fp = VSIFOpenL(m_osFilename.c_str(), "rb"); |
2568 | | if (fp != nullptr) |
2569 | | { |
2570 | | GDALPDFUpdateWriter oWriter(fp); |
2571 | | if (oWriter.ParseTrailerAndXRef()) |
2572 | | { |
2573 | | nCatalogNum = oWriter.GetCatalogNum().toInt(); |
2574 | | nCatalogGen = oWriter.GetCatalogGen(); |
2575 | | } |
2576 | | oWriter.Close(); |
2577 | | } |
2578 | | |
2579 | | PoDoFo::PdfObject *poCatalogPodofo = |
2580 | | m_poDocPodofo->GetObjects().GetObject( |
2581 | | PoDoFo::PdfReference(nCatalogNum, nCatalogGen)); |
2582 | | if (poCatalogPodofo) |
2583 | | m_poCatalogObject = new GDALPDFObjectPodofo( |
2584 | | poCatalogPodofo, m_poDocPodofo->GetObjects()); |
2585 | | } |
2586 | | #endif |
2587 | | |
2588 | | #ifdef HAVE_PDFIUM |
2589 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_poDocPdfium) |
2590 | | { |
2591 | | RetainPtr<CPDF_Dictionary> catalog = |
2592 | | m_poDocPdfium->doc->GetMutableRoot(); |
2593 | | if (catalog) |
2594 | | m_poCatalogObject = GDALPDFObjectPdfium::Build(catalog); |
2595 | | } |
2596 | | #endif // ~ HAVE_PDFIUM |
2597 | | |
2598 | 37.9k | return m_poCatalogObject; |
2599 | 93.1k | } |
2600 | | |
2601 | | /************************************************************************/ |
2602 | | /* ~PDFDataset() */ |
2603 | | /************************************************************************/ |
2604 | | |
2605 | | PDFDataset::~PDFDataset() |
2606 | 38.0k | { |
2607 | | #ifdef HAVE_PDFIUM |
2608 | | m_apoOvrDS.clear(); |
2609 | | m_apoOvrDSBackup.clear(); |
2610 | | #endif |
2611 | | |
2612 | 38.0k | CPLFree(m_pabyCachedData); |
2613 | 38.0k | m_pabyCachedData = nullptr; |
2614 | | |
2615 | 38.0k | delete m_poNeatLine; |
2616 | 38.0k | m_poNeatLine = nullptr; |
2617 | | |
2618 | | /* Collect data necessary to update */ |
2619 | 38.0k | int nNum = 0; |
2620 | 38.0k | int nGen = 0; |
2621 | 38.0k | GDALPDFDictionaryRW *poPageDictCopy = nullptr; |
2622 | 38.0k | GDALPDFDictionaryRW *poCatalogDictCopy = nullptr; |
2623 | 38.0k | if (m_poPageObj) |
2624 | 38.0k | { |
2625 | 38.0k | nNum = m_poPageObj->GetRefNum().toInt(); |
2626 | 38.0k | nGen = m_poPageObj->GetRefGen(); |
2627 | 38.0k | if (eAccess == GA_Update && |
2628 | 0 | (m_bProjDirty || m_bNeatLineDirty || m_bInfoDirty || m_bXMPDirty) && |
2629 | 0 | nNum != 0 && m_poPageObj != nullptr && |
2630 | 0 | m_poPageObj->GetType() == PDFObjectType_Dictionary) |
2631 | 0 | { |
2632 | 0 | poPageDictCopy = m_poPageObj->GetDictionary()->Clone(); |
2633 | |
|
2634 | 0 | if (m_bXMPDirty) |
2635 | 0 | { |
2636 | | /* We need the catalog because it points to the XMP Metadata |
2637 | | * object */ |
2638 | 0 | GetCatalog(); |
2639 | 0 | if (m_poCatalogObject && |
2640 | 0 | m_poCatalogObject->GetType() == PDFObjectType_Dictionary) |
2641 | 0 | poCatalogDictCopy = |
2642 | 0 | m_poCatalogObject->GetDictionary()->Clone(); |
2643 | 0 | } |
2644 | 0 | } |
2645 | 38.0k | } |
2646 | | |
2647 | | /* Close document (and file descriptor) to be able to open it */ |
2648 | | /* in read-write mode afterwards */ |
2649 | 38.0k | delete m_poPageObj; |
2650 | 38.0k | m_poPageObj = nullptr; |
2651 | 38.0k | delete m_poCatalogObject; |
2652 | 38.0k | m_poCatalogObject = nullptr; |
2653 | 38.0k | #ifdef HAVE_POPPLER |
2654 | 38.0k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
2655 | 38.0k | { |
2656 | 38.0k | m_poCatalogObjectPoppler.reset(); |
2657 | 38.0k | PDFFreeDoc(m_poDocPoppler); |
2658 | 38.0k | } |
2659 | 38.0k | m_poDocPoppler = nullptr; |
2660 | 38.0k | #endif |
2661 | | #ifdef HAVE_PODOFO |
2662 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2663 | | { |
2664 | | delete m_poDocPodofo; |
2665 | | } |
2666 | | m_poDocPodofo = nullptr; |
2667 | | #endif |
2668 | | #ifdef HAVE_PDFIUM |
2669 | | if (!m_bIsOvrDS) |
2670 | | { |
2671 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2672 | | { |
2673 | | UnloadPdfiumDocumentPage(&m_poDocPdfium, &m_poPagePdfium); |
2674 | | } |
2675 | | } |
2676 | | m_poDocPdfium = nullptr; |
2677 | | m_poPagePdfium = nullptr; |
2678 | | #endif // ~ HAVE_PDFIUM |
2679 | | |
2680 | 38.0k | m_bHasLoadedLayers = true; |
2681 | 38.0k | m_apoLayers.clear(); |
2682 | | |
2683 | | /* Now do the update */ |
2684 | 38.0k | if (poPageDictCopy) |
2685 | 0 | { |
2686 | 0 | VSILFILE *fp = VSIFOpenL(m_osFilename, "rb+"); |
2687 | 0 | if (fp != nullptr) |
2688 | 0 | { |
2689 | 0 | GDALPDFUpdateWriter oWriter(fp); |
2690 | 0 | if (oWriter.ParseTrailerAndXRef()) |
2691 | 0 | { |
2692 | 0 | if ((m_bProjDirty || m_bNeatLineDirty) && |
2693 | 0 | poPageDictCopy != nullptr) |
2694 | 0 | oWriter.UpdateProj(this, m_dfDPI, poPageDictCopy, |
2695 | 0 | GDALPDFObjectNum(nNum), nGen); |
2696 | |
|
2697 | 0 | if (m_bInfoDirty) |
2698 | 0 | oWriter.UpdateInfo(this); |
2699 | |
|
2700 | 0 | if (m_bXMPDirty && poCatalogDictCopy != nullptr) |
2701 | 0 | oWriter.UpdateXMP(this, poCatalogDictCopy); |
2702 | 0 | } |
2703 | 0 | oWriter.Close(); |
2704 | 0 | } |
2705 | 0 | else |
2706 | 0 | { |
2707 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2708 | 0 | "Cannot open %s in update mode", m_osFilename.c_str()); |
2709 | 0 | } |
2710 | 0 | } |
2711 | 38.0k | delete poPageDictCopy; |
2712 | 38.0k | poPageDictCopy = nullptr; |
2713 | 38.0k | delete poCatalogDictCopy; |
2714 | 38.0k | poCatalogDictCopy = nullptr; |
2715 | | |
2716 | 38.0k | if (m_nGCPCount > 0) |
2717 | 28 | { |
2718 | 28 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
2719 | 28 | CPLFree(m_pasGCPList); |
2720 | 28 | m_pasGCPList = nullptr; |
2721 | 28 | m_nGCPCount = 0; |
2722 | 28 | } |
2723 | | |
2724 | 38.0k | CleanupIntermediateResources(); |
2725 | | |
2726 | | // Do that only after having destroyed Poppler objects |
2727 | 38.0k | m_fp.reset(); |
2728 | 38.0k | } |
2729 | | |
2730 | | /************************************************************************/ |
2731 | | /* IRasterIO() */ |
2732 | | /************************************************************************/ |
2733 | | |
2734 | | CPLErr PDFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2735 | | int nXSize, int nYSize, void *pData, int nBufXSize, |
2736 | | int nBufYSize, GDALDataType eBufType, |
2737 | | int nBandCount, BANDMAP_TYPE panBandMap, |
2738 | | GSpacing nPixelSpace, GSpacing nLineSpace, |
2739 | | GSpacing nBandSpace, |
2740 | | GDALRasterIOExtraArg *psExtraArg) |
2741 | 0 | { |
2742 | | // Try to pass the request to the most appropriate overview dataset. |
2743 | 0 | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2744 | 0 | { |
2745 | 0 | int bTried = FALSE; |
2746 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2747 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2748 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, |
2749 | 0 | nBandSpace, psExtraArg, &bTried); |
2750 | 0 | if (bTried) |
2751 | 0 | return eErr; |
2752 | 0 | } |
2753 | | |
2754 | 0 | int nBandBlockXSize, nBandBlockYSize; |
2755 | 0 | int bReadPixels = FALSE; |
2756 | 0 | GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize); |
2757 | 0 | if (m_aiTiles.empty() && eRWFlag == GF_Read && nXSize == nBufXSize && |
2758 | 0 | nYSize == nBufYSize && |
2759 | 0 | (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) && |
2760 | 0 | eBufType == GDT_UInt8 && nBandCount == nBands && |
2761 | 0 | IsAllBands(nBandCount, panBandMap)) |
2762 | 0 | { |
2763 | 0 | bReadPixels = TRUE; |
2764 | | #ifdef HAVE_PODOFO |
2765 | | if (m_bUseLib.test(PDFLIB_PODOFO) && nBands == 4) |
2766 | | { |
2767 | | bReadPixels = FALSE; |
2768 | | } |
2769 | | #endif |
2770 | 0 | } |
2771 | |
|
2772 | 0 | if (bReadPixels) |
2773 | 0 | return ReadPixels(nXOff, nYOff, nXSize, nYSize, nPixelSpace, nLineSpace, |
2774 | 0 | nBandSpace, static_cast<GByte *>(pData)); |
2775 | | |
2776 | 0 | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2777 | 0 | { |
2778 | 0 | m_bCacheBlocksForOtherBands = true; |
2779 | 0 | } |
2780 | 0 | CPLErr eErr = GDALPamDataset::IRasterIO( |
2781 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2782 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, |
2783 | 0 | psExtraArg); |
2784 | 0 | m_bCacheBlocksForOtherBands = false; |
2785 | 0 | return eErr; |
2786 | 0 | } |
2787 | | |
2788 | | /************************************************************************/ |
2789 | | /* IRasterIO() */ |
2790 | | /************************************************************************/ |
2791 | | |
2792 | | CPLErr PDFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2793 | | int nXSize, int nYSize, void *pData, |
2794 | | int nBufXSize, int nBufYSize, |
2795 | | GDALDataType eBufType, GSpacing nPixelSpace, |
2796 | | GSpacing nLineSpace, |
2797 | | GDALRasterIOExtraArg *psExtraArg) |
2798 | 10.1M | { |
2799 | 10.1M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2800 | | |
2801 | | // Try to pass the request to the most appropriate overview dataset. |
2802 | 10.1M | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2803 | 0 | { |
2804 | 0 | int bTried = FALSE; |
2805 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2806 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2807 | 0 | eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried); |
2808 | 0 | if (bTried) |
2809 | 0 | return eErr; |
2810 | 0 | } |
2811 | | |
2812 | 10.1M | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2813 | 10.1M | { |
2814 | 10.1M | poGDS->m_bCacheBlocksForOtherBands = true; |
2815 | 10.1M | } |
2816 | 10.1M | CPLErr eErr = GDALPamRasterBand::IRasterIO( |
2817 | 10.1M | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2818 | 10.1M | eBufType, nPixelSpace, nLineSpace, psExtraArg); |
2819 | 10.1M | poGDS->m_bCacheBlocksForOtherBands = false; |
2820 | 10.1M | return eErr; |
2821 | 10.1M | } |
2822 | | |
2823 | | /************************************************************************/ |
2824 | | /* PDFDatasetErrorFunction() */ |
2825 | | /************************************************************************/ |
2826 | | |
2827 | | #ifdef HAVE_POPPLER |
2828 | | |
2829 | | static void PDFDatasetErrorFunctionCommon(const CPLString &osError) |
2830 | 16.0M | { |
2831 | 16.0M | if (strcmp(osError.c_str(), "Incorrect password") == 0) |
2832 | 62 | return; |
2833 | | /* Reported on newer USGS GeoPDF */ |
2834 | 16.0M | if (strcmp(osError.c_str(), |
2835 | 16.0M | "Couldn't find group for reference to set OFF") == 0) |
2836 | 722 | { |
2837 | 722 | CPLDebug("PDF", "%s", osError.c_str()); |
2838 | 722 | return; |
2839 | 722 | } |
2840 | | |
2841 | 16.0M | CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str()); |
2842 | 16.0M | } |
2843 | | |
2844 | | static int g_nPopplerErrors = 0; |
2845 | | constexpr int MAX_POPPLER_ERRORS = 1000; |
2846 | | |
2847 | | static void PDFDatasetErrorFunction(ErrorCategory /* eErrCategory */, |
2848 | | Goffset nPos, const char *pszMsg) |
2849 | 16.0M | { |
2850 | 16.0M | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
2851 | 10.1k | { |
2852 | | // If there are too many errors, then unregister ourselves and turn |
2853 | | // quiet error mode, as the error() function in poppler can spend |
2854 | | // significant time formatting an error message we won't emit... |
2855 | 10.1k | setErrorCallback(nullptr); |
2856 | 10.1k | globalParams->setErrQuiet(true); |
2857 | 10.1k | return; |
2858 | 10.1k | } |
2859 | | |
2860 | 16.0M | g_nPopplerErrors++; |
2861 | 16.0M | CPLString osError; |
2862 | | |
2863 | 16.0M | if (nPos >= 0) |
2864 | 15.3M | osError.Printf("Pos = " CPL_FRMT_GUIB ", ", |
2865 | 15.3M | static_cast<GUIntBig>(nPos)); |
2866 | 16.0M | osError += pszMsg; |
2867 | 16.0M | PDFDatasetErrorFunctionCommon(osError); |
2868 | 16.0M | } |
2869 | | #endif |
2870 | | |
2871 | | /************************************************************************/ |
2872 | | /* GDALPDFParseStreamContentOnlyDrawForm() */ |
2873 | | /************************************************************************/ |
2874 | | |
2875 | | static CPLString GDALPDFParseStreamContentOnlyDrawForm(const char *pszContent) |
2876 | 9.72k | { |
2877 | 9.72k | CPLString osToken; |
2878 | 9.72k | char ch; |
2879 | 9.72k | int nCurIdx = 0; |
2880 | 9.72k | CPLString osCurrentForm; |
2881 | | |
2882 | | // CPLDebug("PDF", "content = %s", pszContent); |
2883 | | |
2884 | 7.76M | while ((ch = *pszContent) != '\0') |
2885 | 7.75M | { |
2886 | 7.75M | if (ch == '%') |
2887 | 3.62k | { |
2888 | | /* Skip comments until end-of-line */ |
2889 | 672k | while ((ch = *pszContent) != '\0') |
2890 | 672k | { |
2891 | 672k | if (ch == '\r' || ch == '\n') |
2892 | 3.61k | break; |
2893 | 669k | pszContent++; |
2894 | 669k | } |
2895 | 3.62k | if (ch == 0) |
2896 | 12 | break; |
2897 | 3.62k | } |
2898 | 7.75M | else if (ch == ' ' || ch == '\r' || ch == '\n') |
2899 | 18.7k | { |
2900 | 18.7k | if (!osToken.empty()) |
2901 | 13.7k | { |
2902 | 13.7k | if (nCurIdx == 0 && osToken[0] == '/') |
2903 | 4.33k | { |
2904 | 4.33k | osCurrentForm = osToken.substr(1); |
2905 | 4.33k | nCurIdx++; |
2906 | 4.33k | } |
2907 | 9.37k | else if (nCurIdx == 1 && osToken == "Do") |
2908 | 89 | { |
2909 | 89 | nCurIdx++; |
2910 | 89 | } |
2911 | 9.29k | else |
2912 | 9.29k | { |
2913 | 9.29k | return ""; |
2914 | 9.29k | } |
2915 | 13.7k | } |
2916 | 9.45k | osToken = ""; |
2917 | 9.45k | } |
2918 | 7.73M | else |
2919 | 7.73M | osToken += ch; |
2920 | 7.75M | pszContent++; |
2921 | 7.75M | } |
2922 | | |
2923 | 435 | return osCurrentForm; |
2924 | 9.72k | } |
2925 | | |
2926 | | /************************************************************************/ |
2927 | | /* GDALPDFParseStreamContent() */ |
2928 | | /************************************************************************/ |
2929 | | |
2930 | | typedef enum |
2931 | | { |
2932 | | STATE_INIT, |
2933 | | STATE_AFTER_q, |
2934 | | STATE_AFTER_cm, |
2935 | | STATE_AFTER_Do |
2936 | | } PDFStreamState; |
2937 | | |
2938 | | /* This parser is reduced to understanding sequences that draw rasters, such as |
2939 | | : |
2940 | | q |
2941 | | scaleX 0 0 scaleY translateX translateY cm |
2942 | | /ImXXX Do |
2943 | | Q |
2944 | | |
2945 | | All other sequences will abort the parsing. |
2946 | | |
2947 | | Returns TRUE if the stream only contains images. |
2948 | | */ |
2949 | | |
2950 | | static int GDALPDFParseStreamContent(const char *pszContent, |
2951 | | GDALPDFDictionary *poXObjectDict, |
2952 | | double *pdfDPI, int *pbDPISet, |
2953 | | int *pnBands, |
2954 | | std::vector<GDALPDFTileDesc> &asTiles, |
2955 | | int bAcceptRotationTerms) |
2956 | 9.66k | { |
2957 | 9.66k | CPLString osToken; |
2958 | 9.66k | char ch; |
2959 | 9.66k | PDFStreamState nState = STATE_INIT; |
2960 | 9.66k | int nCurIdx = 0; |
2961 | 9.66k | double adfVals[6]; |
2962 | 9.66k | CPLString osCurrentImage; |
2963 | | |
2964 | 9.66k | double dfDPI = DEFAULT_DPI; |
2965 | 9.66k | *pbDPISet = FALSE; |
2966 | | |
2967 | 7.82M | while ((ch = *pszContent) != '\0') |
2968 | 7.82M | { |
2969 | 7.82M | if (ch == '%') |
2970 | 3.63k | { |
2971 | | /* Skip comments until end-of-line */ |
2972 | 672k | while ((ch = *pszContent) != '\0') |
2973 | 672k | { |
2974 | 672k | if (ch == '\r' || ch == '\n') |
2975 | 3.61k | break; |
2976 | 669k | pszContent++; |
2977 | 669k | } |
2978 | 3.63k | if (ch == 0) |
2979 | 14 | break; |
2980 | 3.63k | } |
2981 | 7.81M | else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') |
2982 | 46.2k | { |
2983 | 46.2k | if (!osToken.empty()) |
2984 | 35.4k | { |
2985 | 35.4k | if (nState == STATE_INIT) |
2986 | 9.98k | { |
2987 | 9.98k | if (osToken == "q") |
2988 | 3.14k | { |
2989 | 3.14k | nState = STATE_AFTER_q; |
2990 | 3.14k | nCurIdx = 0; |
2991 | 3.14k | } |
2992 | 6.83k | else if (osToken != "Q") |
2993 | 6.65k | return FALSE; |
2994 | 9.98k | } |
2995 | 25.5k | else if (nState == STATE_AFTER_q) |
2996 | 22.4k | { |
2997 | 22.4k | if (osToken == "q") |
2998 | 1.13k | { |
2999 | | // ignore |
3000 | 1.13k | } |
3001 | 21.2k | else if (nCurIdx < 6) |
3002 | 18.3k | { |
3003 | 18.3k | adfVals[nCurIdx++] = CPLAtof(osToken); |
3004 | 18.3k | } |
3005 | 2.92k | else if (nCurIdx == 6 && osToken == "cm") |
3006 | 1.39k | { |
3007 | 1.39k | nState = STATE_AFTER_cm; |
3008 | 1.39k | nCurIdx = 0; |
3009 | 1.39k | } |
3010 | 1.53k | else |
3011 | 1.53k | return FALSE; |
3012 | 22.4k | } |
3013 | 3.09k | else if (nState == STATE_AFTER_cm) |
3014 | 2.38k | { |
3015 | 2.38k | if (nCurIdx == 0 && osToken[0] == '/') |
3016 | 1.00k | { |
3017 | 1.00k | osCurrentImage = osToken.substr(1); |
3018 | 1.00k | } |
3019 | 1.37k | else if (osToken == "Do") |
3020 | 768 | { |
3021 | 768 | nState = STATE_AFTER_Do; |
3022 | 768 | } |
3023 | 611 | else |
3024 | 611 | return FALSE; |
3025 | 2.38k | } |
3026 | 712 | else if (nState == STATE_AFTER_Do) |
3027 | 712 | { |
3028 | 712 | if (osToken == "Q") |
3029 | 691 | { |
3030 | 691 | GDALPDFObject *poImage = |
3031 | 691 | poXObjectDict->Get(osCurrentImage); |
3032 | 691 | if (poImage != nullptr && |
3033 | 628 | poImage->GetType() == PDFObjectType_Dictionary) |
3034 | 618 | { |
3035 | 618 | GDALPDFTileDesc sTile; |
3036 | 618 | GDALPDFDictionary *poImageDict = |
3037 | 618 | poImage->GetDictionary(); |
3038 | 618 | GDALPDFObject *poWidth = poImageDict->Get("Width"); |
3039 | 618 | GDALPDFObject *poHeight = |
3040 | 618 | poImageDict->Get("Height"); |
3041 | 618 | GDALPDFObject *poColorSpace = |
3042 | 618 | poImageDict->Get("ColorSpace"); |
3043 | 618 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
3044 | 618 | if (poColorSpace && |
3045 | 575 | poColorSpace->GetType() == PDFObjectType_Name) |
3046 | 338 | { |
3047 | 338 | if (poColorSpace->GetName() == "DeviceRGB") |
3048 | 207 | { |
3049 | 207 | sTile.nBands = 3; |
3050 | 207 | if (*pnBands < 3) |
3051 | 190 | *pnBands = 3; |
3052 | 207 | } |
3053 | 131 | else if (poColorSpace->GetName() == |
3054 | 131 | "DeviceGray") |
3055 | 118 | { |
3056 | 118 | sTile.nBands = 1; |
3057 | 118 | if (*pnBands < 1) |
3058 | 98 | *pnBands = 1; |
3059 | 118 | } |
3060 | 13 | else |
3061 | 13 | sTile.nBands = 0; |
3062 | 338 | } |
3063 | 618 | if (poSMask != nullptr) |
3064 | 8 | *pnBands = 4; |
3065 | | |
3066 | 618 | if (poWidth && poHeight && |
3067 | 580 | ((bAcceptRotationTerms && |
3068 | 0 | adfVals[1] == -adfVals[2]) || |
3069 | 580 | (!bAcceptRotationTerms && adfVals[1] == 0.0 && |
3070 | 580 | adfVals[2] == 0.0))) |
3071 | 577 | { |
3072 | 577 | double dfWidth = Get(poWidth); |
3073 | 577 | double dfHeight = Get(poHeight); |
3074 | 577 | double dfScaleX = adfVals[0]; |
3075 | 577 | double dfScaleY = adfVals[3]; |
3076 | 577 | if (dfWidth > 0 && dfHeight > 0 && |
3077 | 572 | dfScaleX > 0 && dfScaleY > 0 && |
3078 | 569 | dfWidth / dfScaleX * DEFAULT_DPI < |
3079 | 569 | INT_MAX && |
3080 | 569 | dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX) |
3081 | 569 | { |
3082 | 569 | double dfDPI_X = ROUND_IF_CLOSE( |
3083 | 569 | dfWidth / dfScaleX * DEFAULT_DPI, 1e-3); |
3084 | 569 | double dfDPI_Y = ROUND_IF_CLOSE( |
3085 | 569 | dfHeight / dfScaleY * DEFAULT_DPI, |
3086 | 569 | 1e-3); |
3087 | | // CPLDebug("PDF", "Image %s, width = %.16g, |
3088 | | // height = %.16g, scaleX = %.16g, scaleY = |
3089 | | // %.16g --> DPI_X = %.16g, DPI_Y = %.16g", |
3090 | | // osCurrentImage.c_str(), |
3091 | | // dfWidth, dfHeight, |
3092 | | // dfScaleX, dfScaleY, |
3093 | | // dfDPI_X, dfDPI_Y); |
3094 | 569 | if (dfDPI_X > dfDPI) |
3095 | 277 | dfDPI = dfDPI_X; |
3096 | 569 | if (dfDPI_Y > dfDPI) |
3097 | 35 | dfDPI = dfDPI_Y; |
3098 | | |
3099 | 569 | memcpy(&(sTile.adfCM), adfVals, |
3100 | 569 | 6 * sizeof(double)); |
3101 | 569 | sTile.poImage = poImage; |
3102 | 569 | sTile.dfWidth = dfWidth; |
3103 | 569 | sTile.dfHeight = dfHeight; |
3104 | 569 | asTiles.push_back(sTile); |
3105 | | |
3106 | 569 | *pbDPISet = TRUE; |
3107 | 569 | *pdfDPI = dfDPI; |
3108 | 569 | } |
3109 | 577 | } |
3110 | 618 | } |
3111 | 691 | nState = STATE_INIT; |
3112 | 691 | } |
3113 | 21 | else |
3114 | 21 | return FALSE; |
3115 | 712 | } |
3116 | 35.4k | } |
3117 | 37.4k | osToken = ""; |
3118 | 37.4k | } |
3119 | 7.77M | else |
3120 | 7.77M | osToken += ch; |
3121 | 7.81M | pszContent++; |
3122 | 7.81M | } |
3123 | | |
3124 | 848 | return TRUE; |
3125 | 9.66k | } |
3126 | | |
3127 | | /************************************************************************/ |
3128 | | /* CheckTiledRaster() */ |
3129 | | /************************************************************************/ |
3130 | | |
3131 | | int PDFDataset::CheckTiledRaster() |
3132 | 226 | { |
3133 | 226 | size_t i; |
3134 | 226 | int l_nBlockXSize = 0; |
3135 | 226 | int l_nBlockYSize = 0; |
3136 | 226 | const double dfUserUnit = m_dfDPI * USER_UNIT_IN_INCH; |
3137 | | |
3138 | | /* First pass : check that all tiles have same DPI, */ |
3139 | | /* are contained entirely in the raster size, */ |
3140 | | /* and determine the block size */ |
3141 | 303 | for (i = 0; i < m_asTiles.size(); i++) |
3142 | 228 | { |
3143 | 228 | double dfDrawWidth = m_asTiles[i].adfCM[0] * dfUserUnit; |
3144 | 228 | double dfDrawHeight = m_asTiles[i].adfCM[3] * dfUserUnit; |
3145 | 228 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3146 | 228 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3147 | 228 | int nX = static_cast<int>(dfX + 0.1); |
3148 | 228 | int nY = static_cast<int>(dfY + 0.1); |
3149 | 228 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3150 | 228 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3151 | | |
3152 | 228 | GDALPDFDictionary *poImageDict = m_asTiles[i].poImage->GetDictionary(); |
3153 | 228 | GDALPDFObject *poBitsPerComponent = |
3154 | 228 | poImageDict->Get("BitsPerComponent"); |
3155 | 228 | GDALPDFObject *poColorSpace = poImageDict->Get("ColorSpace"); |
3156 | 228 | GDALPDFObject *poFilter = poImageDict->Get("Filter"); |
3157 | | |
3158 | | /* Podofo cannot uncompress JPEG2000 streams */ |
3159 | 228 | if (m_bUseLib.test(PDFLIB_PODOFO) && poFilter != nullptr && |
3160 | 0 | poFilter->GetType() == PDFObjectType_Name && |
3161 | 0 | poFilter->GetName() == "JPXDecode") |
3162 | 0 | { |
3163 | 0 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3164 | 0 | static_cast<int>(i)); |
3165 | 0 | return FALSE; |
3166 | 0 | } |
3167 | | |
3168 | 228 | if (poBitsPerComponent == nullptr || Get(poBitsPerComponent) != 8 || |
3169 | 165 | poColorSpace == nullptr || |
3170 | 163 | poColorSpace->GetType() != PDFObjectType_Name || |
3171 | 125 | (poColorSpace->GetName() != "DeviceRGB" && |
3172 | 45 | poColorSpace->GetName() != "DeviceGray")) |
3173 | 106 | { |
3174 | 106 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3175 | 106 | static_cast<int>(i)); |
3176 | 106 | return FALSE; |
3177 | 106 | } |
3178 | | |
3179 | 122 | if (fabs(dfDrawWidth - m_asTiles[i].dfWidth) > 1e-2 || |
3180 | 102 | fabs(dfDrawHeight - m_asTiles[i].dfHeight) > 1e-2 || |
3181 | 98 | fabs(nWidth - m_asTiles[i].dfWidth) > 1e-8 || |
3182 | 98 | fabs(nHeight - m_asTiles[i].dfHeight) > 1e-8 || |
3183 | 98 | fabs(nX - dfX) > 1e-1 || fabs(nY - dfY) > 1e-1 || nX < 0 || |
3184 | 78 | nY < 0 || nX + nWidth > nRasterXSize || nY >= nRasterYSize) |
3185 | 45 | { |
3186 | 45 | CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", static_cast<int>(i), |
3187 | 45 | dfX, dfY, dfDrawWidth, dfDrawHeight, m_asTiles[i].dfWidth, |
3188 | 45 | m_asTiles[i].dfHeight); |
3189 | 45 | return FALSE; |
3190 | 45 | } |
3191 | 77 | if (l_nBlockXSize == 0 && l_nBlockYSize == 0 && nX == 0 && nY != 0) |
3192 | 0 | { |
3193 | 0 | l_nBlockXSize = nWidth; |
3194 | 0 | l_nBlockYSize = nHeight; |
3195 | 0 | } |
3196 | 77 | } |
3197 | 75 | if (l_nBlockXSize <= 0 || l_nBlockYSize <= 0 || l_nBlockXSize > 2048 || |
3198 | 0 | l_nBlockYSize > 2048) |
3199 | 75 | return FALSE; |
3200 | | |
3201 | 0 | int nXBlocks = DIV_ROUND_UP(nRasterXSize, l_nBlockXSize); |
3202 | 0 | int nYBlocks = DIV_ROUND_UP(nRasterYSize, l_nBlockYSize); |
3203 | | |
3204 | | /* Second pass to determine that all tiles are properly aligned on block |
3205 | | * size */ |
3206 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3207 | 0 | { |
3208 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3209 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3210 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3211 | 0 | int nY = static_cast<int>(dfY + 0.1); |
3212 | 0 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3213 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3214 | 0 | int bOK = TRUE; |
3215 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3216 | 0 | if ((nX % l_nBlockXSize) != 0) |
3217 | 0 | bOK = FALSE; |
3218 | 0 | if (nBlockXOff < nXBlocks - 1 && nWidth != l_nBlockXSize) |
3219 | 0 | bOK = FALSE; |
3220 | 0 | if (nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize) |
3221 | 0 | bOK = FALSE; |
3222 | |
|
3223 | 0 | if (nY > 0 && nHeight != l_nBlockYSize) |
3224 | 0 | bOK = FALSE; |
3225 | 0 | if (nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * l_nBlockYSize) |
3226 | 0 | bOK = FALSE; |
3227 | |
|
3228 | 0 | if (!bOK) |
3229 | 0 | { |
3230 | 0 | CPLDebug("PDF", "Tile %d : %d %d %d %d", static_cast<int>(i), nX, |
3231 | 0 | nY, nWidth, nHeight); |
3232 | 0 | return FALSE; |
3233 | 0 | } |
3234 | 0 | } |
3235 | | |
3236 | | /* Third pass to set the aiTiles array */ |
3237 | 0 | m_aiTiles.resize(static_cast<size_t>(nXBlocks) * nYBlocks, -1); |
3238 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3239 | 0 | { |
3240 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3241 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3242 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3243 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3244 | 0 | int nY = nRasterYSize - (static_cast<int>(dfY + 0.1) + nHeight); |
3245 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3246 | 0 | int nBlockYOff = nY / l_nBlockYSize; |
3247 | 0 | m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff] = static_cast<int>(i); |
3248 | 0 | } |
3249 | |
|
3250 | 0 | this->m_nBlockXSize = l_nBlockXSize; |
3251 | 0 | this->m_nBlockYSize = l_nBlockYSize; |
3252 | |
|
3253 | 0 | return TRUE; |
3254 | 0 | } |
3255 | | |
3256 | | /************************************************************************/ |
3257 | | /* GuessDPIAndBandCount() */ |
3258 | | /************************************************************************/ |
3259 | | |
3260 | | void PDFDataset::GuessDPIAndBandCount(GDALPDFDictionary *poPageDict, |
3261 | | double &dfDPI, int &nBandsGuessed) |
3262 | 38.0k | { |
3263 | | /* Try to get a better value from the images that are drawn */ |
3264 | | /* Very simplistic logic. Will only work for raster only PDF */ |
3265 | | |
3266 | 38.0k | GDALPDFObject *poContents = poPageDict->Get("Contents"); |
3267 | 38.0k | if (poContents != nullptr && poContents->GetType() == PDFObjectType_Array) |
3268 | 2.42k | { |
3269 | 2.42k | GDALPDFArray *poContentsArray = poContents->GetArray(); |
3270 | 2.42k | if (poContentsArray->GetLength() == 1) |
3271 | 344 | { |
3272 | 344 | poContents = poContentsArray->Get(0); |
3273 | 344 | } |
3274 | 2.42k | } |
3275 | | |
3276 | 38.0k | GDALPDFObject *poXObject = poPageDict->LookupObject("Resources.XObject"); |
3277 | 38.0k | if (poContents != nullptr && |
3278 | 30.0k | poContents->GetType() == PDFObjectType_Dictionary && |
3279 | 27.7k | poXObject != nullptr && |
3280 | 10.3k | poXObject->GetType() == PDFObjectType_Dictionary) |
3281 | 10.1k | { |
3282 | 10.1k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
3283 | 10.1k | GDALPDFDictionary *poContentDict = poXObjectDict; |
3284 | 10.1k | GDALPDFStream *poPageStream = poContents->GetStream(); |
3285 | 10.1k | if (poPageStream != nullptr) |
3286 | 9.80k | { |
3287 | 9.80k | char *pszContent = nullptr; |
3288 | 9.80k | constexpr int64_t MAX_LENGTH = 10 * 1000 * 1000; |
3289 | 9.80k | const int64_t nLength = poPageStream->GetLength(MAX_LENGTH); |
3290 | 9.80k | int bResetTiles = FALSE; |
3291 | 9.80k | double dfScaleDPI = 1.0; |
3292 | | |
3293 | 9.80k | if (nLength < MAX_LENGTH) |
3294 | 9.80k | { |
3295 | 9.80k | CPLString osForm; |
3296 | 9.80k | pszContent = poPageStream->GetBytes(); |
3297 | 9.80k | if (pszContent != nullptr) |
3298 | 9.72k | { |
3299 | | #ifdef DEBUG |
3300 | | const char *pszDumpStream = |
3301 | | CPLGetConfigOption("PDF_DUMP_STREAM", nullptr); |
3302 | | if (pszDumpStream != nullptr) |
3303 | | { |
3304 | | VSILFILE *fpDump = VSIFOpenL(pszDumpStream, "wb"); |
3305 | | if (fpDump) |
3306 | | { |
3307 | | VSIFWriteL(pszContent, 1, static_cast<int>(nLength), |
3308 | | fpDump); |
3309 | | VSIFCloseL(fpDump); |
3310 | | } |
3311 | | } |
3312 | | #endif // DEBUG |
3313 | 9.72k | osForm = GDALPDFParseStreamContentOnlyDrawForm(pszContent); |
3314 | 9.72k | if (osForm.empty()) |
3315 | 9.57k | { |
3316 | | /* Special case for USGS Topo PDF, like |
3317 | | * CA_Hollywood_20090811_OM_geo.pdf */ |
3318 | 9.57k | const char *pszOGCDo = strstr(pszContent, " /XO1 Do"); |
3319 | 9.57k | if (pszOGCDo) |
3320 | 20 | { |
3321 | 20 | const char *pszcm = strstr(pszContent, " cm "); |
3322 | 20 | if (pszcm != nullptr && pszcm < pszOGCDo) |
3323 | 0 | { |
3324 | 0 | const char *pszNextcm = strstr(pszcm + 2, "cm"); |
3325 | 0 | if (pszNextcm == nullptr || |
3326 | 0 | pszNextcm > pszOGCDo) |
3327 | 0 | { |
3328 | 0 | const char *pszIter = pszcm; |
3329 | 0 | while (pszIter > pszContent) |
3330 | 0 | { |
3331 | 0 | if ((*pszIter >= '0' && |
3332 | 0 | *pszIter <= '9') || |
3333 | 0 | *pszIter == '-' || |
3334 | 0 | *pszIter == '.' || *pszIter == ' ') |
3335 | 0 | pszIter--; |
3336 | 0 | else |
3337 | 0 | { |
3338 | 0 | pszIter++; |
3339 | 0 | break; |
3340 | 0 | } |
3341 | 0 | } |
3342 | 0 | CPLString oscm(pszIter); |
3343 | 0 | oscm.resize(pszcm - pszIter); |
3344 | 0 | char **papszTokens = |
3345 | 0 | CSLTokenizeString(oscm); |
3346 | 0 | double dfScaleX = -1.0; |
3347 | 0 | double dfScaleY = -2.0; |
3348 | 0 | if (CSLCount(papszTokens) == 6) |
3349 | 0 | { |
3350 | 0 | dfScaleX = CPLAtof(papszTokens[0]); |
3351 | 0 | dfScaleY = CPLAtof(papszTokens[3]); |
3352 | 0 | } |
3353 | 0 | CSLDestroy(papszTokens); |
3354 | 0 | if (dfScaleX == dfScaleY && dfScaleX > 0.0) |
3355 | 0 | { |
3356 | 0 | osForm = "XO1"; |
3357 | 0 | bResetTiles = TRUE; |
3358 | 0 | dfScaleDPI = 1.0 / dfScaleX; |
3359 | 0 | } |
3360 | 0 | } |
3361 | 0 | } |
3362 | 20 | else |
3363 | 20 | { |
3364 | 20 | osForm = "XO1"; |
3365 | 20 | bResetTiles = TRUE; |
3366 | 20 | } |
3367 | 20 | } |
3368 | | /* Special case for USGS Topo PDF, like |
3369 | | * CA_Sacramento_East_20120308_TM_geo.pdf */ |
3370 | 9.55k | else |
3371 | 9.55k | { |
3372 | 9.55k | CPLString osOCG = |
3373 | 9.55k | FindLayerOCG(poPageDict, "Orthoimage"); |
3374 | 9.55k | if (!osOCG.empty()) |
3375 | 0 | { |
3376 | 0 | const char *pszBDCLookup = |
3377 | 0 | CPLSPrintf("/OC /%s BDC", osOCG.c_str()); |
3378 | 0 | const char *pszBDC = |
3379 | 0 | strstr(pszContent, pszBDCLookup); |
3380 | 0 | if (pszBDC != nullptr) |
3381 | 0 | { |
3382 | 0 | const char *pszIter = |
3383 | 0 | pszBDC + strlen(pszBDCLookup); |
3384 | 0 | while (*pszIter != '\0') |
3385 | 0 | { |
3386 | 0 | if (*pszIter == 13 || *pszIter == 10 || |
3387 | 0 | *pszIter == ' ' || *pszIter == 'q') |
3388 | 0 | pszIter++; |
3389 | 0 | else |
3390 | 0 | break; |
3391 | 0 | } |
3392 | 0 | if (STARTS_WITH(pszIter, |
3393 | 0 | "1 0 0 1 0 0 cm\n")) |
3394 | 0 | pszIter += strlen("1 0 0 1 0 0 cm\n"); |
3395 | 0 | if (*pszIter == '/') |
3396 | 0 | { |
3397 | 0 | pszIter++; |
3398 | 0 | const char *pszDo = |
3399 | 0 | strstr(pszIter, " Do"); |
3400 | 0 | if (pszDo != nullptr) |
3401 | 0 | { |
3402 | 0 | osForm = pszIter; |
3403 | 0 | osForm.resize(pszDo - pszIter); |
3404 | 0 | bResetTiles = TRUE; |
3405 | 0 | } |
3406 | 0 | } |
3407 | 0 | } |
3408 | 0 | } |
3409 | 9.55k | } |
3410 | 9.57k | } |
3411 | 9.72k | } |
3412 | | |
3413 | 9.80k | if (!osForm.empty()) |
3414 | 168 | { |
3415 | 168 | CPLFree(pszContent); |
3416 | 168 | pszContent = nullptr; |
3417 | | |
3418 | 168 | GDALPDFObject *poObjForm = poXObjectDict->Get(osForm); |
3419 | 168 | if (poObjForm != nullptr && |
3420 | 112 | poObjForm->GetType() == PDFObjectType_Dictionary && |
3421 | 112 | (poPageStream = poObjForm->GetStream()) != nullptr) |
3422 | 112 | { |
3423 | 112 | GDALPDFDictionary *poObjFormDict = |
3424 | 112 | poObjForm->GetDictionary(); |
3425 | 112 | GDALPDFObject *poSubtype = |
3426 | 112 | poObjFormDict->Get("Subtype"); |
3427 | 112 | if (poSubtype != nullptr && |
3428 | 112 | poSubtype->GetType() == PDFObjectType_Name && |
3429 | 112 | poSubtype->GetName() == "Form") |
3430 | 111 | { |
3431 | 111 | if (poPageStream->GetLength(MAX_LENGTH) < |
3432 | 111 | MAX_LENGTH) |
3433 | 111 | { |
3434 | 111 | pszContent = poPageStream->GetBytes(); |
3435 | | |
3436 | 111 | GDALPDFObject *poXObject2 = |
3437 | 111 | poObjFormDict->LookupObject( |
3438 | 111 | "Resources.XObject"); |
3439 | 111 | if (poXObject2 != nullptr && |
3440 | 3 | poXObject2->GetType() == |
3441 | 3 | PDFObjectType_Dictionary) |
3442 | 3 | poContentDict = poXObject2->GetDictionary(); |
3443 | 111 | } |
3444 | 111 | } |
3445 | 112 | } |
3446 | 168 | } |
3447 | 9.80k | } |
3448 | | |
3449 | 9.80k | if (pszContent != nullptr) |
3450 | 9.66k | { |
3451 | 9.66k | int bDPISet = FALSE; |
3452 | | |
3453 | 9.66k | const char *pszContentToParse = pszContent; |
3454 | 9.66k | if (bResetTiles) |
3455 | 0 | { |
3456 | 0 | while (*pszContentToParse != '\0') |
3457 | 0 | { |
3458 | 0 | if (*pszContentToParse == 13 || |
3459 | 0 | *pszContentToParse == 10 || |
3460 | 0 | *pszContentToParse == ' ' || |
3461 | 0 | (*pszContentToParse >= '0' && |
3462 | 0 | *pszContentToParse <= '9') || |
3463 | 0 | *pszContentToParse == '.' || |
3464 | 0 | *pszContentToParse == '-' || |
3465 | 0 | *pszContentToParse == 'l' || |
3466 | 0 | *pszContentToParse == 'm' || |
3467 | 0 | *pszContentToParse == 'n' || |
3468 | 0 | *pszContentToParse == 'W') |
3469 | 0 | pszContentToParse++; |
3470 | 0 | else |
3471 | 0 | break; |
3472 | 0 | } |
3473 | 0 | } |
3474 | | |
3475 | 9.66k | GDALPDFParseStreamContent(pszContentToParse, poContentDict, |
3476 | 9.66k | &dfDPI, &bDPISet, &nBandsGuessed, |
3477 | 9.66k | m_asTiles, bResetTiles); |
3478 | 9.66k | CPLFree(pszContent); |
3479 | 9.66k | if (bDPISet) |
3480 | 528 | { |
3481 | 528 | dfDPI *= dfScaleDPI; |
3482 | | |
3483 | 528 | CPLDebug("PDF", "DPI guessed from contents stream = %.16g", |
3484 | 528 | dfDPI); |
3485 | 528 | SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI)); |
3486 | 528 | if (bResetTiles) |
3487 | 0 | m_asTiles.resize(0); |
3488 | 528 | } |
3489 | 9.14k | else |
3490 | 9.14k | m_asTiles.resize(0); |
3491 | 9.66k | } |
3492 | 9.80k | } |
3493 | 10.1k | } |
3494 | | |
3495 | 38.0k | GDALPDFObject *poUserUnit = nullptr; |
3496 | 38.0k | if ((poUserUnit = poPageDict->Get("UserUnit")) != nullptr && |
3497 | 5.90k | (poUserUnit->GetType() == PDFObjectType_Int || |
3498 | 243 | poUserUnit->GetType() == PDFObjectType_Real)) |
3499 | 5.66k | { |
3500 | 5.66k | dfDPI = ROUND_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI, 1e-5); |
3501 | 5.66k | CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", dfDPI); |
3502 | 5.66k | } |
3503 | 38.0k | } |
3504 | | |
3505 | | /************************************************************************/ |
3506 | | /* FindXMP() */ |
3507 | | /************************************************************************/ |
3508 | | |
3509 | | void PDFDataset::FindXMP(GDALPDFObject *poObj) |
3510 | 0 | { |
3511 | 0 | if (poObj->GetType() != PDFObjectType_Dictionary) |
3512 | 0 | return; |
3513 | | |
3514 | 0 | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3515 | 0 | GDALPDFObject *poType = poDict->Get("Type"); |
3516 | 0 | GDALPDFObject *poSubtype = poDict->Get("Subtype"); |
3517 | 0 | if (poType == nullptr || poType->GetType() != PDFObjectType_Name || |
3518 | 0 | poType->GetName() != "Metadata" || poSubtype == nullptr || |
3519 | 0 | poSubtype->GetType() != PDFObjectType_Name || |
3520 | 0 | poSubtype->GetName() != "XML") |
3521 | 0 | { |
3522 | 0 | return; |
3523 | 0 | } |
3524 | | |
3525 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
3526 | 0 | if (poStream == nullptr) |
3527 | 0 | return; |
3528 | | |
3529 | 0 | char *pszContent = poStream->GetBytes(); |
3530 | 0 | const auto nLength = poStream->GetLength(); |
3531 | 0 | if (pszContent != nullptr && nLength > 15 && |
3532 | 0 | STARTS_WITH(pszContent, "<?xpacket begin=")) |
3533 | 0 | { |
3534 | 0 | char *apszMDList[2]; |
3535 | 0 | apszMDList[0] = pszContent; |
3536 | 0 | apszMDList[1] = nullptr; |
3537 | 0 | SetMetadata(apszMDList, "xml:XMP"); |
3538 | 0 | } |
3539 | 0 | CPLFree(pszContent); |
3540 | 0 | } |
3541 | | |
3542 | | /************************************************************************/ |
3543 | | /* ParseInfo() */ |
3544 | | /************************************************************************/ |
3545 | | |
3546 | | void PDFDataset::ParseInfo(GDALPDFObject *poInfoObj) |
3547 | 37.9k | { |
3548 | 37.9k | if (poInfoObj->GetType() != PDFObjectType_Dictionary) |
3549 | 28.2k | return; |
3550 | | |
3551 | 9.63k | GDALPDFDictionary *poInfoObjDict = poInfoObj->GetDictionary(); |
3552 | 9.63k | GDALPDFObject *poItem = nullptr; |
3553 | 9.63k | int bOneMDISet = FALSE; |
3554 | 9.63k | if ((poItem = poInfoObjDict->Get("Author")) != nullptr && |
3555 | 2.55k | poItem->GetType() == PDFObjectType_String) |
3556 | 2.54k | { |
3557 | 2.54k | SetMetadataItem("AUTHOR", poItem->GetString().c_str()); |
3558 | 2.54k | bOneMDISet = TRUE; |
3559 | 2.54k | } |
3560 | 9.63k | if ((poItem = poInfoObjDict->Get("Creator")) != nullptr && |
3561 | 6.45k | poItem->GetType() == PDFObjectType_String) |
3562 | 6.44k | { |
3563 | 6.44k | SetMetadataItem("CREATOR", poItem->GetString().c_str()); |
3564 | 6.44k | bOneMDISet = TRUE; |
3565 | 6.44k | } |
3566 | 9.63k | if ((poItem = poInfoObjDict->Get("Keywords")) != nullptr && |
3567 | 1.09k | poItem->GetType() == PDFObjectType_String) |
3568 | 1.09k | { |
3569 | 1.09k | SetMetadataItem("KEYWORDS", poItem->GetString().c_str()); |
3570 | 1.09k | bOneMDISet = TRUE; |
3571 | 1.09k | } |
3572 | 9.63k | if ((poItem = poInfoObjDict->Get("Subject")) != nullptr && |
3573 | 1.61k | poItem->GetType() == PDFObjectType_String) |
3574 | 1.61k | { |
3575 | 1.61k | SetMetadataItem("SUBJECT", poItem->GetString().c_str()); |
3576 | 1.61k | bOneMDISet = TRUE; |
3577 | 1.61k | } |
3578 | 9.63k | if ((poItem = poInfoObjDict->Get("Title")) != nullptr && |
3579 | 3.20k | poItem->GetType() == PDFObjectType_String) |
3580 | 3.16k | { |
3581 | 3.16k | SetMetadataItem("TITLE", poItem->GetString().c_str()); |
3582 | 3.16k | bOneMDISet = TRUE; |
3583 | 3.16k | } |
3584 | 9.63k | if ((poItem = poInfoObjDict->Get("Producer")) != nullptr && |
3585 | 7.98k | poItem->GetType() == PDFObjectType_String) |
3586 | 7.37k | { |
3587 | 7.37k | if (bOneMDISet || |
3588 | 1.38k | poItem->GetString() != "PoDoFo - http://podofo.sf.net") |
3589 | 7.36k | { |
3590 | 7.36k | SetMetadataItem("PRODUCER", poItem->GetString().c_str()); |
3591 | 7.36k | bOneMDISet = TRUE; |
3592 | 7.36k | } |
3593 | 7.37k | } |
3594 | 9.63k | if ((poItem = poInfoObjDict->Get("CreationDate")) != nullptr && |
3595 | 7.94k | poItem->GetType() == PDFObjectType_String) |
3596 | 7.91k | { |
3597 | 7.91k | if (bOneMDISet) |
3598 | 7.14k | SetMetadataItem("CREATION_DATE", poItem->GetString().c_str()); |
3599 | 7.91k | } |
3600 | 9.63k | } |
3601 | | |
3602 | | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3603 | | |
3604 | | /************************************************************************/ |
3605 | | /* AddLayer() */ |
3606 | | /************************************************************************/ |
3607 | | |
3608 | | void PDFDataset::AddLayer(const std::string &osName, int iPage) |
3609 | 917k | { |
3610 | 917k | LayerStruct layerStruct; |
3611 | 917k | layerStruct.osName = osName; |
3612 | 917k | layerStruct.nInsertIdx = static_cast<int>(m_oLayerNameSet.size()); |
3613 | 917k | layerStruct.iPage = iPage; |
3614 | 917k | m_oLayerNameSet.emplace_back(std::move(layerStruct)); |
3615 | 917k | } |
3616 | | |
3617 | | /************************************************************************/ |
3618 | | /* SortLayerList() */ |
3619 | | /************************************************************************/ |
3620 | | |
3621 | | void PDFDataset::SortLayerList() |
3622 | 9.88k | { |
3623 | 9.88k | if (!m_oLayerNameSet.empty()) |
3624 | 6.33k | { |
3625 | | // Sort layers by prioritizing page number and then insertion index |
3626 | 6.33k | std::sort(m_oLayerNameSet.begin(), m_oLayerNameSet.end(), |
3627 | 6.33k | [](const LayerStruct &a, const LayerStruct &b) |
3628 | 1.81M | { |
3629 | 1.81M | if (a.iPage < b.iPage) |
3630 | 1.23k | return true; |
3631 | 1.80M | if (a.iPage > b.iPage) |
3632 | 10 | return false; |
3633 | 1.80M | return a.nInsertIdx < b.nInsertIdx; |
3634 | 1.80M | }); |
3635 | 6.33k | } |
3636 | 9.88k | } |
3637 | | |
3638 | | /************************************************************************/ |
3639 | | /* CreateLayerList() */ |
3640 | | /************************************************************************/ |
3641 | | |
3642 | | void PDFDataset::CreateLayerList() |
3643 | 9.88k | { |
3644 | 9.88k | SortLayerList(); |
3645 | | |
3646 | 9.88k | if (m_oLayerNameSet.size() >= 100) |
3647 | 246 | { |
3648 | 246 | for (const auto &oLayerStruct : m_oLayerNameSet) |
3649 | 879k | { |
3650 | 879k | m_aosLayerNames.AddNameValue( |
3651 | 879k | CPLSPrintf("LAYER_%03d_NAME", m_aosLayerNames.size()), |
3652 | 879k | oLayerStruct.osName.c_str()); |
3653 | 879k | } |
3654 | 246 | } |
3655 | 9.64k | else |
3656 | 9.64k | { |
3657 | 9.64k | for (const auto &oLayerStruct : m_oLayerNameSet) |
3658 | 38.3k | { |
3659 | 38.3k | m_aosLayerNames.AddNameValue( |
3660 | 38.3k | CPLSPrintf("LAYER_%02d_NAME", m_aosLayerNames.size()), |
3661 | 38.3k | oLayerStruct.osName.c_str()); |
3662 | 38.3k | } |
3663 | 9.64k | } |
3664 | 9.88k | } |
3665 | | |
3666 | | /************************************************************************/ |
3667 | | /* BuildPostfixedLayerNameAndAddLayer() */ |
3668 | | /************************************************************************/ |
3669 | | |
3670 | | /** Append a suffix with the page number(s) to the provided layer name, if |
3671 | | * it makes sense (that is if it is a multiple page PDF and we haven't selected |
3672 | | * a specific name). And also call AddLayer() on it if successful. |
3673 | | * If may return an empty string if the layer isn't used by the page of interest |
3674 | | */ |
3675 | | std::string PDFDataset::BuildPostfixedLayerNameAndAddLayer( |
3676 | | const std::string &osName, const std::pair<int, int> &oOCGRef, |
3677 | | int iPageOfInterest, int nPageCount) |
3678 | 914k | { |
3679 | 914k | std::string osPostfixedName = osName; |
3680 | 914k | int iLayerPage = 0; |
3681 | 914k | if (nPageCount > 1 && !m_oMapOCGNumGenToPages.empty()) |
3682 | 342 | { |
3683 | 342 | const auto oIterToPages = m_oMapOCGNumGenToPages.find(oOCGRef); |
3684 | 342 | if (oIterToPages != m_oMapOCGNumGenToPages.end()) |
3685 | 282 | { |
3686 | 282 | const auto &anPages = oIterToPages->second; |
3687 | 282 | if (iPageOfInterest > 0) |
3688 | 0 | { |
3689 | 0 | if (std::find(anPages.begin(), anPages.end(), |
3690 | 0 | iPageOfInterest) == anPages.end()) |
3691 | 0 | { |
3692 | 0 | return std::string(); |
3693 | 0 | } |
3694 | 0 | } |
3695 | 282 | else if (anPages.size() == 1) |
3696 | 279 | { |
3697 | 279 | iLayerPage = anPages.front(); |
3698 | 279 | osPostfixedName += CPLSPrintf(" (page %d)", anPages.front()); |
3699 | 279 | } |
3700 | 3 | else |
3701 | 3 | { |
3702 | 3 | osPostfixedName += " (pages "; |
3703 | 12 | for (size_t j = 0; j < anPages.size(); ++j) |
3704 | 9 | { |
3705 | 9 | if (j > 0) |
3706 | 6 | osPostfixedName += ", "; |
3707 | 9 | osPostfixedName += CPLSPrintf("%d", anPages[j]); |
3708 | 9 | } |
3709 | 3 | osPostfixedName += ')'; |
3710 | 3 | } |
3711 | 282 | } |
3712 | 342 | } |
3713 | | |
3714 | 914k | AddLayer(osPostfixedName, iLayerPage); |
3715 | | |
3716 | 914k | return osPostfixedName; |
3717 | 914k | } |
3718 | | |
3719 | | #endif // defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3720 | | |
3721 | | #ifdef HAVE_POPPLER |
3722 | | |
3723 | | /************************************************************************/ |
3724 | | /* ExploreLayersPoppler() */ |
3725 | | /************************************************************************/ |
3726 | | |
3727 | | void PDFDataset::ExploreLayersPoppler(GDALPDFArray *poArray, |
3728 | | int iPageOfInterest, int nPageCount, |
3729 | | CPLString osTopLayer, int nRecLevel, |
3730 | | int &nVisited, bool &bStop) |
3731 | 43.3k | { |
3732 | 43.3k | if (nRecLevel == 16 || nVisited == 1000) |
3733 | 261 | { |
3734 | 261 | CPLError( |
3735 | 261 | CE_Failure, CPLE_AppDefined, |
3736 | 261 | "ExploreLayersPoppler(): too deep exploration or too many items"); |
3737 | 261 | bStop = true; |
3738 | 261 | return; |
3739 | 261 | } |
3740 | 43.0k | if (bStop) |
3741 | 0 | return; |
3742 | | |
3743 | 43.0k | int nLength = poArray->GetLength(); |
3744 | 43.0k | CPLString osCurLayer; |
3745 | 2.78M | for (int i = 0; i < nLength; i++) |
3746 | 2.74M | { |
3747 | 2.74M | nVisited++; |
3748 | 2.74M | GDALPDFObject *poObj = poArray->Get(i); |
3749 | 2.74M | if (poObj == nullptr) |
3750 | 132k | continue; |
3751 | 2.61M | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
3752 | 1.75k | { |
3753 | 1.75k | std::string osName = |
3754 | 1.75k | PDFSanitizeLayerName(poObj->GetString().c_str()); |
3755 | 1.75k | if (!osTopLayer.empty()) |
3756 | 1.12k | { |
3757 | 1.12k | osTopLayer += '.'; |
3758 | 1.12k | osTopLayer += osName; |
3759 | 1.12k | } |
3760 | 629 | else |
3761 | 629 | osTopLayer = std::move(osName); |
3762 | 1.75k | AddLayer(osTopLayer, 0); |
3763 | 1.75k | m_oLayerOCGListPoppler.push_back(std::pair(osTopLayer, nullptr)); |
3764 | 1.75k | } |
3765 | 2.61M | else if (poObj->GetType() == PDFObjectType_Array) |
3766 | 34.5k | { |
3767 | 34.5k | ExploreLayersPoppler(poObj->GetArray(), iPageOfInterest, nPageCount, |
3768 | 34.5k | osCurLayer, nRecLevel + 1, nVisited, bStop); |
3769 | 34.5k | if (bStop) |
3770 | 4.01k | return; |
3771 | 30.5k | osCurLayer = ""; |
3772 | 30.5k | } |
3773 | 2.57M | else if (poObj->GetType() == PDFObjectType_Dictionary) |
3774 | 1.50M | { |
3775 | 1.50M | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3776 | 1.50M | GDALPDFObject *poName = poDict->Get("Name"); |
3777 | 1.50M | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
3778 | 1.39M | { |
3779 | 1.39M | std::string osName = |
3780 | 1.39M | PDFSanitizeLayerName(poName->GetString().c_str()); |
3781 | | /* coverity[copy_paste_error] */ |
3782 | 1.39M | if (!osTopLayer.empty()) |
3783 | 1.30M | { |
3784 | 1.30M | osCurLayer = osTopLayer; |
3785 | 1.30M | osCurLayer += '.'; |
3786 | 1.30M | osCurLayer += osName; |
3787 | 1.30M | } |
3788 | 94.0k | else |
3789 | 94.0k | osCurLayer = std::move(osName); |
3790 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
3791 | | |
3792 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3793 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3794 | | const |
3795 | | #endif |
3796 | 1.39M | OCGs *optContentConfig = |
3797 | 1.39M | m_poDocPoppler->getOptContentConfig(); |
3798 | 1.39M | struct Ref r; |
3799 | 1.39M | r.num = poObj->GetRefNum().toInt(); |
3800 | 1.39M | r.gen = poObj->GetRefGen(); |
3801 | 1.39M | OptionalContentGroup *ocg = optContentConfig->findOcgByRef(r); |
3802 | 1.39M | if (ocg) |
3803 | 914k | { |
3804 | 914k | const auto oRefPair = std::pair(poObj->GetRefNum().toInt(), |
3805 | 914k | poObj->GetRefGen()); |
3806 | 914k | const std::string osPostfixedName = |
3807 | 914k | BuildPostfixedLayerNameAndAddLayer( |
3808 | 914k | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
3809 | 914k | if (osPostfixedName.empty()) |
3810 | 0 | continue; |
3811 | | |
3812 | 914k | m_oLayerOCGListPoppler.push_back( |
3813 | 914k | std::make_pair(osPostfixedName, ocg)); |
3814 | 914k | m_aoLayerWithRef.emplace_back(osPostfixedName.c_str(), |
3815 | 914k | poObj->GetRefNum(), r.gen); |
3816 | 914k | } |
3817 | 1.39M | } |
3818 | 1.50M | } |
3819 | 2.61M | } |
3820 | 43.0k | } |
3821 | | |
3822 | | /************************************************************************/ |
3823 | | /* FindLayersPoppler() */ |
3824 | | /************************************************************************/ |
3825 | | |
3826 | | void PDFDataset::FindLayersPoppler(int iPageOfInterest) |
3827 | 37.9k | { |
3828 | 37.9k | int nPageCount = 0; |
3829 | 37.9k | const auto poPages = GetPagesKids(); |
3830 | 37.9k | if (poPages) |
3831 | 36.8k | nPageCount = poPages->GetLength(); |
3832 | | |
3833 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3834 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3835 | | const |
3836 | | #endif |
3837 | 37.9k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3838 | 37.9k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3839 | 28.1k | return; |
3840 | | |
3841 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3842 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3843 | | const |
3844 | | #endif |
3845 | 9.88k | Array *array = optContentConfig->getOrderArray(); |
3846 | 9.88k | if (array) |
3847 | 8.82k | { |
3848 | 8.82k | GDALPDFArray *poArray = GDALPDFCreateArray(array); |
3849 | 8.82k | int nVisited = 0; |
3850 | 8.82k | bool bStop = false; |
3851 | 8.82k | ExploreLayersPoppler(poArray, iPageOfInterest, nPageCount, CPLString(), |
3852 | 8.82k | 0, nVisited, bStop); |
3853 | 8.82k | delete poArray; |
3854 | 8.82k | } |
3855 | 1.06k | else |
3856 | 1.06k | { |
3857 | 1.06k | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3858 | 2.09k | { |
3859 | 2.09k | auto ocg = refOCGPair.second.get(); |
3860 | 2.09k | if (ocg != nullptr && ocg->getName() != nullptr) |
3861 | 1.94k | { |
3862 | 1.94k | const char *pszLayerName = |
3863 | 1.94k | reinterpret_cast<const char *>(ocg->getName()->c_str()); |
3864 | 1.94k | AddLayer(pszLayerName, 0); |
3865 | 1.94k | m_oLayerOCGListPoppler.push_back( |
3866 | 1.94k | std::make_pair(CPLString(pszLayerName), ocg)); |
3867 | 1.94k | } |
3868 | 2.09k | } |
3869 | 1.06k | } |
3870 | | |
3871 | 9.88k | CreateLayerList(); |
3872 | 9.88k | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
3873 | 9.88k | } |
3874 | | |
3875 | | /************************************************************************/ |
3876 | | /* TurnLayersOnOffPoppler() */ |
3877 | | /************************************************************************/ |
3878 | | |
3879 | | void PDFDataset::TurnLayersOnOffPoppler() |
3880 | 37.9k | { |
3881 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3882 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3883 | | const |
3884 | | #endif |
3885 | 37.9k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3886 | 37.9k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3887 | 28.1k | return; |
3888 | | |
3889 | | // Which layers to turn ON ? |
3890 | 9.88k | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
3891 | 9.88k | if (pszLayers) |
3892 | 0 | { |
3893 | 0 | int i; |
3894 | 0 | int bAll = EQUAL(pszLayers, "ALL"); |
3895 | 0 | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3896 | 0 | { |
3897 | 0 | auto ocg = refOCGPair.second.get(); |
3898 | 0 | ocg->setState((bAll) ? OptionalContentGroup::On |
3899 | 0 | : OptionalContentGroup::Off); |
3900 | 0 | } |
3901 | |
|
3902 | 0 | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
3903 | 0 | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
3904 | 0 | { |
3905 | 0 | bool isFound = false; |
3906 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
3907 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
3908 | 0 | { |
3909 | 0 | if (oIter2->first != papszLayers[i]) |
3910 | 0 | continue; |
3911 | | |
3912 | 0 | isFound = true; |
3913 | 0 | auto oIter = oIter2; |
3914 | 0 | if (oIter->second) |
3915 | 0 | { |
3916 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
3917 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3918 | 0 | } |
3919 | | |
3920 | | // Turn child layers on, unless there's one of them explicitly |
3921 | | // listed in the list. |
3922 | 0 | size_t nLen = strlen(papszLayers[i]); |
3923 | 0 | int bFoundChildLayer = FALSE; |
3924 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3925 | 0 | for (; |
3926 | 0 | oIter != m_oLayerOCGListPoppler.end() && !bFoundChildLayer; |
3927 | 0 | ++oIter) |
3928 | 0 | { |
3929 | 0 | if (oIter->first.size() > nLen && |
3930 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
3931 | 0 | 0 && |
3932 | 0 | oIter->first[nLen] == '.') |
3933 | 0 | { |
3934 | 0 | for (int j = 0; papszLayers[j] != nullptr; j++) |
3935 | 0 | { |
3936 | 0 | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
3937 | 0 | 0) |
3938 | 0 | { |
3939 | 0 | bFoundChildLayer = TRUE; |
3940 | 0 | break; |
3941 | 0 | } |
3942 | 0 | } |
3943 | 0 | } |
3944 | 0 | } |
3945 | |
|
3946 | 0 | if (!bFoundChildLayer) |
3947 | 0 | { |
3948 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3949 | 0 | for (; oIter != m_oLayerOCGListPoppler.end() && |
3950 | 0 | !bFoundChildLayer; |
3951 | 0 | ++oIter) |
3952 | 0 | { |
3953 | 0 | if (oIter->first.size() > nLen && |
3954 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], |
3955 | 0 | nLen) == 0 && |
3956 | 0 | oIter->first[nLen] == '.') |
3957 | 0 | { |
3958 | 0 | if (oIter->second) |
3959 | 0 | { |
3960 | | // CPLDebug("PDF", "Turn '%s' on too", |
3961 | | // oIter->first.c_str()); |
3962 | 0 | oIter->second->setState( |
3963 | 0 | OptionalContentGroup::On); |
3964 | 0 | } |
3965 | 0 | } |
3966 | 0 | } |
3967 | 0 | } |
3968 | | |
3969 | | // Turn parent layers on too |
3970 | 0 | std::string layer(papszLayers[i]); |
3971 | 0 | std::string::size_type j; |
3972 | 0 | while ((j = layer.find_last_of('.')) != std::string::npos) |
3973 | 0 | { |
3974 | 0 | layer.resize(j); |
3975 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3976 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
3977 | 0 | { |
3978 | 0 | if (oIter->first == layer && oIter->second) |
3979 | 0 | { |
3980 | | // CPLDebug("PDF", "Turn '%s' on too", |
3981 | | // layer.c_str()); |
3982 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3983 | 0 | } |
3984 | 0 | } |
3985 | 0 | } |
3986 | 0 | } |
3987 | 0 | if (!isFound) |
3988 | 0 | { |
3989 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
3990 | 0 | papszLayers[i]); |
3991 | 0 | } |
3992 | 0 | } |
3993 | 0 | CSLDestroy(papszLayers); |
3994 | |
|
3995 | 0 | m_bUseOCG = true; |
3996 | 0 | } |
3997 | | |
3998 | | // Which layers to turn OFF ? |
3999 | 9.88k | const char *pszLayersOFF = |
4000 | 9.88k | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
4001 | 9.88k | if (pszLayersOFF) |
4002 | 0 | { |
4003 | 0 | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
4004 | 0 | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
4005 | 0 | { |
4006 | 0 | bool isFound = false; |
4007 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
4008 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
4009 | 0 | { |
4010 | 0 | if (oIter2->first != papszLayersOFF[i]) |
4011 | 0 | continue; |
4012 | | |
4013 | 0 | isFound = true; |
4014 | 0 | auto oIter = oIter2; |
4015 | 0 | if (oIter->second) |
4016 | 0 | { |
4017 | | // CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]); |
4018 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
4019 | 0 | } |
4020 | | |
4021 | | // Turn child layers off too |
4022 | 0 | size_t nLen = strlen(papszLayersOFF[i]); |
4023 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
4024 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
4025 | 0 | { |
4026 | 0 | if (oIter->first.size() > nLen && |
4027 | 0 | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
4028 | 0 | nLen) == 0 && |
4029 | 0 | oIter->first[nLen] == '.') |
4030 | 0 | { |
4031 | 0 | if (oIter->second) |
4032 | 0 | { |
4033 | | // CPLDebug("PDF", "Turn '%s' off too", |
4034 | | // oIter->first.c_str()); |
4035 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
4036 | 0 | } |
4037 | 0 | } |
4038 | 0 | } |
4039 | 0 | } |
4040 | 0 | if (!isFound) |
4041 | 0 | { |
4042 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4043 | 0 | papszLayersOFF[i]); |
4044 | 0 | } |
4045 | 0 | } |
4046 | 0 | CSLDestroy(papszLayersOFF); |
4047 | |
|
4048 | 0 | m_bUseOCG = true; |
4049 | 0 | } |
4050 | 9.88k | } |
4051 | | |
4052 | | #endif |
4053 | | |
4054 | | #ifdef HAVE_PDFIUM |
4055 | | |
4056 | | /************************************************************************/ |
4057 | | /* ExploreLayersPdfium() */ |
4058 | | /************************************************************************/ |
4059 | | |
4060 | | void PDFDataset::ExploreLayersPdfium(GDALPDFArray *poArray, int iPageOfInterest, |
4061 | | int nPageCount, int nRecLevel, |
4062 | | CPLString osTopLayer) |
4063 | | { |
4064 | | if (nRecLevel == 16) |
4065 | | return; |
4066 | | |
4067 | | const int nLength = poArray->GetLength(); |
4068 | | std::string osCurLayer; |
4069 | | for (int i = 0; i < nLength; i++) |
4070 | | { |
4071 | | GDALPDFObject *poObj = poArray->Get(i); |
4072 | | if (poObj == nullptr) |
4073 | | continue; |
4074 | | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
4075 | | { |
4076 | | const std::string osName = |
4077 | | PDFSanitizeLayerName(poObj->GetString().c_str()); |
4078 | | if (!osTopLayer.empty()) |
4079 | | osTopLayer = std::string(osTopLayer).append(".").append(osName); |
4080 | | else |
4081 | | osTopLayer = osName; |
4082 | | AddLayer(osTopLayer, 0); |
4083 | | m_oMapLayerNameToOCGNumGenPdfium[osTopLayer] = std::pair(-1, -1); |
4084 | | } |
4085 | | else if (poObj->GetType() == PDFObjectType_Array) |
4086 | | { |
4087 | | ExploreLayersPdfium(poObj->GetArray(), iPageOfInterest, nPageCount, |
4088 | | nRecLevel + 1, osCurLayer); |
4089 | | osCurLayer.clear(); |
4090 | | } |
4091 | | else if (poObj->GetType() == PDFObjectType_Dictionary) |
4092 | | { |
4093 | | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
4094 | | GDALPDFObject *poName = poDict->Get("Name"); |
4095 | | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
4096 | | { |
4097 | | std::string osName = |
4098 | | PDFSanitizeLayerName(poName->GetString().c_str()); |
4099 | | // coverity[copy_paste_error] |
4100 | | if (!osTopLayer.empty()) |
4101 | | { |
4102 | | osCurLayer = |
4103 | | std::string(osTopLayer).append(".").append(osName); |
4104 | | } |
4105 | | else |
4106 | | osCurLayer = std::move(osName); |
4107 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
4108 | | |
4109 | | const auto oRefPair = |
4110 | | std::pair(poObj->GetRefNum().toInt(), poObj->GetRefGen()); |
4111 | | const std::string osPostfixedName = |
4112 | | BuildPostfixedLayerNameAndAddLayer( |
4113 | | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
4114 | | if (osPostfixedName.empty()) |
4115 | | continue; |
4116 | | |
4117 | | m_aoLayerWithRef.emplace_back( |
4118 | | osPostfixedName, poObj->GetRefNum(), poObj->GetRefGen()); |
4119 | | m_oMapLayerNameToOCGNumGenPdfium[osPostfixedName] = oRefPair; |
4120 | | } |
4121 | | } |
4122 | | } |
4123 | | } |
4124 | | |
4125 | | /************************************************************************/ |
4126 | | /* FindLayersPdfium() */ |
4127 | | /************************************************************************/ |
4128 | | |
4129 | | void PDFDataset::FindLayersPdfium(int iPageOfInterest) |
4130 | | { |
4131 | | int nPageCount = 0; |
4132 | | const auto poPages = GetPagesKids(); |
4133 | | if (poPages) |
4134 | | nPageCount = poPages->GetLength(); |
4135 | | |
4136 | | GDALPDFObject *poCatalog = GetCatalog(); |
4137 | | if (poCatalog == nullptr || |
4138 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4139 | | return; |
4140 | | GDALPDFObject *poOrder = poCatalog->LookupObject("OCProperties.D.Order"); |
4141 | | if (poOrder != nullptr && poOrder->GetType() == PDFObjectType_Array) |
4142 | | { |
4143 | | ExploreLayersPdfium(poOrder->GetArray(), iPageOfInterest, nPageCount, |
4144 | | 0); |
4145 | | } |
4146 | | #if 0 |
4147 | | else |
4148 | | { |
4149 | | GDALPDFObject* poOCGs = poD->GetDictionary()->Get("OCGs"); |
4150 | | if( poOCGs != nullptr && poOCGs->GetType() == PDFObjectType_Array ) |
4151 | | { |
4152 | | GDALPDFArray* poArray = poOCGs->GetArray(); |
4153 | | int nLength = poArray->GetLength(); |
4154 | | for(int i=0;i<nLength;i++) |
4155 | | { |
4156 | | GDALPDFObject* poObj = poArray->Get(i); |
4157 | | if( poObj != nullptr ) |
4158 | | { |
4159 | | // TODO ? |
4160 | | } |
4161 | | } |
4162 | | } |
4163 | | } |
4164 | | #endif |
4165 | | |
4166 | | CreateLayerList(); |
4167 | | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
4168 | | } |
4169 | | |
4170 | | /************************************************************************/ |
4171 | | /* TurnLayersOnOffPdfium() */ |
4172 | | /************************************************************************/ |
4173 | | |
4174 | | void PDFDataset::TurnLayersOnOffPdfium() |
4175 | | { |
4176 | | GDALPDFObject *poCatalog = GetCatalog(); |
4177 | | if (poCatalog == nullptr || |
4178 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4179 | | return; |
4180 | | GDALPDFObject *poOCGs = poCatalog->LookupObject("OCProperties.OCGs"); |
4181 | | if (poOCGs == nullptr || poOCGs->GetType() != PDFObjectType_Array) |
4182 | | return; |
4183 | | |
4184 | | // Which layers to turn ON ? |
4185 | | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
4186 | | if (pszLayers) |
4187 | | { |
4188 | | int i; |
4189 | | int bAll = EQUAL(pszLayers, "ALL"); |
4190 | | |
4191 | | GDALPDFArray *poOCGsArray = poOCGs->GetArray(); |
4192 | | int nLength = poOCGsArray->GetLength(); |
4193 | | for (i = 0; i < nLength; i++) |
4194 | | { |
4195 | | GDALPDFObject *poOCG = poOCGsArray->Get(i); |
4196 | | m_oMapOCGNumGenToVisibilityStatePdfium[std::pair( |
4197 | | poOCG->GetRefNum().toInt(), poOCG->GetRefGen())] = |
4198 | | (bAll) ? VISIBILITY_ON : VISIBILITY_OFF; |
4199 | | } |
4200 | | |
4201 | | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
4202 | | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
4203 | | { |
4204 | | auto oIter = m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4205 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4206 | | { |
4207 | | if (oIter->second.first >= 0) |
4208 | | { |
4209 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
4210 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4211 | | VISIBILITY_ON; |
4212 | | } |
4213 | | |
4214 | | // Turn child layers on, unless there's one of them explicitly |
4215 | | // listed in the list. |
4216 | | size_t nLen = strlen(papszLayers[i]); |
4217 | | int bFoundChildLayer = FALSE; |
4218 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4219 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4220 | | !bFoundChildLayer; |
4221 | | oIter++) |
4222 | | { |
4223 | | if (oIter->first.size() > nLen && |
4224 | | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
4225 | | 0 && |
4226 | | oIter->first[nLen] == '.') |
4227 | | { |
4228 | | for (int j = 0; papszLayers[j] != nullptr; j++) |
4229 | | { |
4230 | | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
4231 | | 0) |
4232 | | bFoundChildLayer = TRUE; |
4233 | | } |
4234 | | } |
4235 | | } |
4236 | | |
4237 | | if (!bFoundChildLayer) |
4238 | | { |
4239 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4240 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4241 | | !bFoundChildLayer; |
4242 | | oIter++) |
4243 | | { |
4244 | | if (oIter->first.size() > nLen && |
4245 | | strncmp(oIter->first.c_str(), papszLayers[i], |
4246 | | nLen) == 0 && |
4247 | | oIter->first[nLen] == '.') |
4248 | | { |
4249 | | if (oIter->second.first >= 0) |
4250 | | { |
4251 | | // CPLDebug("PDF", "Turn '%s' on too", |
4252 | | // oIter->first.c_str()); |
4253 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4254 | | [oIter->second] = VISIBILITY_ON; |
4255 | | } |
4256 | | } |
4257 | | } |
4258 | | } |
4259 | | |
4260 | | // Turn parent layers on too |
4261 | | char *pszLastDot = nullptr; |
4262 | | while ((pszLastDot = strrchr(papszLayers[i], '.')) != nullptr) |
4263 | | { |
4264 | | *pszLastDot = '\0'; |
4265 | | oIter = |
4266 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4267 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4268 | | { |
4269 | | if (oIter->second.first >= 0) |
4270 | | { |
4271 | | // CPLDebug("PDF", "Turn '%s' on too", |
4272 | | // papszLayers[i]); |
4273 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4274 | | [oIter->second] = VISIBILITY_ON; |
4275 | | } |
4276 | | } |
4277 | | } |
4278 | | } |
4279 | | else |
4280 | | { |
4281 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4282 | | papszLayers[i]); |
4283 | | } |
4284 | | } |
4285 | | CSLDestroy(papszLayers); |
4286 | | |
4287 | | m_bUseOCG = true; |
4288 | | } |
4289 | | |
4290 | | // Which layers to turn OFF ? |
4291 | | const char *pszLayersOFF = |
4292 | | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
4293 | | if (pszLayersOFF) |
4294 | | { |
4295 | | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
4296 | | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
4297 | | { |
4298 | | auto oIter = |
4299 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayersOFF[i]); |
4300 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4301 | | { |
4302 | | if (oIter->second.first >= 0) |
4303 | | { |
4304 | | // CPLDebug("PDF", "Turn '%s' (%d,%d) off", |
4305 | | // papszLayersOFF[i], oIter->second.first, |
4306 | | // oIter->second.second); |
4307 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4308 | | VISIBILITY_OFF; |
4309 | | } |
4310 | | |
4311 | | // Turn child layers off too |
4312 | | size_t nLen = strlen(papszLayersOFF[i]); |
4313 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4314 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end(); oIter++) |
4315 | | { |
4316 | | if (oIter->first.size() > nLen && |
4317 | | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
4318 | | nLen) == 0 && |
4319 | | oIter->first[nLen] == '.') |
4320 | | { |
4321 | | if (oIter->second.first >= 0) |
4322 | | { |
4323 | | // CPLDebug("PDF", "Turn '%s' off too", |
4324 | | // oIter->first.c_str()); |
4325 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4326 | | [oIter->second] = VISIBILITY_OFF; |
4327 | | } |
4328 | | } |
4329 | | } |
4330 | | } |
4331 | | else |
4332 | | { |
4333 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4334 | | papszLayersOFF[i]); |
4335 | | } |
4336 | | } |
4337 | | CSLDestroy(papszLayersOFF); |
4338 | | |
4339 | | m_bUseOCG = true; |
4340 | | } |
4341 | | } |
4342 | | |
4343 | | /************************************************************************/ |
4344 | | /* GetVisibilityStateForOGCPdfium() */ |
4345 | | /************************************************************************/ |
4346 | | |
4347 | | PDFDataset::VisibilityState PDFDataset::GetVisibilityStateForOGCPdfium(int nNum, |
4348 | | int nGen) |
4349 | | { |
4350 | | auto oIter = |
4351 | | m_oMapOCGNumGenToVisibilityStatePdfium.find(std::pair(nNum, nGen)); |
4352 | | if (oIter == m_oMapOCGNumGenToVisibilityStatePdfium.end()) |
4353 | | return VISIBILITY_DEFAULT; |
4354 | | return oIter->second; |
4355 | | } |
4356 | | |
4357 | | #endif /* HAVE_PDFIUM */ |
4358 | | |
4359 | | /************************************************************************/ |
4360 | | /* GetPagesKids() */ |
4361 | | /************************************************************************/ |
4362 | | |
4363 | | GDALPDFArray *PDFDataset::GetPagesKids() |
4364 | 75.9k | { |
4365 | 75.9k | const auto poCatalog = GetCatalog(); |
4366 | 75.9k | if (!poCatalog || poCatalog->GetType() != PDFObjectType_Dictionary) |
4367 | 0 | { |
4368 | 0 | return nullptr; |
4369 | 0 | } |
4370 | 75.9k | const auto poKids = poCatalog->LookupObject("Pages.Kids"); |
4371 | 75.9k | if (!poKids || poKids->GetType() != PDFObjectType_Array) |
4372 | 2.24k | { |
4373 | 2.24k | return nullptr; |
4374 | 2.24k | } |
4375 | 73.7k | return poKids->GetArray(); |
4376 | 75.9k | } |
4377 | | |
4378 | | /************************************************************************/ |
4379 | | /* MapOCGsToPages() */ |
4380 | | /************************************************************************/ |
4381 | | |
4382 | | void PDFDataset::MapOCGsToPages() |
4383 | 37.9k | { |
4384 | 37.9k | const auto poKidsArray = GetPagesKids(); |
4385 | 37.9k | if (!poKidsArray) |
4386 | 1.12k | { |
4387 | 1.12k | return; |
4388 | 1.12k | } |
4389 | 36.8k | const int nKidsArrayLength = poKidsArray->GetLength(); |
4390 | 104k | for (int iPage = 0; iPage < nKidsArrayLength; ++iPage) |
4391 | 67.4k | { |
4392 | 67.4k | const auto poPage = poKidsArray->Get(iPage); |
4393 | 67.4k | if (poPage && poPage->GetType() == PDFObjectType_Dictionary) |
4394 | 45.7k | { |
4395 | 45.7k | const auto poXObject = poPage->LookupObject("Resources.XObject"); |
4396 | 45.7k | if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary) |
4397 | 14.3k | { |
4398 | 14.3k | for (const auto &oNameObjectPair : |
4399 | 14.3k | poXObject->GetDictionary()->GetValues()) |
4400 | 91.7k | { |
4401 | 91.7k | const auto poProperties = |
4402 | 91.7k | oNameObjectPair.second->LookupObject( |
4403 | 91.7k | "Resources.Properties"); |
4404 | 91.7k | if (poProperties && |
4405 | 242 | poProperties->GetType() == PDFObjectType_Dictionary) |
4406 | 239 | { |
4407 | 239 | const auto &oMap = |
4408 | 239 | poProperties->GetDictionary()->GetValues(); |
4409 | 239 | for (const auto &[osKey, poObj] : oMap) |
4410 | 447 | { |
4411 | 447 | if (poObj->GetRefNum().toBool() && |
4412 | 393 | poObj->GetType() == PDFObjectType_Dictionary) |
4413 | 386 | { |
4414 | 386 | GDALPDFObject *poType = |
4415 | 386 | poObj->GetDictionary()->Get("Type"); |
4416 | 386 | GDALPDFObject *poName = |
4417 | 386 | poObj->GetDictionary()->Get("Name"); |
4418 | 386 | if (poType && |
4419 | 351 | poType->GetType() == PDFObjectType_Name && |
4420 | 351 | poType->GetName() == "OCG" && poName && |
4421 | 336 | poName->GetType() == PDFObjectType_String) |
4422 | 336 | { |
4423 | 336 | m_oMapOCGNumGenToPages |
4424 | 336 | [std::pair(poObj->GetRefNum().toInt(), |
4425 | 336 | poObj->GetRefGen())] |
4426 | 336 | .push_back(iPage + 1); |
4427 | 336 | } |
4428 | 386 | } |
4429 | 447 | } |
4430 | 239 | } |
4431 | 91.7k | } |
4432 | 14.3k | } |
4433 | 45.7k | } |
4434 | 67.4k | } |
4435 | 36.8k | } |
4436 | | |
4437 | | /************************************************************************/ |
4438 | | /* FindLayerOCG() */ |
4439 | | /************************************************************************/ |
4440 | | |
4441 | | CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary *poPageDict, |
4442 | | const char *pszLayerName) |
4443 | 9.55k | { |
4444 | 9.55k | GDALPDFObject *poProperties = |
4445 | 9.55k | poPageDict->LookupObject("Resources.Properties"); |
4446 | 9.55k | if (poProperties != nullptr && |
4447 | 3.87k | poProperties->GetType() == PDFObjectType_Dictionary) |
4448 | 3.86k | { |
4449 | 3.86k | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4450 | 3.86k | for (const auto &[osKey, poObj] : oMap) |
4451 | 16.7k | { |
4452 | 16.7k | if (poObj->GetRefNum().toBool() && |
4453 | 15.9k | poObj->GetType() == PDFObjectType_Dictionary) |
4454 | 15.9k | { |
4455 | 15.9k | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4456 | 15.9k | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4457 | 15.9k | if (poType != nullptr && |
4458 | 15.5k | poType->GetType() == PDFObjectType_Name && |
4459 | 15.5k | poType->GetName() == "OCG" && poName != nullptr && |
4460 | 15.3k | poName->GetType() == PDFObjectType_String) |
4461 | 15.3k | { |
4462 | 15.3k | if (poName->GetString() == pszLayerName) |
4463 | 0 | return osKey; |
4464 | 15.3k | } |
4465 | 15.9k | } |
4466 | 16.7k | } |
4467 | 3.86k | } |
4468 | 9.55k | return ""; |
4469 | 9.55k | } |
4470 | | |
4471 | | /************************************************************************/ |
4472 | | /* FindLayersGeneric() */ |
4473 | | /************************************************************************/ |
4474 | | |
4475 | | void PDFDataset::FindLayersGeneric(GDALPDFDictionary *poPageDict) |
4476 | 0 | { |
4477 | 0 | GDALPDFObject *poProperties = |
4478 | 0 | poPageDict->LookupObject("Resources.Properties"); |
4479 | 0 | if (poProperties != nullptr && |
4480 | 0 | poProperties->GetType() == PDFObjectType_Dictionary) |
4481 | 0 | { |
4482 | 0 | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4483 | 0 | for (const auto &[osKey, poObj] : oMap) |
4484 | 0 | { |
4485 | 0 | if (poObj->GetRefNum().toBool() && |
4486 | 0 | poObj->GetType() == PDFObjectType_Dictionary) |
4487 | 0 | { |
4488 | 0 | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4489 | 0 | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4490 | 0 | if (poType != nullptr && |
4491 | 0 | poType->GetType() == PDFObjectType_Name && |
4492 | 0 | poType->GetName() == "OCG" && poName != nullptr && |
4493 | 0 | poName->GetType() == PDFObjectType_String) |
4494 | 0 | { |
4495 | 0 | m_aoLayerWithRef.emplace_back( |
4496 | 0 | PDFSanitizeLayerName(poName->GetString().c_str()) |
4497 | 0 | .c_str(), |
4498 | 0 | poObj->GetRefNum(), poObj->GetRefGen()); |
4499 | 0 | } |
4500 | 0 | } |
4501 | 0 | } |
4502 | 0 | } |
4503 | 0 | } |
4504 | | |
4505 | | /************************************************************************/ |
4506 | | /* Open() */ |
4507 | | /************************************************************************/ |
4508 | | |
4509 | | PDFDataset *PDFDataset::Open(GDALOpenInfo *poOpenInfo) |
4510 | | |
4511 | 49.0k | { |
4512 | 49.0k | if (!PDFDatasetIdentify(poOpenInfo)) |
4513 | 0 | return nullptr; |
4514 | | |
4515 | 49.0k | const char *pszUserPwd = |
4516 | 49.0k | GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", nullptr); |
4517 | | |
4518 | 49.0k | const bool bOpenSubdataset = STARTS_WITH(poOpenInfo->pszFilename, "PDF:"); |
4519 | 49.0k | const bool bOpenSubdatasetImage = |
4520 | 49.0k | STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:"); |
4521 | 49.0k | int iPage = -1; |
4522 | 49.0k | int nImageNum = -1; |
4523 | 49.0k | std::string osSubdatasetName; |
4524 | 49.0k | const char *pszFilename = poOpenInfo->pszFilename; |
4525 | | |
4526 | 49.0k | if (bOpenSubdataset) |
4527 | 0 | { |
4528 | 0 | iPage = atoi(pszFilename + 4); |
4529 | 0 | if (iPage <= 0) |
4530 | 0 | return nullptr; |
4531 | 0 | pszFilename = strchr(pszFilename + 4, ':'); |
4532 | 0 | if (pszFilename == nullptr) |
4533 | 0 | return nullptr; |
4534 | 0 | pszFilename++; |
4535 | 0 | osSubdatasetName = CPLSPrintf("Page %d", iPage); |
4536 | 0 | } |
4537 | 49.0k | else if (bOpenSubdatasetImage) |
4538 | 0 | { |
4539 | 0 | iPage = atoi(pszFilename + 10); |
4540 | 0 | if (iPage <= 0) |
4541 | 0 | return nullptr; |
4542 | 0 | const char *pszNext = strchr(pszFilename + 10, ':'); |
4543 | 0 | if (pszNext == nullptr) |
4544 | 0 | return nullptr; |
4545 | 0 | nImageNum = atoi(pszNext + 1); |
4546 | 0 | if (nImageNum <= 0) |
4547 | 0 | return nullptr; |
4548 | 0 | pszFilename = strchr(pszNext + 1, ':'); |
4549 | 0 | if (pszFilename == nullptr) |
4550 | 0 | return nullptr; |
4551 | 0 | pszFilename++; |
4552 | 0 | osSubdatasetName = CPLSPrintf("Image %d", nImageNum); |
4553 | 0 | } |
4554 | 49.0k | else |
4555 | 49.0k | iPage = 1; |
4556 | | |
4557 | 49.0k | std::bitset<PDFLIB_COUNT> bHasLib; |
4558 | 49.0k | bHasLib.reset(); |
4559 | | // Each library set their flag |
4560 | 49.0k | #if defined(HAVE_POPPLER) |
4561 | 49.0k | bHasLib.set(PDFLIB_POPPLER); |
4562 | 49.0k | #endif // HAVE_POPPLER |
4563 | | #if defined(HAVE_PODOFO) |
4564 | | bHasLib.set(PDFLIB_PODOFO); |
4565 | | #endif // HAVE_PODOFO |
4566 | | #if defined(HAVE_PDFIUM) |
4567 | | bHasLib.set(PDFLIB_PDFIUM); |
4568 | | #endif // HAVE_PDFIUM |
4569 | | |
4570 | 49.0k | std::bitset<PDFLIB_COUNT> bUseLib; |
4571 | | |
4572 | | // More than one library available |
4573 | | // Detect which one |
4574 | 49.0k | if (bHasLib.count() != 1) |
4575 | 0 | { |
4576 | 0 | const char *pszDefaultLib = bHasLib.test(PDFLIB_PDFIUM) ? "PDFIUM" |
4577 | 0 | : bHasLib.test(PDFLIB_POPPLER) ? "POPPLER" |
4578 | 0 | : "PODOFO"; |
4579 | 0 | const char *pszPDFLib = |
4580 | 0 | GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", pszDefaultLib); |
4581 | 0 | while (true) |
4582 | 0 | { |
4583 | 0 | if (EQUAL(pszPDFLib, "POPPLER")) |
4584 | 0 | bUseLib.set(PDFLIB_POPPLER); |
4585 | 0 | else if (EQUAL(pszPDFLib, "PODOFO")) |
4586 | 0 | bUseLib.set(PDFLIB_PODOFO); |
4587 | 0 | else if (EQUAL(pszPDFLib, "PDFIUM")) |
4588 | 0 | bUseLib.set(PDFLIB_PDFIUM); |
4589 | |
|
4590 | 0 | if (bUseLib.count() != 1 || (bHasLib & bUseLib) == 0) |
4591 | 0 | { |
4592 | 0 | CPLDebug("PDF", |
4593 | 0 | "Invalid value for GDAL_PDF_LIB config option: %s. " |
4594 | 0 | "Fallback to %s", |
4595 | 0 | pszPDFLib, pszDefaultLib); |
4596 | 0 | pszPDFLib = pszDefaultLib; |
4597 | 0 | bUseLib.reset(); |
4598 | 0 | } |
4599 | 0 | else |
4600 | 0 | break; |
4601 | 0 | } |
4602 | 0 | } |
4603 | 49.0k | else |
4604 | 49.0k | bUseLib = bHasLib; |
4605 | | |
4606 | 49.0k | GDALPDFObject *poPageObj = nullptr; |
4607 | 49.0k | #ifdef HAVE_POPPLER |
4608 | 49.0k | PDFDoc *poDocPoppler = nullptr; |
4609 | 49.0k | Page *poPagePoppler = nullptr; |
4610 | 49.0k | Catalog *poCatalogPoppler = nullptr; |
4611 | 49.0k | #endif |
4612 | | #ifdef HAVE_PODOFO |
4613 | | std::unique_ptr<PoDoFo::PdfMemDocument> poDocPodofo; |
4614 | | PoDoFo::PdfPage *poPagePodofo = nullptr; |
4615 | | #endif |
4616 | | #ifdef HAVE_PDFIUM |
4617 | | TPdfiumDocumentStruct *poDocPdfium = nullptr; |
4618 | | TPdfiumPageStruct *poPagePdfium = nullptr; |
4619 | | #endif |
4620 | 49.0k | int nPages = 0; |
4621 | 49.0k | VSIVirtualHandleUniquePtr fp; |
4622 | | |
4623 | 49.0k | #ifdef HAVE_POPPLER |
4624 | 49.0k | if (bUseLib.test(PDFLIB_POPPLER)) |
4625 | 49.0k | { |
4626 | 49.0k | static bool globalParamsCreatedByGDAL = false; |
4627 | 49.0k | { |
4628 | 49.0k | CPLMutexHolderD(&hGlobalParamsMutex); |
4629 | | /* poppler global variable */ |
4630 | 49.0k | if (globalParams == nullptr) |
4631 | 11 | { |
4632 | 11 | globalParamsCreatedByGDAL = true; |
4633 | 11 | globalParams.reset(new GlobalParams()); |
4634 | 11 | } |
4635 | | |
4636 | 49.0k | globalParams->setPrintCommands(CPLTestBool( |
4637 | 49.0k | CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE"))); |
4638 | 49.0k | } |
4639 | | |
4640 | 49.0k | const auto registerErrorCallback = []() |
4641 | 97.9k | { |
4642 | | /* Set custom error handler for poppler errors */ |
4643 | 97.9k | setErrorCallback(PDFDatasetErrorFunction); |
4644 | 97.9k | assert(globalParams); // avoid CSA false positive |
4645 | 97.9k | globalParams->setErrQuiet(false); |
4646 | 97.9k | }; |
4647 | | |
4648 | 49.0k | fp.reset(VSIFOpenL(pszFilename, "rb")); |
4649 | 49.0k | if (!fp) |
4650 | 0 | return nullptr; |
4651 | | |
4652 | 49.0k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
4653 | 49.0k | { |
4654 | | // Workaround for ossfuzz only due to |
4655 | | // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37584 |
4656 | | // https://gitlab.freedesktop.org/poppler/poppler/-/issues/1137 |
4657 | 49.0k | GByte *pabyRet = nullptr; |
4658 | 49.0k | vsi_l_offset nSize = 0; |
4659 | 49.0k | if (VSIIngestFile(fp.get(), pszFilename, &pabyRet, &nSize, |
4660 | 49.0k | 10 * 1024 * 1024)) |
4661 | 49.0k | { |
4662 | | // Replace nul byte by something else so that strstr() works |
4663 | 1.71G | for (size_t i = 0; i < nSize; i++) |
4664 | 1.71G | { |
4665 | 1.71G | if (pabyRet[i] == 0) |
4666 | 66.4M | pabyRet[i] = ' '; |
4667 | 1.71G | } |
4668 | 49.0k | if (strstr(reinterpret_cast<const char *>(pabyRet), |
4669 | 49.0k | "/JBIG2Decode")) |
4670 | 13 | { |
4671 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
4672 | 13 | "/JBIG2Decode found. Giving up due to potential " |
4673 | 13 | "very long processing time."); |
4674 | 13 | CPLFree(pabyRet); |
4675 | 13 | return nullptr; |
4676 | 13 | } |
4677 | 49.0k | } |
4678 | 48.9k | CPLFree(pabyRet); |
4679 | 48.9k | } |
4680 | 0 | #endif |
4681 | | |
4682 | 0 | fp.reset(VSICreateBufferedReaderHandle(fp.release())); |
4683 | 48.9k | while (true) |
4684 | 48.9k | { |
4685 | 48.9k | fp->Seek(0, SEEK_SET); |
4686 | 48.9k | g_nPopplerErrors = 0; |
4687 | 48.9k | if (globalParamsCreatedByGDAL) |
4688 | 48.9k | registerErrorCallback(); |
4689 | 48.9k | Object oObj; |
4690 | 48.9k | auto poStream = std::make_unique<VSIPDFFileStream>( |
4691 | 48.9k | fp.get(), pszFilename, std::move(oObj)); |
4692 | 48.9k | const bool bFoundLinearizedHint = poStream->FoundLinearizedHint(); |
4693 | 48.9k | #if POPPLER_MAJOR_VERSION > 22 || \ |
4694 | 48.9k | (POPPLER_MAJOR_VERSION == 22 && POPPLER_MINOR_VERSION > 2) |
4695 | 48.9k | std::optional<GooString> osUserPwd; |
4696 | 48.9k | if (pszUserPwd) |
4697 | 0 | osUserPwd = std::optional<GooString>(pszUserPwd); |
4698 | 48.9k | try |
4699 | 48.9k | { |
4700 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
4701 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
4702 | | poDocPoppler = new PDFDoc( |
4703 | | std::move(poStream), std::optional<GooString>(), osUserPwd); |
4704 | | #else |
4705 | 48.9k | poDocPoppler = new PDFDoc( |
4706 | 48.9k | poStream.release(), std::optional<GooString>(), osUserPwd); |
4707 | 48.9k | #endif |
4708 | 48.9k | } |
4709 | 48.9k | catch (const std::exception &e) |
4710 | 48.9k | { |
4711 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4712 | 0 | "PDFDoc::PDFDoc() failed with %s", e.what()); |
4713 | 0 | return nullptr; |
4714 | 0 | } |
4715 | | #else |
4716 | | GooString *poUserPwd = nullptr; |
4717 | | if (pszUserPwd) |
4718 | | poUserPwd = new GooString(pszUserPwd); |
4719 | | poDocPoppler = new PDFDoc(poStream.release(), nullptr, poUserPwd); |
4720 | | delete poUserPwd; |
4721 | | #endif |
4722 | 48.9k | if (globalParamsCreatedByGDAL) |
4723 | 48.9k | registerErrorCallback(); |
4724 | 48.9k | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
4725 | 4.81k | { |
4726 | 4.81k | PDFFreeDoc(poDocPoppler); |
4727 | 4.81k | return nullptr; |
4728 | 4.81k | } |
4729 | | |
4730 | 44.1k | if (!poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0) |
4731 | 6.01k | { |
4732 | 6.01k | if (poDocPoppler->getErrorCode() == errEncrypted) |
4733 | 69 | { |
4734 | 69 | if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
4735 | 0 | { |
4736 | 0 | pszUserPwd = |
4737 | 0 | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4738 | 0 | PDFFreeDoc(poDocPoppler); |
4739 | | |
4740 | | /* Reset errors that could have been issued during |
4741 | | * opening and that */ |
4742 | | /* did not result in an invalid document */ |
4743 | 0 | CPLErrorReset(); |
4744 | |
|
4745 | 0 | continue; |
4746 | 0 | } |
4747 | 69 | else if (pszUserPwd == nullptr) |
4748 | 69 | { |
4749 | 69 | CPLError(CE_Failure, CPLE_AppDefined, |
4750 | 69 | "A password is needed. You can specify it " |
4751 | 69 | "through the PDF_USER_PWD " |
4752 | 69 | "configuration option / USER_PWD open option " |
4753 | 69 | "(that can be set to ASK_INTERACTIVE)"); |
4754 | 69 | } |
4755 | 0 | else |
4756 | 0 | { |
4757 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4758 | 0 | "Invalid password"); |
4759 | 0 | } |
4760 | 69 | } |
4761 | 5.94k | else |
4762 | 5.94k | { |
4763 | 5.94k | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4764 | 5.94k | } |
4765 | | |
4766 | 6.01k | PDFFreeDoc(poDocPoppler); |
4767 | 6.01k | return nullptr; |
4768 | 6.01k | } |
4769 | 38.1k | else if (poDocPoppler->isLinearized() && !bFoundLinearizedHint) |
4770 | 2 | { |
4771 | | // This is a likely defect of poppler Linearization.cc file that |
4772 | | // recognizes a file as linearized if the /Linearized hint is |
4773 | | // missing, but the content of this dictionary are present. But |
4774 | | // given the hacks of PDFFreeDoc() and |
4775 | | // VSIPDFFileStream::FillBuffer() opening such a file will |
4776 | | // result in a null-ptr deref at closing if we try to access a |
4777 | | // page and build the page cache, so just exit now |
4778 | 2 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4779 | | |
4780 | 2 | PDFFreeDoc(poDocPoppler); |
4781 | 2 | return nullptr; |
4782 | 2 | } |
4783 | 38.1k | else |
4784 | 38.1k | { |
4785 | 38.1k | break; |
4786 | 38.1k | } |
4787 | 44.1k | } |
4788 | | |
4789 | 38.1k | poCatalogPoppler = poDocPoppler->getCatalog(); |
4790 | 38.1k | if (poCatalogPoppler == nullptr || !poCatalogPoppler->isOk()) |
4791 | 0 | { |
4792 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4793 | 0 | "Invalid PDF : invalid catalog"); |
4794 | 0 | PDFFreeDoc(poDocPoppler); |
4795 | 0 | return nullptr; |
4796 | 0 | } |
4797 | | |
4798 | 38.1k | nPages = poDocPoppler->getNumPages(); |
4799 | | |
4800 | 38.1k | if (iPage == 1 && nPages > 10000 && |
4801 | 0 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_LIMIT_PAGE_COUNT", "YES"))) |
4802 | 0 | { |
4803 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
4804 | 0 | "This PDF document reports %d pages. " |
4805 | 0 | "Limiting count to 10000 for performance reasons. " |
4806 | 0 | "You may remove this limit by setting the " |
4807 | 0 | "GDAL_PDF_LIMIT_PAGE_COUNT configuration option to NO", |
4808 | 0 | nPages); |
4809 | 0 | nPages = 10000; |
4810 | 0 | } |
4811 | | |
4812 | 38.1k | if (iPage < 1 || iPage > nPages) |
4813 | 0 | { |
4814 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4815 | 0 | iPage, nPages); |
4816 | 0 | PDFFreeDoc(poDocPoppler); |
4817 | 0 | return nullptr; |
4818 | 0 | } |
4819 | | |
4820 | | /* Sanity check to validate page count */ |
4821 | 38.1k | if (iPage > 1 && nPages <= 10000 && iPage != nPages) |
4822 | 0 | { |
4823 | 0 | poPagePoppler = poCatalogPoppler->getPage(nPages); |
4824 | 0 | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4825 | 0 | { |
4826 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4827 | 0 | "Invalid PDF : invalid page count"); |
4828 | 0 | PDFFreeDoc(poDocPoppler); |
4829 | 0 | return nullptr; |
4830 | 0 | } |
4831 | 0 | } |
4832 | | |
4833 | 38.1k | poPagePoppler = poCatalogPoppler->getPage(iPage); |
4834 | 38.1k | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4835 | 132 | { |
4836 | 132 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
4837 | 132 | PDFFreeDoc(poDocPoppler); |
4838 | 132 | return nullptr; |
4839 | 132 | } |
4840 | | |
4841 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
4842 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 3) |
4843 | | const Object &oPageObj = poPagePoppler->getPageObj(); |
4844 | | #else |
4845 | | /* Here's the dirty part: this is a private member */ |
4846 | | /* so we had to #define private public to get it ! */ |
4847 | 38.0k | const Object &oPageObj = poPagePoppler->pageObj; |
4848 | 38.0k | #endif |
4849 | 38.0k | if (!oPageObj.isDict()) |
4850 | 0 | { |
4851 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4852 | 0 | "Invalid PDF : !oPageObj.isDict()"); |
4853 | 0 | PDFFreeDoc(poDocPoppler); |
4854 | 0 | return nullptr; |
4855 | 0 | } |
4856 | | |
4857 | 38.0k | poPageObj = new GDALPDFObjectPoppler(&oPageObj); |
4858 | 38.0k | Ref *poPageRef = poCatalogPoppler->getPageRef(iPage); |
4859 | 38.0k | if (poPageRef != nullptr) |
4860 | 38.0k | { |
4861 | 38.0k | cpl::down_cast<GDALPDFObjectPoppler *>(poPageObj)->SetRefNumAndGen( |
4862 | 38.0k | GDALPDFObjectNum(poPageRef->num), poPageRef->gen); |
4863 | 38.0k | } |
4864 | 38.0k | } |
4865 | 38.0k | #endif // ~ HAVE_POPPLER |
4866 | | |
4867 | | #ifdef HAVE_PODOFO |
4868 | | if (bUseLib.test(PDFLIB_PODOFO) && poPageObj == nullptr) |
4869 | | { |
4870 | | #if !(PODOFO_VERSION_MAJOR > 0 || \ |
4871 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)) |
4872 | | PoDoFo::PdfError::EnableDebug(false); |
4873 | | PoDoFo::PdfError::EnableLogging(false); |
4874 | | #endif |
4875 | | |
4876 | | poDocPodofo = std::make_unique<PoDoFo::PdfMemDocument>(); |
4877 | | try |
4878 | | { |
4879 | | poDocPodofo->Load(pszFilename); |
4880 | | } |
4881 | | catch (PoDoFo::PdfError &oError) |
4882 | | { |
4883 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4884 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4885 | | if (oError.GetCode() == PoDoFo::PdfErrorCode::InvalidPassword) |
4886 | | #else |
4887 | | if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword) |
4888 | | #endif |
4889 | | { |
4890 | | if (pszUserPwd) |
4891 | | { |
4892 | | pszUserPwd = |
4893 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4894 | | |
4895 | | try |
4896 | | { |
4897 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4898 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4899 | | poDocPodofo = |
4900 | | std::make_unique<PoDoFo::PdfMemDocument>(); |
4901 | | poDocPodofo->Load(pszFilename, pszUserPwd); |
4902 | | #else |
4903 | | poDocPodofo->SetPassword(pszUserPwd); |
4904 | | #endif |
4905 | | } |
4906 | | catch (PoDoFo::PdfError &oError2) |
4907 | | { |
4908 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4909 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4910 | | if (oError2.GetCode() == |
4911 | | PoDoFo::PdfErrorCode::InvalidPassword) |
4912 | | #else |
4913 | | if (oError2.GetError() == |
4914 | | PoDoFo::ePdfError_InvalidPassword) |
4915 | | #endif |
4916 | | { |
4917 | | CPLError(CE_Failure, CPLE_AppDefined, |
4918 | | "Invalid password"); |
4919 | | } |
4920 | | else |
4921 | | { |
4922 | | CPLError(CE_Failure, CPLE_AppDefined, |
4923 | | "Invalid PDF : %s", oError2.what()); |
4924 | | } |
4925 | | return nullptr; |
4926 | | } |
4927 | | catch (...) |
4928 | | { |
4929 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4930 | | return nullptr; |
4931 | | } |
4932 | | } |
4933 | | else |
4934 | | { |
4935 | | CPLError(CE_Failure, CPLE_AppDefined, |
4936 | | "A password is needed. You can specify it through " |
4937 | | "the PDF_USER_PWD " |
4938 | | "configuration option / USER_PWD open option " |
4939 | | "(that can be set to ASK_INTERACTIVE)"); |
4940 | | return nullptr; |
4941 | | } |
4942 | | } |
4943 | | else |
4944 | | { |
4945 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4946 | | oError.what()); |
4947 | | return nullptr; |
4948 | | } |
4949 | | } |
4950 | | catch (...) |
4951 | | { |
4952 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4953 | | return nullptr; |
4954 | | } |
4955 | | |
4956 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4957 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4958 | | auto &oPageCollections = poDocPodofo->GetPages(); |
4959 | | nPages = static_cast<int>(oPageCollections.GetCount()); |
4960 | | #else |
4961 | | nPages = poDocPodofo->GetPageCount(); |
4962 | | #endif |
4963 | | if (iPage < 1 || iPage > nPages) |
4964 | | { |
4965 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4966 | | iPage, nPages); |
4967 | | return nullptr; |
4968 | | } |
4969 | | |
4970 | | try |
4971 | | { |
4972 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4973 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4974 | | /* Sanity check to validate page count */ |
4975 | | if (iPage != nPages) |
4976 | | CPL_IGNORE_RET_VAL(oPageCollections.GetPageAt(nPages - 1)); |
4977 | | |
4978 | | poPagePodofo = &oPageCollections.GetPageAt(iPage - 1); |
4979 | | #else |
4980 | | /* Sanity check to validate page count */ |
4981 | | if (iPage != nPages) |
4982 | | CPL_IGNORE_RET_VAL(poDocPodofo->GetPage(nPages - 1)); |
4983 | | |
4984 | | poPagePodofo = poDocPodofo->GetPage(iPage - 1); |
4985 | | #endif |
4986 | | } |
4987 | | catch (PoDoFo::PdfError &oError) |
4988 | | { |
4989 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4990 | | oError.what()); |
4991 | | return nullptr; |
4992 | | } |
4993 | | catch (...) |
4994 | | { |
4995 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4996 | | return nullptr; |
4997 | | } |
4998 | | |
4999 | | if (poPagePodofo == nullptr) |
5000 | | { |
5001 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
5002 | | return nullptr; |
5003 | | } |
5004 | | |
5005 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5006 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5007 | | const PoDoFo::PdfObject *pObj = &poPagePodofo->GetObject(); |
5008 | | #else |
5009 | | const PoDoFo::PdfObject *pObj = poPagePodofo->GetObject(); |
5010 | | #endif |
5011 | | poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects()); |
5012 | | } |
5013 | | #endif // ~ HAVE_PODOFO |
5014 | | |
5015 | | #ifdef HAVE_PDFIUM |
5016 | | if (bUseLib.test(PDFLIB_PDFIUM) && poPageObj == nullptr) |
5017 | | { |
5018 | | if (!LoadPdfiumDocumentPage(pszFilename, pszUserPwd, iPage, |
5019 | | &poDocPdfium, &poPagePdfium, &nPages)) |
5020 | | { |
5021 | | // CPLError is called inside function |
5022 | | return nullptr; |
5023 | | } |
5024 | | |
5025 | | const auto pageObj = poPagePdfium->page->GetDict(); |
5026 | | if (pageObj == nullptr) |
5027 | | { |
5028 | | CPLError(CE_Failure, CPLE_AppDefined, |
5029 | | "Invalid PDF : invalid page object"); |
5030 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
5031 | | return nullptr; |
5032 | | } |
5033 | | poPageObj = GDALPDFObjectPdfium::Build(pageObj); |
5034 | | } |
5035 | | #endif // ~ HAVE_PDFIUM |
5036 | | |
5037 | 38.0k | if (poPageObj == nullptr) |
5038 | 0 | return nullptr; |
5039 | 38.0k | GDALPDFDictionary *poPageDict = poPageObj->GetDictionary(); |
5040 | 38.0k | if (poPageDict == nullptr) |
5041 | 0 | { |
5042 | 0 | delete poPageObj; |
5043 | |
|
5044 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5045 | 0 | "Invalid PDF : poPageDict == nullptr"); |
5046 | 0 | #ifdef HAVE_POPPLER |
5047 | 0 | if (bUseLib.test(PDFLIB_POPPLER)) |
5048 | 0 | PDFFreeDoc(poDocPoppler); |
5049 | 0 | #endif |
5050 | | #ifdef HAVE_PDFIUM |
5051 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5052 | | { |
5053 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
5054 | | } |
5055 | | #endif |
5056 | 0 | return nullptr; |
5057 | 0 | } |
5058 | | |
5059 | 38.0k | const char *pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", nullptr); |
5060 | 38.0k | if (pszDumpObject != nullptr) |
5061 | 0 | { |
5062 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpObject); |
5063 | 0 | oDumper.Dump(poPageObj); |
5064 | 0 | } |
5065 | | |
5066 | 38.0k | PDFDataset *poDS = new PDFDataset(); |
5067 | 38.0k | poDS->m_fp = std::move(fp); |
5068 | 38.0k | poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions); |
5069 | 38.0k | poDS->m_bUseLib = bUseLib; |
5070 | 38.0k | poDS->m_osFilename = pszFilename; |
5071 | 38.0k | poDS->eAccess = poOpenInfo->eAccess; |
5072 | | |
5073 | 38.0k | if (nPages > 1 && !bOpenSubdataset) |
5074 | 4.06k | { |
5075 | 4.06k | int i; |
5076 | 4.06k | CPLStringList aosList; |
5077 | 95.4k | for (i = 0; i < nPages; i++) |
5078 | 91.3k | { |
5079 | 91.3k | char szKey[32]; |
5080 | 91.3k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_NAME", i + 1); |
5081 | 91.3k | aosList.AddNameValue( |
5082 | 91.3k | szKey, CPLSPrintf("PDF:%d:%s", i + 1, poOpenInfo->pszFilename)); |
5083 | 91.3k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_DESC", i + 1); |
5084 | 91.3k | aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i + 1, |
5085 | 91.3k | poOpenInfo->pszFilename)); |
5086 | 91.3k | } |
5087 | 4.06k | poDS->SetMetadata(aosList.List(), GDAL_MDD_SUBDATASETS); |
5088 | 4.06k | } |
5089 | | |
5090 | 38.0k | #ifdef HAVE_POPPLER |
5091 | 38.0k | poDS->m_poDocPoppler = poDocPoppler; |
5092 | 38.0k | #endif |
5093 | | #ifdef HAVE_PODOFO |
5094 | | poDS->m_poDocPodofo = poDocPodofo.release(); |
5095 | | #endif |
5096 | | #ifdef HAVE_PDFIUM |
5097 | | poDS->m_poDocPdfium = poDocPdfium; |
5098 | | poDS->m_poPagePdfium = poPagePdfium; |
5099 | | #endif |
5100 | 38.0k | poDS->m_poPageObj = poPageObj; |
5101 | 38.0k | poDS->m_osUserPwd = pszUserPwd ? pszUserPwd : ""; |
5102 | 38.0k | poDS->m_iPage = iPage; |
5103 | | |
5104 | 38.0k | const char *pszDumpCatalog = |
5105 | 38.0k | CPLGetConfigOption("PDF_DUMP_CATALOG", nullptr); |
5106 | 38.0k | if (pszDumpCatalog != nullptr) |
5107 | 0 | { |
5108 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpCatalog); |
5109 | 0 | auto poCatalog = poDS->GetCatalog(); |
5110 | 0 | if (poCatalog) |
5111 | 0 | oDumper.Dump(poCatalog); |
5112 | 0 | } |
5113 | | |
5114 | 38.0k | int nBandsGuessed = 0; |
5115 | 38.0k | if (nImageNum < 0) |
5116 | 38.0k | { |
5117 | 38.0k | double dfDPI = std::numeric_limits<double>::quiet_NaN(); |
5118 | 38.0k | poDS->GuessDPIAndBandCount(poPageDict, dfDPI, nBandsGuessed); |
5119 | 38.0k | if (!std::isnan(dfDPI)) |
5120 | 6.09k | poDS->m_dfDPI = dfDPI; |
5121 | 38.0k | if (nBandsGuessed < 4) |
5122 | 38.0k | nBandsGuessed = 0; |
5123 | 38.0k | } |
5124 | | |
5125 | 38.0k | int nTargetBands = 3; |
5126 | | #ifdef HAVE_PDFIUM |
5127 | | // Use Alpha channel for PDFIUM as default format RGBA |
5128 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5129 | | nTargetBands = 4; |
5130 | | #endif |
5131 | 38.0k | if (nBandsGuessed) |
5132 | 8 | nTargetBands = nBandsGuessed; |
5133 | 38.0k | const char *pszPDFBands = |
5134 | 38.0k | GetOption(poOpenInfo->papszOpenOptions, "BANDS", nullptr); |
5135 | 38.0k | if (pszPDFBands) |
5136 | 20.2k | { |
5137 | 20.2k | nTargetBands = atoi(pszPDFBands); |
5138 | 20.2k | if (nTargetBands != 3 && nTargetBands != 4) |
5139 | 0 | { |
5140 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
5141 | 0 | "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback"); |
5142 | 0 | nTargetBands = 3; |
5143 | 0 | } |
5144 | 20.2k | } |
5145 | | #ifdef HAVE_PODOFO |
5146 | | if (bUseLib.test(PDFLIB_PODOFO) && nTargetBands == 4 && |
5147 | | poDS->m_aiTiles.empty()) |
5148 | | { |
5149 | | CPLError(CE_Warning, CPLE_NotSupported, |
5150 | | "GDAL_PDF_BANDS=4 not supported when PDF driver is compiled " |
5151 | | "against Podofo. " |
5152 | | "Using 3 as a fallback"); |
5153 | | nTargetBands = 3; |
5154 | | } |
5155 | | #endif |
5156 | | |
5157 | | // Create bands. We must do that before initializing PAM. But at that point |
5158 | | // we don't know yet the dataset dimension, since we need to know the DPI, |
5159 | | // that we can fully know only after loading PAM... So we will have to patch |
5160 | | // later the band dimension. |
5161 | 152k | for (int iBand = 1; iBand <= nTargetBands; iBand++) |
5162 | 114k | { |
5163 | 114k | if (poDS->m_poImageObj != nullptr) |
5164 | 0 | poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand)); |
5165 | 114k | else |
5166 | 114k | poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand, 0)); |
5167 | 114k | } |
5168 | | |
5169 | | /* -------------------------------------------------------------------- */ |
5170 | | /* Initialize any PAM information. */ |
5171 | | /* -------------------------------------------------------------------- */ |
5172 | 38.0k | if (bOpenSubdataset || bOpenSubdatasetImage) |
5173 | 0 | { |
5174 | 0 | poDS->SetPhysicalFilename(pszFilename); |
5175 | 0 | poDS->SetSubdatasetName(osSubdatasetName.c_str()); |
5176 | 0 | } |
5177 | 38.0k | else |
5178 | 38.0k | { |
5179 | 38.0k | poDS->SetDescription(poOpenInfo->pszFilename); |
5180 | 38.0k | } |
5181 | | |
5182 | 38.0k | poDS->TryLoadXML(); |
5183 | | |
5184 | | // Establish DPI |
5185 | 38.0k | const char *pszDPI = |
5186 | 38.0k | GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr); |
5187 | 38.0k | if (pszDPI == nullptr) |
5188 | 17.7k | pszDPI = poDS->GDALPamDataset::GetMetadataItem("DPI"); |
5189 | 38.0k | if (pszDPI != nullptr) |
5190 | 20.2k | { |
5191 | 20.2k | poDS->m_dfDPI = CPLAtof(pszDPI); |
5192 | | |
5193 | 20.2k | if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, |
5194 | 20.2k | "SAVE_DPI_TO_PAM", "FALSE"))) |
5195 | 0 | { |
5196 | 0 | const std::string osDPI(pszDPI); |
5197 | 0 | poDS->GDALPamDataset::SetMetadataItem("DPI", osDPI.c_str()); |
5198 | 0 | } |
5199 | 20.2k | } |
5200 | | |
5201 | 38.0k | if (poDS->m_dfDPI < 1e-2 || poDS->m_dfDPI > 7200) |
5202 | 59 | { |
5203 | 59 | CPLError(CE_Warning, CPLE_AppDefined, |
5204 | 59 | "Invalid value for GDAL_PDF_DPI. Using default value instead"); |
5205 | 59 | poDS->m_dfDPI = GDAL_DEFAULT_DPI; |
5206 | 59 | } |
5207 | 38.0k | poDS->SetMetadataItem("DPI", CPLSPrintf("%.16g", poDS->m_dfDPI)); |
5208 | | |
5209 | 38.0k | double dfX1 = 0.0; |
5210 | 38.0k | double dfY1 = 0.0; |
5211 | 38.0k | double dfX2 = 0.0; |
5212 | 38.0k | double dfY2 = 0.0; |
5213 | | |
5214 | 38.0k | #ifdef HAVE_POPPLER |
5215 | 38.0k | if (bUseLib.test(PDFLIB_POPPLER)) |
5216 | 38.0k | { |
5217 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
5218 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION > 5) || \ |
5219 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION == 5 && \ |
5220 | | POPPLER_MICRO_VERSION > 0) |
5221 | | const auto *psMediaBox = &(poPagePoppler->getMediaBox()); |
5222 | | #else |
5223 | 38.0k | const auto *psMediaBox = poPagePoppler->getMediaBox(); |
5224 | 38.0k | #endif |
5225 | 38.0k | dfX1 = psMediaBox->x1; |
5226 | 38.0k | dfY1 = psMediaBox->y1; |
5227 | 38.0k | dfX2 = psMediaBox->x2; |
5228 | 38.0k | dfY2 = psMediaBox->y2; |
5229 | 38.0k | } |
5230 | 38.0k | #endif |
5231 | | |
5232 | | #ifdef HAVE_PODOFO |
5233 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5234 | | { |
5235 | | CPLAssert(poPagePodofo); |
5236 | | auto oMediaBox = poPagePodofo->GetMediaBox(); |
5237 | | dfX1 = oMediaBox.GetLeft(); |
5238 | | dfY1 = oMediaBox.GetBottom(); |
5239 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5240 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5241 | | dfX2 = dfX1 + oMediaBox.Width; |
5242 | | dfY2 = dfY1 + oMediaBox.Height; |
5243 | | #else |
5244 | | dfX2 = dfX1 + oMediaBox.GetWidth(); |
5245 | | dfY2 = dfY1 + oMediaBox.GetHeight(); |
5246 | | #endif |
5247 | | } |
5248 | | #endif |
5249 | | |
5250 | | #ifdef HAVE_PDFIUM |
5251 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5252 | | { |
5253 | | CPLAssert(poPagePdfium); |
5254 | | CFX_FloatRect rect = poPagePdfium->page->GetBBox(); |
5255 | | dfX1 = rect.left; |
5256 | | dfX2 = rect.right; |
5257 | | dfY1 = rect.bottom; |
5258 | | dfY2 = rect.top; |
5259 | | } |
5260 | | #endif // ~ HAVE_PDFIUM |
5261 | | |
5262 | 38.0k | double dfUserUnit = poDS->m_dfDPI * USER_UNIT_IN_INCH; |
5263 | 38.0k | poDS->m_dfPageWidth = dfX2 - dfX1; |
5264 | 38.0k | poDS->m_dfPageHeight = dfY2 - dfY1; |
5265 | | // CPLDebug("PDF", "left=%f right=%f bottom=%f top=%f", dfX1, dfX2, dfY1, |
5266 | | // dfY2); |
5267 | 38.0k | const double dfXSize = floor((dfX2 - dfX1) * dfUserUnit + 0.5); |
5268 | 38.0k | const double dfYSize = floor((dfY2 - dfY1) * dfUserUnit + 0.5); |
5269 | 38.0k | if (!(dfXSize >= 0 && dfXSize <= INT_MAX && dfYSize >= 0 && |
5270 | 38.0k | dfYSize <= INT_MAX)) |
5271 | 12 | { |
5272 | 12 | delete poDS; |
5273 | 12 | return nullptr; |
5274 | 12 | } |
5275 | 38.0k | poDS->nRasterXSize = static_cast<int>(dfXSize); |
5276 | 38.0k | poDS->nRasterYSize = static_cast<int>(dfYSize); |
5277 | | |
5278 | 38.0k | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) |
5279 | 23 | { |
5280 | 23 | delete poDS; |
5281 | 23 | return nullptr; |
5282 | 23 | } |
5283 | | |
5284 | 37.9k | double dfRotation = 0; |
5285 | 37.9k | #ifdef HAVE_POPPLER |
5286 | 37.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
5287 | 37.9k | dfRotation = poDocPoppler->getPageRotate(iPage); |
5288 | 37.9k | #endif |
5289 | | |
5290 | | #ifdef HAVE_PODOFO |
5291 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5292 | | { |
5293 | | CPLAssert(poPagePodofo); |
5294 | | #if PODOFO_VERSION_MAJOR >= 1 |
5295 | | poPagePodofo->TryGetRotationRaw(dfRotation); |
5296 | | #elif (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5297 | | dfRotation = poPagePodofo->GetRotationRaw(); |
5298 | | #else |
5299 | | dfRotation = poPagePodofo->GetRotation(); |
5300 | | #endif |
5301 | | } |
5302 | | #endif |
5303 | | |
5304 | | #ifdef HAVE_PDFIUM |
5305 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5306 | | { |
5307 | | CPLAssert(poPagePdfium); |
5308 | | dfRotation = poPagePdfium->page->GetPageRotation() * 90; |
5309 | | } |
5310 | | #endif |
5311 | | |
5312 | 37.9k | if (dfRotation == 90 || dfRotation == -90 || dfRotation == 270) |
5313 | 189 | { |
5314 | | /* FIXME: the podofo case should be implemented. This needs to rotate */ |
5315 | | /* the output of pdftoppm */ |
5316 | 189 | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
5317 | 189 | if (bUseLib.test(PDFLIB_POPPLER) || bUseLib.test(PDFLIB_PDFIUM)) |
5318 | 189 | { |
5319 | 189 | int nTmp = poDS->nRasterXSize; |
5320 | 189 | poDS->nRasterXSize = poDS->nRasterYSize; |
5321 | 189 | poDS->nRasterYSize = nTmp; |
5322 | 189 | } |
5323 | 189 | #endif |
5324 | 189 | } |
5325 | | |
5326 | 37.9k | if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5327 | 20.2k | { |
5328 | 20.2k | poDS->m_nBlockXSize = 512; |
5329 | 20.2k | poDS->m_nBlockYSize = 512; |
5330 | 20.2k | } |
5331 | | /* Check if the PDF is only made of regularly tiled images */ |
5332 | | /* (like some USGS GeoPDF production) */ |
5333 | 17.7k | else if (dfRotation == 0.0 && !poDS->m_asTiles.empty() && |
5334 | 226 | EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"), |
5335 | 17.7k | "ALL")) |
5336 | 226 | { |
5337 | 226 | poDS->CheckTiledRaster(); |
5338 | 226 | if (!poDS->m_aiTiles.empty()) |
5339 | 0 | poDS->SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", |
5340 | 0 | GDAL_MDD_IMAGE_STRUCTURE); |
5341 | 226 | } |
5342 | | |
5343 | 37.9k | GDALPDFObject *poLGIDict = nullptr; |
5344 | 37.9k | GDALPDFObject *poVP = nullptr; |
5345 | 37.9k | int bIsOGCBP = FALSE; |
5346 | 37.9k | if ((poLGIDict = poPageDict->Get("LGIDict")) != nullptr && nImageNum < 0) |
5347 | 650 | { |
5348 | | /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */ |
5349 | 650 | CPLDebug("PDF", "OGC Encoding Best Practice style detected"); |
5350 | 650 | if (poDS->ParseLGIDictObject(poLGIDict)) |
5351 | 211 | { |
5352 | 211 | if (poDS->m_bHasCTM) |
5353 | 185 | { |
5354 | 185 | if (dfRotation == 90) |
5355 | 0 | { |
5356 | 0 | poDS->m_gt.xorig = poDS->m_adfCTM[4]; |
5357 | 0 | poDS->m_gt.xscale = poDS->m_adfCTM[2] / dfUserUnit; |
5358 | 0 | poDS->m_gt.xrot = poDS->m_adfCTM[0] / dfUserUnit; |
5359 | 0 | poDS->m_gt.yorig = poDS->m_adfCTM[5]; |
5360 | 0 | poDS->m_gt.yrot = poDS->m_adfCTM[3] / dfUserUnit; |
5361 | 0 | poDS->m_gt.yscale = poDS->m_adfCTM[1] / dfUserUnit; |
5362 | 0 | } |
5363 | 185 | else if (dfRotation == -90 || dfRotation == 270) |
5364 | 0 | { |
5365 | 0 | poDS->m_gt.xorig = |
5366 | 0 | poDS->m_adfCTM[4] + |
5367 | 0 | poDS->m_adfCTM[2] * poDS->m_dfPageHeight + |
5368 | 0 | poDS->m_adfCTM[0] * poDS->m_dfPageWidth; |
5369 | 0 | poDS->m_gt.xscale = -poDS->m_adfCTM[2] / dfUserUnit; |
5370 | 0 | poDS->m_gt.xrot = -poDS->m_adfCTM[0] / dfUserUnit; |
5371 | 0 | poDS->m_gt.yorig = |
5372 | 0 | poDS->m_adfCTM[5] + |
5373 | 0 | poDS->m_adfCTM[3] * poDS->m_dfPageHeight + |
5374 | 0 | poDS->m_adfCTM[1] * poDS->m_dfPageWidth; |
5375 | 0 | poDS->m_gt.yrot = -poDS->m_adfCTM[3] / dfUserUnit; |
5376 | 0 | poDS->m_gt.yscale = -poDS->m_adfCTM[1] / dfUserUnit; |
5377 | 0 | } |
5378 | 185 | else |
5379 | 185 | { |
5380 | 185 | poDS->m_gt.xorig = poDS->m_adfCTM[4] + |
5381 | 185 | poDS->m_adfCTM[2] * dfY2 + |
5382 | 185 | poDS->m_adfCTM[0] * dfX1; |
5383 | 185 | poDS->m_gt.xscale = poDS->m_adfCTM[0] / dfUserUnit; |
5384 | 185 | poDS->m_gt.xrot = -poDS->m_adfCTM[2] / dfUserUnit; |
5385 | 185 | poDS->m_gt.yorig = poDS->m_adfCTM[5] + |
5386 | 185 | poDS->m_adfCTM[3] * dfY2 + |
5387 | 185 | poDS->m_adfCTM[1] * dfX1; |
5388 | 185 | poDS->m_gt.yrot = poDS->m_adfCTM[1] / dfUserUnit; |
5389 | 185 | poDS->m_gt.yscale = -poDS->m_adfCTM[3] / dfUserUnit; |
5390 | 185 | } |
5391 | | |
5392 | 185 | poDS->m_bGeoTransformValid = true; |
5393 | 185 | } |
5394 | | |
5395 | 211 | bIsOGCBP = TRUE; |
5396 | | |
5397 | 211 | int i; |
5398 | 313 | for (i = 0; i < poDS->m_nGCPCount; i++) |
5399 | 102 | { |
5400 | 102 | if (dfRotation == 90) |
5401 | 0 | { |
5402 | 0 | double dfPixel = |
5403 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5404 | 0 | double dfLine = |
5405 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5406 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = dfLine; |
5407 | 0 | poDS->m_pasGCPList[i].dfGCPLine = dfPixel; |
5408 | 0 | } |
5409 | 102 | else if (dfRotation == -90 || dfRotation == 270) |
5410 | 0 | { |
5411 | 0 | double dfPixel = |
5412 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5413 | 0 | double dfLine = |
5414 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5415 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = |
5416 | 0 | poDS->nRasterXSize - dfLine; |
5417 | 0 | poDS->m_pasGCPList[i].dfGCPLine = |
5418 | 0 | poDS->nRasterYSize - dfPixel; |
5419 | 0 | } |
5420 | 102 | else |
5421 | 102 | { |
5422 | 102 | poDS->m_pasGCPList[i].dfGCPPixel = |
5423 | 102 | (-dfX1 + poDS->m_pasGCPList[i].dfGCPPixel) * dfUserUnit; |
5424 | 102 | poDS->m_pasGCPList[i].dfGCPLine = |
5425 | 102 | (dfY2 - poDS->m_pasGCPList[i].dfGCPLine) * dfUserUnit; |
5426 | 102 | } |
5427 | 102 | } |
5428 | 211 | } |
5429 | 650 | } |
5430 | 37.3k | else if ((poVP = poPageDict->Get("VP")) != nullptr && nImageNum < 0) |
5431 | 8.38k | { |
5432 | | /* Cf adobe_supplement_iso32000.pdf */ |
5433 | 8.38k | CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?"); |
5434 | 8.38k | if (dfX1 != 0 || dfY1 != 0) |
5435 | 195 | { |
5436 | 195 | CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case..."); |
5437 | 195 | } |
5438 | 8.38k | poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1); |
5439 | 8.38k | } |
5440 | 28.9k | else |
5441 | 28.9k | { |
5442 | 28.9k | GDALPDFObject *poXObject = |
5443 | 28.9k | poPageDict->LookupObject("Resources.XObject"); |
5444 | | |
5445 | 28.9k | if (poXObject != nullptr && |
5446 | 8.82k | poXObject->GetType() == PDFObjectType_Dictionary) |
5447 | 8.77k | { |
5448 | 8.77k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
5449 | 8.77k | const auto &oMap = poXObjectDict->GetValues(); |
5450 | 8.77k | int nSubDataset = 0; |
5451 | 8.77k | for (const auto &[osKey, poObj] : oMap) |
5452 | 57.2k | { |
5453 | 57.2k | if (poObj->GetType() == PDFObjectType_Dictionary) |
5454 | 53.6k | { |
5455 | 53.6k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
5456 | 53.6k | GDALPDFObject *poSubtype = nullptr; |
5457 | 53.6k | GDALPDFObject *poMeasure = nullptr; |
5458 | 53.6k | GDALPDFObject *poWidth = nullptr; |
5459 | 53.6k | GDALPDFObject *poHeight = nullptr; |
5460 | 53.6k | int nW = 0; |
5461 | 53.6k | int nH = 0; |
5462 | 53.6k | if ((poSubtype = poDict->Get("Subtype")) != nullptr && |
5463 | 51.8k | poSubtype->GetType() == PDFObjectType_Name && |
5464 | 51.8k | poSubtype->GetName() == "Image" && |
5465 | 12.2k | (poMeasure = poDict->Get("Measure")) != nullptr && |
5466 | 1 | poMeasure->GetType() == PDFObjectType_Dictionary && |
5467 | 0 | (poWidth = poDict->Get("Width")) != nullptr && |
5468 | 0 | poWidth->GetType() == PDFObjectType_Int && |
5469 | 0 | (nW = poWidth->GetInt()) > 0 && |
5470 | 0 | (poHeight = poDict->Get("Height")) != nullptr && |
5471 | 0 | poHeight->GetType() == PDFObjectType_Int && |
5472 | 0 | (nH = poHeight->GetInt()) > 0) |
5473 | 0 | { |
5474 | 0 | if (nImageNum < 0) |
5475 | 0 | CPLDebug("PDF", |
5476 | 0 | "Measure found on Image object (%d)", |
5477 | 0 | poObj->GetRefNum().toInt()); |
5478 | |
|
5479 | 0 | GDALPDFObject *poColorSpace = poDict->Get("ColorSpace"); |
5480 | 0 | GDALPDFObject *poBitsPerComponent = |
5481 | 0 | poDict->Get("BitsPerComponent"); |
5482 | 0 | if (poObj->GetRefNum().toBool() && |
5483 | 0 | poObj->GetRefGen() == 0 && |
5484 | 0 | poColorSpace != nullptr && |
5485 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
5486 | 0 | (poColorSpace->GetName() == "DeviceGray" || |
5487 | 0 | poColorSpace->GetName() == "DeviceRGB") && |
5488 | 0 | (poBitsPerComponent == nullptr || |
5489 | 0 | (poBitsPerComponent->GetType() == |
5490 | 0 | PDFObjectType_Int && |
5491 | 0 | poBitsPerComponent->GetInt() == 8))) |
5492 | 0 | { |
5493 | 0 | if (nImageNum < 0) |
5494 | 0 | { |
5495 | 0 | nSubDataset++; |
5496 | 0 | poDS->SetMetadataItem( |
5497 | 0 | CPLSPrintf("SUBDATASET_%d_NAME", |
5498 | 0 | nSubDataset), |
5499 | 0 | CPLSPrintf("PDF_IMAGE:%d:%d:%s", iPage, |
5500 | 0 | poObj->GetRefNum().toInt(), |
5501 | 0 | pszFilename), |
5502 | 0 | GDAL_MDD_SUBDATASETS); |
5503 | 0 | poDS->SetMetadataItem( |
5504 | 0 | CPLSPrintf("SUBDATASET_%d_DESC", |
5505 | 0 | nSubDataset), |
5506 | 0 | CPLSPrintf("Georeferenced image of size " |
5507 | 0 | "%dx%d of page %d of %s", |
5508 | 0 | nW, nH, iPage, pszFilename), |
5509 | 0 | GDAL_MDD_SUBDATASETS); |
5510 | 0 | } |
5511 | 0 | else if (poObj->GetRefNum().toInt() == nImageNum) |
5512 | 0 | { |
5513 | 0 | poDS->nRasterXSize = nW; |
5514 | 0 | poDS->nRasterYSize = nH; |
5515 | 0 | poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW, |
5516 | 0 | 0); |
5517 | 0 | poDS->m_poImageObj = poObj; |
5518 | 0 | if (poColorSpace->GetName() == "DeviceGray") |
5519 | 0 | { |
5520 | 0 | for (int i = 1; i < poDS->nBands; ++i) |
5521 | 0 | delete poDS->papoBands[i]; |
5522 | 0 | poDS->nBands = 1; |
5523 | 0 | } |
5524 | 0 | break; |
5525 | 0 | } |
5526 | 0 | } |
5527 | 0 | } |
5528 | 53.6k | } |
5529 | 57.2k | } |
5530 | 8.77k | } |
5531 | | |
5532 | 28.9k | if (nImageNum >= 0 && poDS->m_poImageObj == nullptr) |
5533 | 0 | { |
5534 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d", |
5535 | 0 | nImageNum); |
5536 | 0 | delete poDS; |
5537 | 0 | return nullptr; |
5538 | 0 | } |
5539 | | |
5540 | | /* Not a geospatial PDF doc */ |
5541 | 28.9k | } |
5542 | | |
5543 | | /* If pixel size or top left coordinates are very close to an int, round |
5544 | | * them to the int */ |
5545 | 37.9k | double dfEps = |
5546 | 37.9k | (fabs(poDS->m_gt.xorig) > 1e5 && fabs(poDS->m_gt.yorig) > 1e5) ? 1e-5 |
5547 | 37.9k | : 1e-8; |
5548 | 37.9k | poDS->m_gt.xorig = ROUND_IF_CLOSE(poDS->m_gt.xorig, dfEps); |
5549 | 37.9k | poDS->m_gt.xscale = ROUND_IF_CLOSE(poDS->m_gt.xscale); |
5550 | 37.9k | poDS->m_gt.yorig = ROUND_IF_CLOSE(poDS->m_gt.yorig, dfEps); |
5551 | 37.9k | poDS->m_gt.yscale = ROUND_IF_CLOSE(poDS->m_gt.yscale); |
5552 | | |
5553 | 37.9k | if (bUseLib.test(PDFLIB_PDFIUM)) |
5554 | 0 | { |
5555 | | // Attempt to "fix" the loss of precision due to the use of float32 for |
5556 | | // numbers by pdfium |
5557 | 0 | if ((fabs(poDS->m_gt.xorig) > 1e5 || fabs(poDS->m_gt.yorig) > 1e5) && |
5558 | 0 | fabs(poDS->m_gt.xorig - std::round(poDS->m_gt.xorig)) < |
5559 | 0 | 1e-6 * fabs(poDS->m_gt.xorig) && |
5560 | 0 | fabs(poDS->m_gt.xscale - std::round(poDS->m_gt.xscale)) < |
5561 | 0 | 1e-3 * fabs(poDS->m_gt.xscale) && |
5562 | 0 | fabs(poDS->m_gt.yorig - std::round(poDS->m_gt.yorig)) < |
5563 | 0 | 1e-6 * fabs(poDS->m_gt.yorig) && |
5564 | 0 | fabs(poDS->m_gt.yscale - std::round(poDS->m_gt.yscale)) < |
5565 | 0 | 1e-3 * fabs(poDS->m_gt.yscale)) |
5566 | 0 | { |
5567 | 0 | for (int i = 0; i < 6; i++) |
5568 | 0 | { |
5569 | 0 | poDS->m_gt[i] = std::round(poDS->m_gt[i]); |
5570 | 0 | } |
5571 | 0 | } |
5572 | 0 | } |
5573 | | |
5574 | 37.9k | if (poDS->m_poNeatLine) |
5575 | 3.98k | { |
5576 | 3.98k | char *pszNeatLineWkt = nullptr; |
5577 | 3.98k | OGRLinearRing *poRing = poDS->m_poNeatLine->getExteriorRing(); |
5578 | | /* Adobe style is already in target SRS units */ |
5579 | 3.98k | if (bIsOGCBP) |
5580 | 210 | { |
5581 | 210 | int nPoints = poRing->getNumPoints(); |
5582 | 210 | int i; |
5583 | | |
5584 | 65.0k | for (i = 0; i < nPoints; i++) |
5585 | 64.8k | { |
5586 | 64.8k | double x, y; |
5587 | 64.8k | if (dfRotation == 90.0) |
5588 | 0 | { |
5589 | 0 | x = poRing->getY(i) * dfUserUnit; |
5590 | 0 | y = poRing->getX(i) * dfUserUnit; |
5591 | 0 | } |
5592 | 64.8k | else if (dfRotation == -90.0 || dfRotation == 270.0) |
5593 | 0 | { |
5594 | 0 | x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit; |
5595 | 0 | y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit; |
5596 | 0 | } |
5597 | 64.8k | else |
5598 | 64.8k | { |
5599 | 64.8k | x = (-dfX1 + poRing->getX(i)) * dfUserUnit; |
5600 | 64.8k | y = (dfY2 - poRing->getY(i)) * dfUserUnit; |
5601 | 64.8k | } |
5602 | 64.8k | double X = poDS->m_gt.xorig + x * poDS->m_gt.xscale + |
5603 | 64.8k | y * poDS->m_gt.xrot; |
5604 | 64.8k | double Y = poDS->m_gt.yorig + x * poDS->m_gt.yrot + |
5605 | 64.8k | y * poDS->m_gt.yscale; |
5606 | 64.8k | poRing->setPoint(i, X, Y); |
5607 | 64.8k | } |
5608 | 210 | } |
5609 | 3.98k | poRing->closeRings(); |
5610 | | |
5611 | 3.98k | poDS->m_poNeatLine->exportToWkt(&pszNeatLineWkt); |
5612 | 3.98k | if (nImageNum < 0) |
5613 | 3.98k | poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt); |
5614 | 3.98k | CPLFree(pszNeatLineWkt); |
5615 | 3.98k | } |
5616 | | |
5617 | 37.9k | poDS->MapOCGsToPages(); |
5618 | | |
5619 | 37.9k | #ifdef HAVE_POPPLER |
5620 | 37.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
5621 | 37.9k | { |
5622 | 37.9k | auto poMetadata = poCatalogPoppler->readMetadata(); |
5623 | 37.9k | if (poMetadata) |
5624 | 6.45k | { |
5625 | 6.45k | const char *pszContent = poMetadata->c_str(); |
5626 | 6.45k | if (pszContent != nullptr && |
5627 | 6.45k | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5628 | 5.81k | { |
5629 | 5.81k | const char *const apszMDList[2] = {pszContent, nullptr}; |
5630 | 5.81k | poDS->SetMetadata(const_cast<char **>(apszMDList), "xml:XMP"); |
5631 | 5.81k | } |
5632 | | #if (POPPLER_MAJOR_VERSION < 21 || \ |
5633 | | (POPPLER_MAJOR_VERSION == 21 && POPPLER_MINOR_VERSION < 10)) |
5634 | | delete poMetadata; |
5635 | | #endif |
5636 | 6.45k | } |
5637 | | |
5638 | | /* Read Info object */ |
5639 | | /* The test is necessary since with some corrupted PDFs |
5640 | | * poDocPoppler->getDocInfo() */ |
5641 | | /* might abort() */ |
5642 | 37.9k | if (poDocPoppler->getXRef()->isOk()) |
5643 | 37.9k | { |
5644 | 37.9k | Object oInfo = poDocPoppler->getDocInfo(); |
5645 | 37.9k | GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE); |
5646 | 37.9k | poDS->ParseInfo(&oInfoObjPoppler); |
5647 | 37.9k | } |
5648 | | |
5649 | | /* Find layers */ |
5650 | 37.9k | poDS->FindLayersPoppler( |
5651 | 37.9k | (bOpenSubdataset || bOpenSubdatasetImage) ? iPage : 0); |
5652 | | |
5653 | | /* Turn user specified layers on or off */ |
5654 | 37.9k | poDS->TurnLayersOnOffPoppler(); |
5655 | 37.9k | } |
5656 | 37.9k | #endif |
5657 | | |
5658 | | #ifdef HAVE_PODOFO |
5659 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5660 | | { |
5661 | | for (const auto &obj : poDS->m_poDocPodofo->GetObjects()) |
5662 | | { |
5663 | | GDALPDFObjectPodofo oObjPodofo(obj, |
5664 | | poDS->m_poDocPodofo->GetObjects()); |
5665 | | poDS->FindXMP(&oObjPodofo); |
5666 | | } |
5667 | | |
5668 | | /* Find layers */ |
5669 | | poDS->FindLayersGeneric(poPageDict); |
5670 | | |
5671 | | /* Read Info object */ |
5672 | | const PoDoFo::PdfInfo *poInfo = poDS->m_poDocPodofo->GetInfo(); |
5673 | | if (poInfo != nullptr) |
5674 | | { |
5675 | | GDALPDFObjectPodofo oInfoObjPodofo( |
5676 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5677 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5678 | | &(poInfo->GetObject()), |
5679 | | #else |
5680 | | poInfo->GetObject(), |
5681 | | #endif |
5682 | | poDS->m_poDocPodofo->GetObjects()); |
5683 | | poDS->ParseInfo(&oInfoObjPodofo); |
5684 | | } |
5685 | | } |
5686 | | #endif |
5687 | | #ifdef HAVE_PDFIUM |
5688 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5689 | | { |
5690 | | // coverity is confused by WrapRetain(), believing that multiple |
5691 | | // smart pointers manage the same raw pointer. Which is actually |
5692 | | // true, but a RetainPtr holds a reference counted object. It is |
5693 | | // thus safe to have several RetainPtr holding it. |
5694 | | // coverity[multiple_init_smart_ptr] |
5695 | | GDALPDFObjectPdfium *poRoot = GDALPDFObjectPdfium::Build( |
5696 | | pdfium::WrapRetain(poDocPdfium->doc->GetRoot())); |
5697 | | if (poRoot->GetType() == PDFObjectType_Dictionary) |
5698 | | { |
5699 | | GDALPDFDictionary *poDict = poRoot->GetDictionary(); |
5700 | | GDALPDFObject *poMetadata(poDict->Get("Metadata")); |
5701 | | if (poMetadata != nullptr) |
5702 | | { |
5703 | | GDALPDFStream *poStream = poMetadata->GetStream(); |
5704 | | if (poStream != nullptr) |
5705 | | { |
5706 | | char *pszContent = poStream->GetBytes(); |
5707 | | const auto nLength = poStream->GetLength(); |
5708 | | if (pszContent != nullptr && nLength > 15 && |
5709 | | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5710 | | { |
5711 | | char *apszMDList[2]; |
5712 | | apszMDList[0] = pszContent; |
5713 | | apszMDList[1] = nullptr; |
5714 | | poDS->SetMetadata(apszMDList, "xml:XMP"); |
5715 | | } |
5716 | | CPLFree(pszContent); |
5717 | | } |
5718 | | } |
5719 | | } |
5720 | | delete poRoot; |
5721 | | |
5722 | | /* Find layers */ |
5723 | | poDS->FindLayersPdfium((bOpenSubdataset || bOpenSubdatasetImage) ? iPage |
5724 | | : 0); |
5725 | | |
5726 | | /* Turn user specified layers on or off */ |
5727 | | poDS->TurnLayersOnOffPdfium(); |
5728 | | |
5729 | | GDALPDFObjectPdfium *poInfo = |
5730 | | GDALPDFObjectPdfium::Build(poDocPdfium->doc->GetInfo()); |
5731 | | if (poInfo) |
5732 | | { |
5733 | | /* Read Info object */ |
5734 | | poDS->ParseInfo(poInfo); |
5735 | | delete poInfo; |
5736 | | } |
5737 | | } |
5738 | | #endif // ~ HAVE_PDFIUM |
5739 | | |
5740 | | // Patch band size with actual dataset size |
5741 | 151k | for (int iBand = 1; iBand <= poDS->nBands; iBand++) |
5742 | 113k | { |
5743 | 113k | cpl::down_cast<PDFRasterBand *>(poDS->GetRasterBand(iBand)) |
5744 | 113k | ->SetSize(poDS->nRasterXSize, poDS->nRasterYSize); |
5745 | 113k | } |
5746 | | |
5747 | | /* Check if this is a raster-only PDF file and that we are */ |
5748 | | /* opened in vector-only mode */ |
5749 | 37.9k | if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && |
5750 | 28.8k | (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && |
5751 | 8.58k | !poDS->OpenVectorLayers(poPageDict)) |
5752 | 6.99k | { |
5753 | 6.99k | CPLDebug("PDF", "This is a raster-only PDF dataset, " |
5754 | 6.99k | "but it has been opened in vector-only mode"); |
5755 | | /* Clear dirty flag */ |
5756 | 6.99k | poDS->m_bProjDirty = false; |
5757 | 6.99k | poDS->m_bNeatLineDirty = false; |
5758 | 6.99k | poDS->m_bInfoDirty = false; |
5759 | 6.99k | poDS->m_bXMPDirty = false; |
5760 | 6.99k | delete poDS; |
5761 | 6.99k | return nullptr; |
5762 | 6.99k | } |
5763 | | |
5764 | | /* -------------------------------------------------------------------- */ |
5765 | | /* Support overviews. */ |
5766 | | /* -------------------------------------------------------------------- */ |
5767 | 30.9k | if (!CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5768 | 10.7k | { |
5769 | 10.7k | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
5770 | 10.7k | } |
5771 | | |
5772 | | /* Clear dirty flag */ |
5773 | 30.9k | poDS->m_bProjDirty = false; |
5774 | 30.9k | poDS->m_bNeatLineDirty = false; |
5775 | 30.9k | poDS->m_bInfoDirty = false; |
5776 | 30.9k | poDS->m_bXMPDirty = false; |
5777 | | |
5778 | 30.9k | return (poDS); |
5779 | 37.9k | } |
5780 | | |
5781 | | /************************************************************************/ |
5782 | | /* ParseLGIDictObject() */ |
5783 | | /************************************************************************/ |
5784 | | |
5785 | | int PDFDataset::ParseLGIDictObject(GDALPDFObject *poLGIDict) |
5786 | 650 | { |
5787 | 650 | bool bOK = false; |
5788 | 650 | if (poLGIDict->GetType() == PDFObjectType_Array) |
5789 | 67 | { |
5790 | 67 | GDALPDFArray *poArray = poLGIDict->GetArray(); |
5791 | 67 | int nArrayLength = poArray->GetLength(); |
5792 | 67 | int iMax = -1; |
5793 | 67 | GDALPDFObject *poArrayElt = nullptr; |
5794 | 67 | for (int i = 0; i < nArrayLength; i++) |
5795 | 34 | { |
5796 | 34 | if ((poArrayElt = poArray->Get(i)) == nullptr || |
5797 | 33 | poArrayElt->GetType() != PDFObjectType_Dictionary) |
5798 | 34 | { |
5799 | 34 | CPLError(CE_Failure, CPLE_AppDefined, |
5800 | 34 | "LGIDict[%d] is not a dictionary", i); |
5801 | 34 | return FALSE; |
5802 | 34 | } |
5803 | | |
5804 | 0 | int bIsBestCandidate = FALSE; |
5805 | 0 | if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), |
5806 | 0 | &bIsBestCandidate)) |
5807 | 0 | { |
5808 | 0 | if (bIsBestCandidate || iMax < 0) |
5809 | 0 | iMax = i; |
5810 | 0 | } |
5811 | 0 | } |
5812 | | |
5813 | 33 | if (iMax < 0) |
5814 | 33 | return FALSE; |
5815 | | |
5816 | 0 | poArrayElt = poArray->Get(iMax); |
5817 | 0 | bOK = CPL_TO_BOOL( |
5818 | 0 | ParseLGIDictDictSecondPass(poArrayElt->GetDictionary())); |
5819 | 0 | } |
5820 | 583 | else if (poLGIDict->GetType() == PDFObjectType_Dictionary) |
5821 | 545 | { |
5822 | 545 | bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) && |
5823 | 374 | ParseLGIDictDictSecondPass(poLGIDict->GetDictionary()); |
5824 | 545 | } |
5825 | 38 | else |
5826 | 38 | { |
5827 | 38 | CPLError(CE_Failure, CPLE_AppDefined, "LGIDict is of type %s", |
5828 | 38 | poLGIDict->GetTypeName()); |
5829 | 38 | } |
5830 | | |
5831 | 583 | return bOK; |
5832 | 650 | } |
5833 | | |
5834 | | /************************************************************************/ |
5835 | | /* Get() */ |
5836 | | /************************************************************************/ |
5837 | | |
5838 | | static double Get(GDALPDFObject *poObj, int nIndice) |
5839 | 1.39M | { |
5840 | 1.39M | if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0) |
5841 | 695k | { |
5842 | 695k | poObj = poObj->GetArray()->Get(nIndice); |
5843 | 695k | if (poObj == nullptr) |
5844 | 320 | return 0; |
5845 | 695k | return Get(poObj); |
5846 | 695k | } |
5847 | 702k | else if (poObj->GetType() == PDFObjectType_Int) |
5848 | 219k | return poObj->GetInt(); |
5849 | 482k | else if (poObj->GetType() == PDFObjectType_Real) |
5850 | 155k | return poObj->GetReal(); |
5851 | 326k | else if (poObj->GetType() == PDFObjectType_String) |
5852 | 11.3k | { |
5853 | 11.3k | const char *pszStr = poObj->GetString().c_str(); |
5854 | 11.3k | size_t nLen = strlen(pszStr); |
5855 | 11.3k | if (nLen == 0) |
5856 | 6.17k | return 0; |
5857 | | /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" |
5858 | | */ |
5859 | 5.19k | char chLast = pszStr[nLen - 1]; |
5860 | 5.19k | if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S') |
5861 | 334 | { |
5862 | 334 | double dfDeg = CPLAtof(pszStr); |
5863 | 334 | double dfMin = 0.0; |
5864 | 334 | double dfSec = 0.0; |
5865 | 334 | const char *pszNext = strchr(pszStr, ' '); |
5866 | 334 | if (pszNext) |
5867 | 158 | pszNext++; |
5868 | 334 | if (pszNext) |
5869 | 158 | dfMin = CPLAtof(pszNext); |
5870 | 334 | if (pszNext) |
5871 | 158 | pszNext = strchr(pszNext, ' '); |
5872 | 334 | if (pszNext) |
5873 | 158 | pszNext++; |
5874 | 334 | if (pszNext) |
5875 | 158 | dfSec = CPLAtof(pszNext); |
5876 | 334 | double dfVal = dfDeg + dfMin / 60 + dfSec / 3600; |
5877 | 334 | if (chLast == 'W' || chLast == 'S') |
5878 | 0 | return -dfVal; |
5879 | 334 | else |
5880 | 334 | return dfVal; |
5881 | 334 | } |
5882 | 4.85k | return CPLAtof(pszStr); |
5883 | 5.19k | } |
5884 | 315k | else |
5885 | 315k | { |
5886 | 315k | CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s", |
5887 | 315k | poObj->GetTypeName()); |
5888 | 315k | return 0; |
5889 | 315k | } |
5890 | 1.39M | } |
5891 | | |
5892 | | /************************************************************************/ |
5893 | | /* Get() */ |
5894 | | /************************************************************************/ |
5895 | | |
5896 | | static double Get(GDALPDFDictionary *poDict, const char *pszName) |
5897 | 0 | { |
5898 | 0 | GDALPDFObject *poObj = poDict->Get(pszName); |
5899 | 0 | if (poObj != nullptr) |
5900 | 0 | return Get(poObj); |
5901 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find parameter %s", pszName); |
5902 | 0 | return 0; |
5903 | 0 | } |
5904 | | |
5905 | | /************************************************************************/ |
5906 | | /* ParseLGIDictDictFirstPass() */ |
5907 | | /************************************************************************/ |
5908 | | |
5909 | | int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary *poLGIDict, |
5910 | | int *pbIsBestCandidate) |
5911 | 545 | { |
5912 | 545 | if (pbIsBestCandidate) |
5913 | 0 | *pbIsBestCandidate = FALSE; |
5914 | | |
5915 | 545 | if (poLGIDict == nullptr) |
5916 | 0 | return FALSE; |
5917 | | |
5918 | | /* -------------------------------------------------------------------- */ |
5919 | | /* Extract Type attribute */ |
5920 | | /* -------------------------------------------------------------------- */ |
5921 | 545 | GDALPDFObject *poType = poLGIDict->Get("Type"); |
5922 | 545 | if (poType == nullptr) |
5923 | 44 | { |
5924 | 44 | CPLError(CE_Failure, CPLE_AppDefined, |
5925 | 44 | "Cannot find Type of LGIDict object"); |
5926 | 44 | return FALSE; |
5927 | 44 | } |
5928 | | |
5929 | 501 | if (poType->GetType() != PDFObjectType_Name) |
5930 | 1 | { |
5931 | 1 | CPLError(CE_Failure, CPLE_AppDefined, |
5932 | 1 | "Invalid type for Type of LGIDict object"); |
5933 | 1 | return FALSE; |
5934 | 1 | } |
5935 | | |
5936 | 500 | if (strcmp(poType->GetName().c_str(), "LGIDict") != 0) |
5937 | 87 | { |
5938 | 87 | CPLError(CE_Failure, CPLE_AppDefined, |
5939 | 87 | "Invalid value for Type of LGIDict object : %s", |
5940 | 87 | poType->GetName().c_str()); |
5941 | 87 | return FALSE; |
5942 | 87 | } |
5943 | | |
5944 | | /* -------------------------------------------------------------------- */ |
5945 | | /* Extract Version attribute */ |
5946 | | /* -------------------------------------------------------------------- */ |
5947 | 413 | GDALPDFObject *poVersion = poLGIDict->Get("Version"); |
5948 | 413 | if (poVersion == nullptr) |
5949 | 1 | { |
5950 | 1 | CPLError(CE_Failure, CPLE_AppDefined, |
5951 | 1 | "Cannot find Version of LGIDict object"); |
5952 | 1 | return FALSE; |
5953 | 1 | } |
5954 | | |
5955 | 412 | if (poVersion->GetType() == PDFObjectType_String) |
5956 | 345 | { |
5957 | | /* OGC best practice is 2.1 */ |
5958 | 345 | CPLDebug("PDF", "LGIDict Version : %s", poVersion->GetString().c_str()); |
5959 | 345 | } |
5960 | 67 | else if (poVersion->GetType() == PDFObjectType_Int) |
5961 | 1 | { |
5962 | | /* Old TerraGo is 2 */ |
5963 | 1 | CPLDebug("PDF", "LGIDict Version : %d", poVersion->GetInt()); |
5964 | 1 | } |
5965 | | |
5966 | | /* USGS PDF maps have several LGIDict. Keep the one whose description */ |
5967 | | /* is "Map Layers" by default */ |
5968 | 412 | const char *pszNeatlineToSelect = |
5969 | 412 | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
5970 | | |
5971 | | /* -------------------------------------------------------------------- */ |
5972 | | /* Extract Neatline attribute */ |
5973 | | /* -------------------------------------------------------------------- */ |
5974 | 412 | GDALPDFObject *poNeatline = poLGIDict->Get("Neatline"); |
5975 | 412 | if (poNeatline != nullptr && poNeatline->GetType() == PDFObjectType_Array) |
5976 | 404 | { |
5977 | 404 | int nLength = poNeatline->GetArray()->GetLength(); |
5978 | 404 | if ((nLength % 2) != 0 || nLength < 4) |
5979 | 38 | { |
5980 | 38 | CPLError(CE_Failure, CPLE_AppDefined, |
5981 | 38 | "Invalid length for Neatline"); |
5982 | 38 | return FALSE; |
5983 | 38 | } |
5984 | | |
5985 | 366 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
5986 | 366 | bool bIsAskedNeatline = false; |
5987 | 366 | if (poDescription != nullptr && |
5988 | 28 | poDescription->GetType() == PDFObjectType_String) |
5989 | 28 | { |
5990 | 28 | CPLDebug("PDF", "Description = %s", |
5991 | 28 | poDescription->GetString().c_str()); |
5992 | | |
5993 | 28 | if (EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect)) |
5994 | 0 | { |
5995 | 0 | m_dfMaxArea = 1e300; |
5996 | 0 | bIsAskedNeatline = true; |
5997 | 0 | } |
5998 | 28 | } |
5999 | | |
6000 | 366 | if (!bIsAskedNeatline) |
6001 | 366 | { |
6002 | 366 | double dfMinX = 0.0; |
6003 | 366 | double dfMinY = 0.0; |
6004 | 366 | double dfMaxX = 0.0; |
6005 | 366 | double dfMaxY = 0.0; |
6006 | 137k | for (int i = 0; i < nLength; i += 2) |
6007 | 136k | { |
6008 | 136k | double dfX = Get(poNeatline, i); |
6009 | 136k | double dfY = Get(poNeatline, i + 1); |
6010 | 136k | if (i == 0 || dfX < dfMinX) |
6011 | 940 | dfMinX = dfX; |
6012 | 136k | if (i == 0 || dfY < dfMinY) |
6013 | 1.43k | dfMinY = dfY; |
6014 | 136k | if (i == 0 || dfX > dfMaxX) |
6015 | 1.54k | dfMaxX = dfX; |
6016 | 136k | if (i == 0 || dfY > dfMaxY) |
6017 | 1.14k | dfMaxY = dfY; |
6018 | 136k | } |
6019 | 366 | double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY); |
6020 | 366 | if (dfArea < m_dfMaxArea) |
6021 | 0 | { |
6022 | 0 | CPLDebug("PDF", "Not the largest neatline. Skipping it"); |
6023 | 0 | return TRUE; |
6024 | 0 | } |
6025 | | |
6026 | 366 | CPLDebug("PDF", "This is the largest neatline for now"); |
6027 | 366 | m_dfMaxArea = dfArea; |
6028 | 366 | } |
6029 | 0 | else |
6030 | 0 | CPLDebug("PDF", "The \"%s\" registration will be selected", |
6031 | 0 | pszNeatlineToSelect); |
6032 | | |
6033 | 366 | if (pbIsBestCandidate) |
6034 | 0 | *pbIsBestCandidate = TRUE; |
6035 | | |
6036 | 366 | delete m_poNeatLine; |
6037 | 366 | m_poNeatLine = new OGRPolygon(); |
6038 | 366 | OGRLinearRing *poRing = new OGRLinearRing(); |
6039 | 366 | if (nLength == 4) |
6040 | 3 | { |
6041 | | /* 2 points only ? They are the bounding box */ |
6042 | 3 | double dfX1 = Get(poNeatline, 0); |
6043 | 3 | double dfY1 = Get(poNeatline, 1); |
6044 | 3 | double dfX2 = Get(poNeatline, 2); |
6045 | 3 | double dfY2 = Get(poNeatline, 3); |
6046 | 3 | poRing->addPoint(dfX1, dfY1); |
6047 | 3 | poRing->addPoint(dfX2, dfY1); |
6048 | 3 | poRing->addPoint(dfX2, dfY2); |
6049 | 3 | poRing->addPoint(dfX1, dfY2); |
6050 | 3 | } |
6051 | 363 | else |
6052 | 363 | { |
6053 | 137k | for (int i = 0; i < nLength; i += 2) |
6054 | 136k | { |
6055 | 136k | double dfX = Get(poNeatline, i); |
6056 | 136k | double dfY = Get(poNeatline, i + 1); |
6057 | 136k | poRing->addPoint(dfX, dfY); |
6058 | 136k | } |
6059 | 363 | } |
6060 | 366 | poRing->closeRings(); |
6061 | 366 | m_poNeatLine->addRingDirectly(poRing); |
6062 | 366 | } |
6063 | | |
6064 | 374 | return TRUE; |
6065 | 412 | } |
6066 | | |
6067 | | /************************************************************************/ |
6068 | | /* ParseLGIDictDictSecondPass() */ |
6069 | | /************************************************************************/ |
6070 | | |
6071 | | int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary *poLGIDict) |
6072 | 374 | { |
6073 | 374 | int i; |
6074 | | |
6075 | | /* -------------------------------------------------------------------- */ |
6076 | | /* Extract Description attribute */ |
6077 | | /* -------------------------------------------------------------------- */ |
6078 | 374 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
6079 | 374 | if (poDescription != nullptr && |
6080 | 32 | poDescription->GetType() == PDFObjectType_String) |
6081 | 32 | { |
6082 | 32 | CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str()); |
6083 | 32 | } |
6084 | | |
6085 | | /* -------------------------------------------------------------------- */ |
6086 | | /* Extract CTM attribute */ |
6087 | | /* -------------------------------------------------------------------- */ |
6088 | 374 | GDALPDFObject *poCTM = poLGIDict->Get("CTM"); |
6089 | 374 | m_bHasCTM = false; |
6090 | 374 | if (poCTM != nullptr && poCTM->GetType() == PDFObjectType_Array && |
6091 | 328 | CPLTestBool(CPLGetConfigOption("PDF_USE_CTM", "YES"))) |
6092 | 328 | { |
6093 | 328 | int nLength = poCTM->GetArray()->GetLength(); |
6094 | 328 | if (nLength != 6) |
6095 | 66 | { |
6096 | 66 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for CTM"); |
6097 | 66 | return FALSE; |
6098 | 66 | } |
6099 | | |
6100 | 262 | m_bHasCTM = true; |
6101 | 1.83k | for (i = 0; i < nLength; i++) |
6102 | 1.57k | { |
6103 | 1.57k | m_adfCTM[i] = Get(poCTM, i); |
6104 | | /* Nullify rotation terms that are significantly smaller than */ |
6105 | | /* scaling terms. */ |
6106 | 1.57k | if ((i == 1 || i == 2) && |
6107 | 524 | fabs(m_adfCTM[i]) < fabs(m_adfCTM[0]) * 1e-10) |
6108 | 142 | m_adfCTM[i] = 0; |
6109 | 1.57k | CPLDebug("PDF", "CTM[%d] = %.16g", i, m_adfCTM[i]); |
6110 | 1.57k | } |
6111 | 262 | } |
6112 | | |
6113 | | /* -------------------------------------------------------------------- */ |
6114 | | /* Extract Registration attribute */ |
6115 | | /* -------------------------------------------------------------------- */ |
6116 | 308 | GDALPDFObject *poRegistration = poLGIDict->Get("Registration"); |
6117 | 308 | if (poRegistration != nullptr && |
6118 | 28 | poRegistration->GetType() == PDFObjectType_Array) |
6119 | 28 | { |
6120 | 28 | GDALPDFArray *poRegistrationArray = poRegistration->GetArray(); |
6121 | 28 | int nLength = poRegistrationArray->GetLength(); |
6122 | 28 | if (nLength > 4 || (!m_bHasCTM && nLength >= 2) || |
6123 | 0 | CPLTestBool(CPLGetConfigOption("PDF_REPORT_GCPS", "NO"))) |
6124 | 28 | { |
6125 | 28 | m_nGCPCount = 0; |
6126 | 28 | m_pasGCPList = |
6127 | 28 | static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nLength)); |
6128 | | |
6129 | 142 | for (i = 0; i < nLength; i++) |
6130 | 114 | { |
6131 | 114 | GDALPDFObject *poGCP = poRegistrationArray->Get(i); |
6132 | 114 | if (poGCP != nullptr && |
6133 | 114 | poGCP->GetType() == PDFObjectType_Array && |
6134 | 111 | poGCP->GetArray()->GetLength() == 4) |
6135 | 110 | { |
6136 | 110 | double dfUserX = Get(poGCP, 0); |
6137 | 110 | double dfUserY = Get(poGCP, 1); |
6138 | 110 | double dfX = Get(poGCP, 2); |
6139 | 110 | double dfY = Get(poGCP, 3); |
6140 | 110 | CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX); |
6141 | 110 | CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY); |
6142 | 110 | CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX); |
6143 | 110 | CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY); |
6144 | | |
6145 | 110 | char szID[32]; |
6146 | 110 | snprintf(szID, sizeof(szID), "%d", m_nGCPCount + 1); |
6147 | 110 | m_pasGCPList[m_nGCPCount].pszId = CPLStrdup(szID); |
6148 | 110 | m_pasGCPList[m_nGCPCount].pszInfo = CPLStrdup(""); |
6149 | 110 | m_pasGCPList[m_nGCPCount].dfGCPPixel = dfUserX; |
6150 | 110 | m_pasGCPList[m_nGCPCount].dfGCPLine = dfUserY; |
6151 | 110 | m_pasGCPList[m_nGCPCount].dfGCPX = dfX; |
6152 | 110 | m_pasGCPList[m_nGCPCount].dfGCPY = dfY; |
6153 | 110 | m_nGCPCount++; |
6154 | 110 | } |
6155 | 114 | } |
6156 | | |
6157 | 28 | if (m_nGCPCount == 0) |
6158 | 0 | { |
6159 | 0 | CPLFree(m_pasGCPList); |
6160 | 0 | m_pasGCPList = nullptr; |
6161 | 0 | } |
6162 | 28 | } |
6163 | 28 | } |
6164 | | |
6165 | 308 | if (!m_bHasCTM && m_nGCPCount == 0) |
6166 | 18 | { |
6167 | 18 | CPLDebug("PDF", "Neither CTM nor Registration found"); |
6168 | 18 | return FALSE; |
6169 | 18 | } |
6170 | | |
6171 | | /* -------------------------------------------------------------------- */ |
6172 | | /* Extract Projection attribute */ |
6173 | | /* -------------------------------------------------------------------- */ |
6174 | 290 | GDALPDFObject *poProjection = poLGIDict->Get("Projection"); |
6175 | 290 | if (poProjection == nullptr || |
6176 | 263 | poProjection->GetType() != PDFObjectType_Dictionary) |
6177 | 35 | { |
6178 | 35 | CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection"); |
6179 | 35 | return FALSE; |
6180 | 35 | } |
6181 | | |
6182 | 255 | return ParseProjDict(poProjection->GetDictionary()); |
6183 | 290 | } |
6184 | | |
6185 | | /************************************************************************/ |
6186 | | /* ParseProjDict() */ |
6187 | | /************************************************************************/ |
6188 | | |
6189 | | int PDFDataset::ParseProjDict(GDALPDFDictionary *poProjDict) |
6190 | 255 | { |
6191 | 255 | if (poProjDict == nullptr) |
6192 | 0 | return FALSE; |
6193 | 255 | OGRSpatialReference oSRS; |
6194 | 255 | oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
6195 | | |
6196 | | /* -------------------------------------------------------------------- */ |
6197 | | /* Extract WKT attribute (GDAL extension) */ |
6198 | | /* -------------------------------------------------------------------- */ |
6199 | 255 | GDALPDFObject *poWKT = poProjDict->Get("WKT"); |
6200 | 255 | if (poWKT != nullptr && poWKT->GetType() == PDFObjectType_String && |
6201 | 26 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE"))) |
6202 | 26 | { |
6203 | 26 | CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it"); |
6204 | 26 | const char *pszWKTRead = poWKT->GetString().c_str(); |
6205 | 26 | if (pszWKTRead[0] != 0) |
6206 | 26 | m_oSRS.importFromWkt(pszWKTRead); |
6207 | 26 | return TRUE; |
6208 | 26 | } |
6209 | | |
6210 | | /* -------------------------------------------------------------------- */ |
6211 | | /* Extract Type attribute */ |
6212 | | /* -------------------------------------------------------------------- */ |
6213 | 229 | GDALPDFObject *poType = poProjDict->Get("Type"); |
6214 | 229 | if (poType == nullptr) |
6215 | 10 | { |
6216 | 10 | CPLError(CE_Failure, CPLE_AppDefined, |
6217 | 10 | "Cannot find Type of Projection object"); |
6218 | 10 | return FALSE; |
6219 | 10 | } |
6220 | | |
6221 | 219 | if (poType->GetType() != PDFObjectType_Name) |
6222 | 3 | { |
6223 | 3 | CPLError(CE_Failure, CPLE_AppDefined, |
6224 | 3 | "Invalid type for Type of Projection object"); |
6225 | 3 | return FALSE; |
6226 | 3 | } |
6227 | | |
6228 | 216 | if (strcmp(poType->GetName().c_str(), "Projection") != 0) |
6229 | 13 | { |
6230 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
6231 | 13 | "Invalid value for Type of Projection object : %s", |
6232 | 13 | poType->GetName().c_str()); |
6233 | 13 | return FALSE; |
6234 | 13 | } |
6235 | | |
6236 | | /* -------------------------------------------------------------------- */ |
6237 | | /* Extract Datum attribute */ |
6238 | | /* -------------------------------------------------------------------- */ |
6239 | 203 | int bIsWGS84 = FALSE; |
6240 | 203 | int bIsNAD83 = FALSE; |
6241 | | /* int bIsNAD27 = FALSE; */ |
6242 | | |
6243 | 203 | GDALPDFObject *poDatum = poProjDict->Get("Datum"); |
6244 | 203 | if (poDatum != nullptr) |
6245 | 43 | { |
6246 | 43 | if (poDatum->GetType() == PDFObjectType_String) |
6247 | 43 | { |
6248 | | /* Using Annex A of |
6249 | | * http://portal.opengeospatial.org/files/?artifact_id=40537 */ |
6250 | 43 | const char *pszDatum = poDatum->GetString().c_str(); |
6251 | 43 | CPLDebug("PDF", "Datum = %s", pszDatum); |
6252 | 43 | if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE")) |
6253 | 19 | { |
6254 | 19 | bIsWGS84 = TRUE; |
6255 | 19 | oSRS.SetWellKnownGeogCS("WGS84"); |
6256 | 19 | } |
6257 | 24 | else if (EQUAL(pszDatum, "NAR") || STARTS_WITH_CI(pszDatum, "NAR-")) |
6258 | 0 | { |
6259 | 0 | bIsNAD83 = TRUE; |
6260 | 0 | oSRS.SetWellKnownGeogCS("NAD83"); |
6261 | 0 | } |
6262 | 24 | else if (EQUAL(pszDatum, "NAS") || STARTS_WITH_CI(pszDatum, "NAS-")) |
6263 | 0 | { |
6264 | | /* bIsNAD27 = TRUE; */ |
6265 | 0 | oSRS.SetWellKnownGeogCS("NAD27"); |
6266 | 0 | } |
6267 | 24 | else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */ |
6268 | 0 | { |
6269 | 0 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6270 | 0 | "unknown" /*const char * pszDatumName */, |
6271 | 0 | "International 1924", 6378388, 297); |
6272 | 0 | oSRS.SetTOWGS84(-333, -222, 114); |
6273 | 0 | } |
6274 | 24 | else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */ |
6275 | 0 | { |
6276 | 0 | oSRS.importFromEPSG(4131); |
6277 | 0 | } |
6278 | 24 | else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */ |
6279 | 0 | { |
6280 | 0 | oSRS.importFromEPSG(4283); |
6281 | 0 | } |
6282 | 24 | else if (STARTS_WITH_CI(pszDatum, "OHA-")) /* Old Hawaiian */ |
6283 | 0 | { |
6284 | 0 | oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */ |
6285 | 0 | if (!EQUAL(pszDatum, "OHA-M")) |
6286 | 0 | { |
6287 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6288 | 0 | "Using OHA-M (Old Hawaiian Mean) definition for " |
6289 | 0 | "%s. Potential issue with datum shift parameters", |
6290 | 0 | pszDatum); |
6291 | 0 | OGR_SRSNode *poNode = oSRS.GetRoot(); |
6292 | 0 | int iChild = poNode->FindChild("AUTHORITY"); |
6293 | 0 | if (iChild != -1) |
6294 | 0 | poNode->DestroyChild(iChild); |
6295 | 0 | iChild = poNode->FindChild("DATUM"); |
6296 | 0 | if (iChild != -1) |
6297 | 0 | { |
6298 | 0 | poNode = poNode->GetChild(iChild); |
6299 | 0 | iChild = poNode->FindChild("AUTHORITY"); |
6300 | 0 | if (iChild != -1) |
6301 | 0 | poNode->DestroyChild(iChild); |
6302 | 0 | } |
6303 | 0 | } |
6304 | 0 | } |
6305 | 24 | else |
6306 | 24 | { |
6307 | 24 | CPLError(CE_Warning, CPLE_AppDefined, |
6308 | 24 | "Unhandled (yet) value for Datum : %s. Defaulting to " |
6309 | 24 | "WGS84...", |
6310 | 24 | pszDatum); |
6311 | 24 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6312 | 24 | "unknown" /*const char * pszDatumName */, |
6313 | 24 | "unknown", 6378137, 298.257223563); |
6314 | 24 | } |
6315 | 43 | } |
6316 | 0 | else if (poDatum->GetType() == PDFObjectType_Dictionary) |
6317 | 0 | { |
6318 | 0 | GDALPDFDictionary *poDatumDict = poDatum->GetDictionary(); |
6319 | |
|
6320 | 0 | GDALPDFObject *poDatumDescription = poDatumDict->Get("Description"); |
6321 | 0 | const char *pszDatumDescription = "unknown"; |
6322 | 0 | if (poDatumDescription != nullptr && |
6323 | 0 | poDatumDescription->GetType() == PDFObjectType_String) |
6324 | 0 | pszDatumDescription = poDatumDescription->GetString().c_str(); |
6325 | 0 | CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription); |
6326 | |
|
6327 | 0 | GDALPDFObject *poEllipsoid = poDatumDict->Get("Ellipsoid"); |
6328 | 0 | if (poEllipsoid == nullptr || |
6329 | 0 | !(poEllipsoid->GetType() == PDFObjectType_String || |
6330 | 0 | poEllipsoid->GetType() == PDFObjectType_Dictionary)) |
6331 | 0 | { |
6332 | 0 | CPLError( |
6333 | 0 | CE_Warning, CPLE_AppDefined, |
6334 | 0 | "Cannot find Ellipsoid in Datum. Defaulting to WGS84..."); |
6335 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "unknown", |
6336 | 0 | 6378137, 298.257223563); |
6337 | 0 | } |
6338 | 0 | else if (poEllipsoid->GetType() == PDFObjectType_String) |
6339 | 0 | { |
6340 | 0 | const char *pszEllipsoid = poEllipsoid->GetString().c_str(); |
6341 | 0 | CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid); |
6342 | 0 | if (EQUAL(pszEllipsoid, "WE")) |
6343 | 0 | { |
6344 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "WGS 84", |
6345 | 0 | 6378137, 298.257223563); |
6346 | 0 | } |
6347 | 0 | else |
6348 | 0 | { |
6349 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6350 | 0 | "Unhandled (yet) value for Ellipsoid : %s. " |
6351 | 0 | "Defaulting to WGS84...", |
6352 | 0 | pszEllipsoid); |
6353 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, pszEllipsoid, |
6354 | 0 | 6378137, 298.257223563); |
6355 | 0 | } |
6356 | 0 | } |
6357 | 0 | else // if (poEllipsoid->GetType() == PDFObjectType_Dictionary) |
6358 | 0 | { |
6359 | 0 | GDALPDFDictionary *poEllipsoidDict = |
6360 | 0 | poEllipsoid->GetDictionary(); |
6361 | |
|
6362 | 0 | GDALPDFObject *poEllipsoidDescription = |
6363 | 0 | poEllipsoidDict->Get("Description"); |
6364 | 0 | const char *pszEllipsoidDescription = "unknown"; |
6365 | 0 | if (poEllipsoidDescription != nullptr && |
6366 | 0 | poEllipsoidDescription->GetType() == PDFObjectType_String) |
6367 | 0 | pszEllipsoidDescription = |
6368 | 0 | poEllipsoidDescription->GetString().c_str(); |
6369 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", |
6370 | 0 | pszEllipsoidDescription); |
6371 | |
|
6372 | 0 | double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis"); |
6373 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", |
6374 | 0 | dfSemiMajor); |
6375 | 0 | double dfInvFlattening = -1.0; |
6376 | |
|
6377 | 0 | if (poEllipsoidDict->Get("InvFlattening")) |
6378 | 0 | { |
6379 | 0 | dfInvFlattening = Get(poEllipsoidDict, "InvFlattening"); |
6380 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", |
6381 | 0 | dfInvFlattening); |
6382 | 0 | } |
6383 | 0 | else if (poEllipsoidDict->Get("SemiMinorAxis")) |
6384 | 0 | { |
6385 | 0 | double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis"); |
6386 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", |
6387 | 0 | dfSemiMinor); |
6388 | 0 | dfInvFlattening = |
6389 | 0 | OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); |
6390 | 0 | } |
6391 | |
|
6392 | 0 | if (dfSemiMajor != 0.0 && dfInvFlattening != -1.0) |
6393 | 0 | { |
6394 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6395 | 0 | pszEllipsoidDescription, dfSemiMajor, |
6396 | 0 | dfInvFlattening); |
6397 | 0 | } |
6398 | 0 | else |
6399 | 0 | { |
6400 | 0 | CPLError( |
6401 | 0 | CE_Warning, CPLE_AppDefined, |
6402 | 0 | "Invalid Ellipsoid object. Defaulting to WGS84..."); |
6403 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6404 | 0 | pszEllipsoidDescription, 6378137, |
6405 | 0 | 298.257223563); |
6406 | 0 | } |
6407 | 0 | } |
6408 | |
|
6409 | 0 | GDALPDFObject *poTOWGS84 = poDatumDict->Get("ToWGS84"); |
6410 | 0 | if (poTOWGS84 != nullptr && |
6411 | 0 | poTOWGS84->GetType() == PDFObjectType_Dictionary) |
6412 | 0 | { |
6413 | 0 | GDALPDFDictionary *poTOWGS84Dict = poTOWGS84->GetDictionary(); |
6414 | 0 | double dx = Get(poTOWGS84Dict, "dx"); |
6415 | 0 | double dy = Get(poTOWGS84Dict, "dy"); |
6416 | 0 | double dz = Get(poTOWGS84Dict, "dz"); |
6417 | 0 | if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") && |
6418 | 0 | poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf")) |
6419 | 0 | { |
6420 | 0 | double rx = Get(poTOWGS84Dict, "rx"); |
6421 | 0 | double ry = Get(poTOWGS84Dict, "ry"); |
6422 | 0 | double rz = Get(poTOWGS84Dict, "rz"); |
6423 | 0 | double sf = Get(poTOWGS84Dict, "sf"); |
6424 | 0 | oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf); |
6425 | 0 | } |
6426 | 0 | else |
6427 | 0 | { |
6428 | 0 | oSRS.SetTOWGS84(dx, dy, dz); |
6429 | 0 | } |
6430 | 0 | } |
6431 | 0 | } |
6432 | 43 | } |
6433 | | |
6434 | | /* -------------------------------------------------------------------- */ |
6435 | | /* Extract Hemisphere attribute */ |
6436 | | /* -------------------------------------------------------------------- */ |
6437 | 203 | CPLString osHemisphere; |
6438 | 203 | GDALPDFObject *poHemisphere = poProjDict->Get("Hemisphere"); |
6439 | 203 | if (poHemisphere != nullptr && |
6440 | 0 | poHemisphere->GetType() == PDFObjectType_String) |
6441 | 0 | { |
6442 | 0 | osHemisphere = poHemisphere->GetString(); |
6443 | 0 | } |
6444 | | |
6445 | | /* -------------------------------------------------------------------- */ |
6446 | | /* Extract ProjectionType attribute */ |
6447 | | /* -------------------------------------------------------------------- */ |
6448 | 203 | GDALPDFObject *poProjectionType = poProjDict->Get("ProjectionType"); |
6449 | 203 | if (poProjectionType == nullptr || |
6450 | 198 | poProjectionType->GetType() != PDFObjectType_String) |
6451 | 5 | { |
6452 | 5 | CPLError(CE_Failure, CPLE_AppDefined, |
6453 | 5 | "Cannot find ProjectionType of Projection object"); |
6454 | 5 | return FALSE; |
6455 | 5 | } |
6456 | 198 | CPLString osProjectionType(poProjectionType->GetString()); |
6457 | 198 | CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str()); |
6458 | | |
6459 | | /* Unhandled: NONE, GEODETIC */ |
6460 | | |
6461 | 198 | if (EQUAL(osProjectionType, "GEOGRAPHIC")) |
6462 | 185 | { |
6463 | | /* Nothing to do */ |
6464 | 185 | } |
6465 | | |
6466 | | /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */ |
6467 | | |
6468 | 13 | else if (EQUAL(osProjectionType, "UT")) /* UTM */ |
6469 | 0 | { |
6470 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6471 | 0 | if (dfZone >= 1 && dfZone <= 60) |
6472 | 0 | { |
6473 | 0 | int nZone = static_cast<int>(dfZone); |
6474 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6475 | 0 | if (bIsWGS84) |
6476 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6477 | 0 | else |
6478 | 0 | oSRS.SetUTM(nZone, bNorth); |
6479 | 0 | } |
6480 | 0 | } |
6481 | | |
6482 | 13 | else if (EQUAL(osProjectionType, |
6483 | 13 | "UP")) /* Universal Polar Stereographic (UPS) */ |
6484 | 0 | { |
6485 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6486 | 0 | if (bIsWGS84) |
6487 | 0 | oSRS.importFromEPSG((bNorth) ? 32661 : 32761); |
6488 | 0 | else |
6489 | 0 | oSRS.SetPS((bNorth) ? 90 : -90, 0, 0.994, 200000, 200000); |
6490 | 0 | } |
6491 | | |
6492 | 13 | else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */ |
6493 | 0 | { |
6494 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6495 | 0 | if (dfZone >= 0 && dfZone <= INT_MAX) |
6496 | 0 | { |
6497 | 0 | int nZone = static_cast<int>(dfZone); |
6498 | 0 | oSRS.SetStatePlane(nZone, bIsNAD83); |
6499 | 0 | } |
6500 | 0 | } |
6501 | | |
6502 | 13 | else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */ |
6503 | 0 | { |
6504 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6505 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6506 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6507 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6508 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6509 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6510 | 0 | oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, |
6511 | 0 | dfFalseEasting, dfFalseNorthing); |
6512 | 0 | } |
6513 | | |
6514 | 13 | else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */ |
6515 | 0 | { |
6516 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6517 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6518 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6519 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6520 | 0 | oSRS.SetAE(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6521 | 0 | } |
6522 | | |
6523 | 13 | else if (EQUAL(osProjectionType, "BF")) /* Bonne */ |
6524 | 0 | { |
6525 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6526 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6527 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6528 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6529 | 0 | oSRS.SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6530 | 0 | dfFalseNorthing); |
6531 | 0 | } |
6532 | | |
6533 | 13 | else if (EQUAL(osProjectionType, "CS")) /* Cassini */ |
6534 | 0 | { |
6535 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6536 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6537 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6538 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6539 | 0 | oSRS.SetCS(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6540 | 0 | } |
6541 | | |
6542 | 13 | else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */ |
6543 | 0 | { |
6544 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6545 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6546 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6547 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6548 | 0 | oSRS.SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6549 | 0 | dfFalseNorthing); |
6550 | 0 | } |
6551 | | |
6552 | 13 | else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */ |
6553 | 0 | { |
6554 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6555 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6556 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6557 | 0 | oSRS.SetEckertIV(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6558 | 0 | } |
6559 | | |
6560 | 13 | else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */ |
6561 | 0 | { |
6562 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6563 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6564 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6565 | 0 | oSRS.SetEckertVI(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6566 | 0 | } |
6567 | | |
6568 | 13 | else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */ |
6569 | 0 | { |
6570 | 0 | double dfCenterLat = Get(poProjDict, "StandardParallel"); |
6571 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6572 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6573 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6574 | 0 | oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting, |
6575 | 0 | dfFalseNorthing); |
6576 | 0 | } |
6577 | | |
6578 | 13 | else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */ |
6579 | 0 | { |
6580 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6581 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6582 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6583 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6584 | 0 | oSRS.SetGnomonic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6585 | 0 | dfFalseNorthing); |
6586 | 0 | } |
6587 | | |
6588 | 13 | else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */ |
6589 | 0 | { |
6590 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6591 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6592 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6593 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6594 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6595 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6596 | 0 | oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting, |
6597 | 0 | dfFalseNorthing); |
6598 | 0 | } |
6599 | | |
6600 | 13 | else if (EQUAL(osProjectionType, "MC")) /* Mercator */ |
6601 | 0 | { |
6602 | | #ifdef not_supported |
6603 | | if (poProjDict->Get("StandardParallelOne") == nullptr) |
6604 | | #endif |
6605 | 0 | { |
6606 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6607 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6608 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6609 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6610 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6611 | 0 | oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6612 | 0 | dfFalseNorthing); |
6613 | 0 | } |
6614 | | #ifdef not_supported |
6615 | | else |
6616 | | { |
6617 | | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6618 | | double dfCenterLat = poProjDict->Get("OriginLatitude") |
6619 | | ? Get(poProjDict, "OriginLatitude") |
6620 | | : 0; |
6621 | | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6622 | | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6623 | | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6624 | | oSRS.SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong, |
6625 | | dfFalseEasting, dfFalseNorthing); |
6626 | | } |
6627 | | #endif |
6628 | 0 | } |
6629 | | |
6630 | 13 | else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */ |
6631 | 0 | { |
6632 | 0 | double dfCenterLat = 0 /* ? */; |
6633 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6634 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6635 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6636 | 0 | oSRS.SetMC(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6637 | 0 | } |
6638 | | |
6639 | 13 | else if (EQUAL(osProjectionType, "MP")) /* Mollweide */ |
6640 | 0 | { |
6641 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6642 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6643 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6644 | 0 | oSRS.SetMollweide(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6645 | 0 | } |
6646 | | |
6647 | | /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */ |
6648 | | |
6649 | 13 | else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */ |
6650 | 0 | { |
6651 | | /* No parameter specified in the PDF, so let's take the ones of |
6652 | | * EPSG:27200 */ |
6653 | 0 | double dfCenterLat = -41; |
6654 | 0 | double dfCenterLong = 173; |
6655 | 0 | double dfFalseEasting = 2510000; |
6656 | 0 | double dfFalseNorthing = 6023150; |
6657 | 0 | oSRS.SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting, |
6658 | 0 | dfFalseNorthing); |
6659 | 0 | } |
6660 | | |
6661 | 13 | else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */ |
6662 | 0 | { |
6663 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6664 | 0 | double dfLat1 = Get(poProjDict, "LatitudeOne"); |
6665 | 0 | double dfLong1 = Get(poProjDict, "LongitudeOne"); |
6666 | 0 | double dfLat2 = Get(poProjDict, "LatitudeTwo"); |
6667 | 0 | double dfLong2 = Get(poProjDict, "LongitudeTwo"); |
6668 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6669 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6670 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6671 | 0 | oSRS.SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2, dfScale, |
6672 | 0 | dfFalseEasting, dfFalseNorthing); |
6673 | 0 | } |
6674 | | |
6675 | 13 | else if (EQUAL(osProjectionType, "OD")) /* Orthographic */ |
6676 | 0 | { |
6677 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6678 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6679 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6680 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6681 | 0 | oSRS.SetOrthographic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6682 | 0 | dfFalseNorthing); |
6683 | 0 | } |
6684 | | |
6685 | 13 | else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */ |
6686 | 0 | { |
6687 | 0 | double dfCenterLat = Get(poProjDict, "LatitudeTrueScale"); |
6688 | 0 | double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole"); |
6689 | 0 | double dfScale = 1.0; |
6690 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6691 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6692 | 0 | oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6693 | 0 | dfFalseNorthing); |
6694 | 0 | } |
6695 | | |
6696 | 13 | else if (EQUAL(osProjectionType, "PH")) /* Polyconic */ |
6697 | 0 | { |
6698 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6699 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6700 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6701 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6702 | 0 | oSRS.SetPolyconic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6703 | 0 | dfFalseNorthing); |
6704 | 0 | } |
6705 | | |
6706 | 13 | else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */ |
6707 | 0 | { |
6708 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6709 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6710 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6711 | 0 | oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6712 | 0 | } |
6713 | | |
6714 | 13 | else if (EQUAL(osProjectionType, "SD")) /* Stereographic */ |
6715 | 0 | { |
6716 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6717 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6718 | 0 | double dfScale = 1.0; |
6719 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6720 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6721 | 0 | oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale, |
6722 | 0 | dfFalseEasting, dfFalseNorthing); |
6723 | 0 | } |
6724 | | |
6725 | 13 | else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */ |
6726 | 0 | { |
6727 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6728 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6729 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6730 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6731 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6732 | 0 | if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfCenterLong >= -180 && |
6733 | 0 | dfCenterLong <= 180 && dfFalseEasting == 500000 && |
6734 | 0 | (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0)) |
6735 | 0 | { |
6736 | 0 | const int nZone = |
6737 | 0 | static_cast<int>(floor((dfCenterLong + 180.0) / 6.0) + 1); |
6738 | 0 | int bNorth = dfFalseNorthing == 0; |
6739 | 0 | if (bIsWGS84) |
6740 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6741 | 0 | else if (bIsNAD83 && bNorth) |
6742 | 0 | oSRS.importFromEPSG(26900 + nZone); |
6743 | 0 | else |
6744 | 0 | oSRS.SetUTM(nZone, bNorth); |
6745 | 0 | } |
6746 | 0 | else |
6747 | 0 | { |
6748 | 0 | oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6749 | 0 | dfFalseNorthing); |
6750 | 0 | } |
6751 | 0 | } |
6752 | | |
6753 | | /* Unhandled TX : Transverse Cylindrical Equal Area */ |
6754 | | |
6755 | 13 | else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */ |
6756 | 0 | { |
6757 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6758 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6759 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6760 | 0 | oSRS.SetVDG(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6761 | 0 | } |
6762 | | |
6763 | 13 | else |
6764 | 13 | { |
6765 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
6766 | 13 | "Unhandled (yet) value for ProjectionType : %s", |
6767 | 13 | osProjectionType.c_str()); |
6768 | 13 | return FALSE; |
6769 | 13 | } |
6770 | | |
6771 | | /* -------------------------------------------------------------------- */ |
6772 | | /* Extract Units attribute */ |
6773 | | /* -------------------------------------------------------------------- */ |
6774 | 185 | CPLString osUnits; |
6775 | 185 | GDALPDFObject *poUnits = poProjDict->Get("Units"); |
6776 | 185 | if (poUnits != nullptr && poUnits->GetType() == PDFObjectType_String && |
6777 | 0 | !EQUAL(osProjectionType, "GEOGRAPHIC")) |
6778 | 0 | { |
6779 | 0 | osUnits = poUnits->GetString(); |
6780 | 0 | CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str()); |
6781 | | |
6782 | | // This is super weird. The false easting/northing of the SRS |
6783 | | // are expressed in the unit, but the geotransform is expressed in |
6784 | | // meters. Hence this hack to have an equivalent SRS definition, but |
6785 | | // with linear units converted in meters. |
6786 | 0 | if (EQUAL(osUnits, "M")) |
6787 | 0 | oSRS.SetLinearUnits("Meter", 1.0); |
6788 | 0 | else if (EQUAL(osUnits, "FT")) |
6789 | 0 | { |
6790 | 0 | oSRS.SetLinearUnits("foot", 0.3048); |
6791 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6792 | 0 | } |
6793 | 0 | else if (EQUAL(osUnits, "USSF")) |
6794 | 0 | { |
6795 | 0 | oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV)); |
6796 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6797 | 0 | } |
6798 | 0 | else |
6799 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unhandled unit: %s", |
6800 | 0 | osUnits.c_str()); |
6801 | 0 | } |
6802 | | |
6803 | | /* -------------------------------------------------------------------- */ |
6804 | | /* Export SpatialRef */ |
6805 | | /* -------------------------------------------------------------------- */ |
6806 | 185 | m_oSRS = std::move(oSRS); |
6807 | | |
6808 | 185 | return TRUE; |
6809 | 198 | } |
6810 | | |
6811 | | /************************************************************************/ |
6812 | | /* ParseVP() */ |
6813 | | /************************************************************************/ |
6814 | | |
6815 | | int PDFDataset::ParseVP(GDALPDFObject *poVP, double dfMediaBoxWidth, |
6816 | | double dfMediaBoxHeight) |
6817 | 8.38k | { |
6818 | 8.38k | int i; |
6819 | | |
6820 | 8.38k | if (poVP->GetType() != PDFObjectType_Array) |
6821 | 54 | return FALSE; |
6822 | | |
6823 | 8.33k | GDALPDFArray *poVPArray = poVP->GetArray(); |
6824 | | |
6825 | 8.33k | int nLength = poVPArray->GetLength(); |
6826 | 8.33k | CPLDebug("PDF", "VP length = %d", nLength); |
6827 | 8.33k | if (nLength < 1) |
6828 | 0 | return FALSE; |
6829 | | |
6830 | | /* -------------------------------------------------------------------- */ |
6831 | | /* Find the largest BBox */ |
6832 | | /* -------------------------------------------------------------------- */ |
6833 | 8.33k | const char *pszNeatlineToSelect = |
6834 | 8.33k | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
6835 | | |
6836 | 8.33k | int iLargest = 0; |
6837 | 8.33k | int iRequestedVP = -1; |
6838 | 8.33k | double dfLargestArea = 0; |
6839 | | |
6840 | 16.8k | for (i = 0; i < nLength; i++) |
6841 | 10.2k | { |
6842 | 10.2k | GDALPDFObject *poVPElt = poVPArray->Get(i); |
6843 | 10.2k | if (poVPElt == nullptr || |
6844 | 9.48k | poVPElt->GetType() != PDFObjectType_Dictionary) |
6845 | 1.49k | { |
6846 | 1.49k | return FALSE; |
6847 | 1.49k | } |
6848 | | |
6849 | 8.75k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6850 | | |
6851 | 8.75k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6852 | 8.75k | if (poMeasure == nullptr || |
6853 | 6.62k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6854 | 2.25k | { |
6855 | 2.25k | continue; |
6856 | 2.25k | } |
6857 | | /* -------------------------------------------------------------------- |
6858 | | */ |
6859 | | /* Extract Subtype attribute */ |
6860 | | /* -------------------------------------------------------------------- |
6861 | | */ |
6862 | 6.50k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6863 | 6.50k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6864 | 6.50k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
6865 | 384 | { |
6866 | 384 | continue; |
6867 | 384 | } |
6868 | | |
6869 | 6.12k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
6870 | 6.12k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
6871 | 190 | { |
6872 | 190 | continue; |
6873 | 190 | } |
6874 | | |
6875 | 5.93k | GDALPDFObject *poName = poVPEltDict->Get("Name"); |
6876 | 5.93k | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
6877 | 4.93k | { |
6878 | 4.93k | CPLDebug("PDF", "Name = %s", poName->GetString().c_str()); |
6879 | 4.93k | if (EQUAL(poName->GetString().c_str(), pszNeatlineToSelect)) |
6880 | 0 | { |
6881 | 0 | iRequestedVP = i; |
6882 | 0 | } |
6883 | 4.93k | } |
6884 | | |
6885 | 5.93k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6886 | 5.93k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6887 | 120 | { |
6888 | 120 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6889 | 120 | return FALSE; |
6890 | 120 | } |
6891 | | |
6892 | 5.81k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6893 | 5.81k | if (nBboxLength != 4) |
6894 | 96 | { |
6895 | 96 | CPLError(CE_Failure, CPLE_AppDefined, |
6896 | 96 | "Invalid length for Bbox object"); |
6897 | 96 | return FALSE; |
6898 | 96 | } |
6899 | | |
6900 | 5.71k | double adfBBox[4]; |
6901 | 5.71k | adfBBox[0] = Get(poBBox, 0); |
6902 | 5.71k | adfBBox[1] = Get(poBBox, 1); |
6903 | 5.71k | adfBBox[2] = Get(poBBox, 2); |
6904 | 5.71k | adfBBox[3] = Get(poBBox, 3); |
6905 | 5.71k | double dfArea = |
6906 | 5.71k | fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]); |
6907 | 5.71k | if (dfArea > dfLargestArea) |
6908 | 5.14k | { |
6909 | 5.14k | iLargest = i; |
6910 | 5.14k | dfLargestArea = dfArea; |
6911 | 5.14k | } |
6912 | 5.71k | } |
6913 | | |
6914 | 6.62k | if (nLength > 1) |
6915 | 1.05k | { |
6916 | 1.05k | CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest); |
6917 | 1.05k | } |
6918 | | |
6919 | 6.62k | GDALPDFObject *poVPElt = nullptr; |
6920 | | |
6921 | 6.62k | if (iRequestedVP > -1) |
6922 | 0 | { |
6923 | 0 | CPLDebug("PDF", "Requested NEATLINE BBox in VP array is element %d", |
6924 | 0 | iRequestedVP); |
6925 | 0 | poVPElt = poVPArray->Get(iRequestedVP); |
6926 | 0 | } |
6927 | 6.62k | else |
6928 | 6.62k | { |
6929 | 6.62k | poVPElt = poVPArray->Get(iLargest); |
6930 | 6.62k | } |
6931 | | |
6932 | 6.62k | if (poVPElt == nullptr || poVPElt->GetType() != PDFObjectType_Dictionary) |
6933 | 0 | { |
6934 | 0 | return FALSE; |
6935 | 0 | } |
6936 | | |
6937 | 6.62k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6938 | | |
6939 | 6.62k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6940 | 6.62k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6941 | 194 | { |
6942 | 194 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6943 | 194 | return FALSE; |
6944 | 194 | } |
6945 | | |
6946 | 6.42k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6947 | 6.42k | if (nBboxLength != 4) |
6948 | 267 | { |
6949 | 267 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for Bbox object"); |
6950 | 267 | return FALSE; |
6951 | 267 | } |
6952 | | |
6953 | 6.16k | double dfULX = Get(poBBox, 0); |
6954 | 6.16k | double dfULY = dfMediaBoxHeight - Get(poBBox, 1); |
6955 | 6.16k | double dfLRX = Get(poBBox, 2); |
6956 | 6.16k | double dfLRY = dfMediaBoxHeight - Get(poBBox, 3); |
6957 | | |
6958 | | /* -------------------------------------------------------------------- */ |
6959 | | /* Extract Measure attribute */ |
6960 | | /* -------------------------------------------------------------------- */ |
6961 | 6.16k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6962 | 6.16k | if (poMeasure == nullptr || |
6963 | 5.51k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6964 | 674 | { |
6965 | 674 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Measure object"); |
6966 | 674 | return FALSE; |
6967 | 674 | } |
6968 | | |
6969 | 5.48k | int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, dfULX, |
6970 | 5.48k | dfULY, dfLRX, dfLRY); |
6971 | | |
6972 | | /* -------------------------------------------------------------------- */ |
6973 | | /* Extract PointData attribute */ |
6974 | | /* -------------------------------------------------------------------- */ |
6975 | 5.48k | GDALPDFObject *poPointData = poVPEltDict->Get("PtData"); |
6976 | 5.48k | if (poPointData != nullptr && |
6977 | 0 | poPointData->GetType() == PDFObjectType_Dictionary) |
6978 | 0 | { |
6979 | 0 | CPLDebug("PDF", "Found PointData"); |
6980 | 0 | } |
6981 | | |
6982 | 5.48k | return bRet; |
6983 | 6.16k | } |
6984 | | |
6985 | | /************************************************************************/ |
6986 | | /* ParseMeasure() */ |
6987 | | /************************************************************************/ |
6988 | | |
6989 | | int PDFDataset::ParseMeasure(GDALPDFObject *poMeasure, double dfMediaBoxWidth, |
6990 | | double dfMediaBoxHeight, double dfULX, |
6991 | | double dfULY, double dfLRX, double dfLRY) |
6992 | 5.48k | { |
6993 | 5.48k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6994 | | |
6995 | | /* -------------------------------------------------------------------- */ |
6996 | | /* Extract Subtype attribute */ |
6997 | | /* -------------------------------------------------------------------- */ |
6998 | 5.48k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6999 | 5.48k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
7000 | 356 | { |
7001 | 356 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Subtype object"); |
7002 | 356 | return FALSE; |
7003 | 356 | } |
7004 | | |
7005 | 5.13k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
7006 | 5.13k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
7007 | 18 | return FALSE; |
7008 | | |
7009 | | /* -------------------------------------------------------------------- */ |
7010 | | /* Extract Bounds attribute (optional) */ |
7011 | | /* -------------------------------------------------------------------- */ |
7012 | | |
7013 | | /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf |
7014 | | */ |
7015 | | /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */ |
7016 | | /* LPTS, GPTS and Bounds. Use those ones */ |
7017 | | |
7018 | 5.11k | GDALPDFObject *poBounds = poMeasureDict->Get("lgit:Bounds"); |
7019 | 5.11k | if (poBounds != nullptr && poBounds->GetType() == PDFObjectType_Array) |
7020 | 0 | { |
7021 | 0 | CPLDebug("PDF", "Using lgit:Bounds"); |
7022 | 0 | } |
7023 | 5.11k | else if ((poBounds = poMeasureDict->Get("Bounds")) == nullptr || |
7024 | 2.88k | poBounds->GetType() != PDFObjectType_Array) |
7025 | 2.23k | { |
7026 | 2.23k | poBounds = nullptr; |
7027 | 2.23k | } |
7028 | | |
7029 | 5.11k | if (poBounds != nullptr) |
7030 | 2.87k | { |
7031 | 2.87k | int nBoundsLength = poBounds->GetArray()->GetLength(); |
7032 | 2.87k | if (nBoundsLength == 8) |
7033 | 2.48k | { |
7034 | 2.48k | double adfBounds[8]; |
7035 | 22.3k | for (int i = 0; i < 8; i++) |
7036 | 19.8k | { |
7037 | 19.8k | adfBounds[i] = Get(poBounds, i); |
7038 | 19.8k | CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]); |
7039 | 19.8k | } |
7040 | | |
7041 | | // TODO we should use it to restrict the neatline but |
7042 | | // I have yet to set a sample where bounds are not the four |
7043 | | // corners of the unit square. |
7044 | 2.48k | } |
7045 | 2.87k | } |
7046 | | |
7047 | | /* -------------------------------------------------------------------- */ |
7048 | | /* Extract GPTS attribute */ |
7049 | | /* -------------------------------------------------------------------- */ |
7050 | 5.11k | GDALPDFObject *poGPTS = poMeasureDict->Get("lgit:GPTS"); |
7051 | 5.11k | if (poGPTS != nullptr && poGPTS->GetType() == PDFObjectType_Array) |
7052 | 0 | { |
7053 | 0 | CPLDebug("PDF", "Using lgit:GPTS"); |
7054 | 0 | } |
7055 | 5.11k | else if ((poGPTS = poMeasureDict->Get("GPTS")) == nullptr || |
7056 | 5.06k | poGPTS->GetType() != PDFObjectType_Array) |
7057 | 56 | { |
7058 | 56 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GPTS object"); |
7059 | 56 | return FALSE; |
7060 | 56 | } |
7061 | | |
7062 | 5.05k | int nGPTSLength = poGPTS->GetArray()->GetLength(); |
7063 | 5.05k | if ((nGPTSLength % 2) != 0 || nGPTSLength < 6) |
7064 | 114 | { |
7065 | 114 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for GPTS object"); |
7066 | 114 | return FALSE; |
7067 | 114 | } |
7068 | | |
7069 | 4.94k | std::vector<double> adfGPTS(nGPTSLength); |
7070 | 47.7k | for (int i = 0; i < nGPTSLength; i++) |
7071 | 42.8k | { |
7072 | 42.8k | adfGPTS[i] = Get(poGPTS, i); |
7073 | 42.8k | CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]); |
7074 | 42.8k | } |
7075 | | |
7076 | | /* -------------------------------------------------------------------- */ |
7077 | | /* Extract LPTS attribute */ |
7078 | | /* -------------------------------------------------------------------- */ |
7079 | 4.94k | GDALPDFObject *poLPTS = poMeasureDict->Get("lgit:LPTS"); |
7080 | 4.94k | if (poLPTS != nullptr && poLPTS->GetType() == PDFObjectType_Array) |
7081 | 0 | { |
7082 | 0 | CPLDebug("PDF", "Using lgit:LPTS"); |
7083 | 0 | } |
7084 | 4.94k | else if ((poLPTS = poMeasureDict->Get("LPTS")) == nullptr || |
7085 | 4.84k | poLPTS->GetType() != PDFObjectType_Array) |
7086 | 101 | { |
7087 | 101 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find LPTS object"); |
7088 | 101 | return FALSE; |
7089 | 101 | } |
7090 | | |
7091 | 4.84k | int nLPTSLength = poLPTS->GetArray()->GetLength(); |
7092 | 4.84k | if (nLPTSLength != nGPTSLength) |
7093 | 252 | { |
7094 | 252 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for LPTS object"); |
7095 | 252 | return FALSE; |
7096 | 252 | } |
7097 | | |
7098 | 4.58k | std::vector<double> adfLPTS(nLPTSLength); |
7099 | 41.3k | for (int i = 0; i < nLPTSLength; i++) |
7100 | 36.7k | { |
7101 | 36.7k | adfLPTS[i] = Get(poLPTS, i); |
7102 | 36.7k | CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]); |
7103 | 36.7k | } |
7104 | | |
7105 | | /* -------------------------------------------------------------------- */ |
7106 | | /* Extract GCS attribute */ |
7107 | | /* -------------------------------------------------------------------- */ |
7108 | 4.58k | GDALPDFObject *poGCS = poMeasureDict->Get("GCS"); |
7109 | 4.58k | if (poGCS == nullptr || poGCS->GetType() != PDFObjectType_Dictionary) |
7110 | 181 | { |
7111 | 181 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS object"); |
7112 | 181 | return FALSE; |
7113 | 181 | } |
7114 | | |
7115 | 4.40k | GDALPDFDictionary *poGCSDict = poGCS->GetDictionary(); |
7116 | | |
7117 | | /* -------------------------------------------------------------------- */ |
7118 | | /* Extract GCS.Type attribute */ |
7119 | | /* -------------------------------------------------------------------- */ |
7120 | 4.40k | GDALPDFObject *poGCSType = poGCSDict->Get("Type"); |
7121 | 4.40k | if (poGCSType == nullptr || poGCSType->GetType() != PDFObjectType_Name) |
7122 | 72 | { |
7123 | 72 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS.Type object"); |
7124 | 72 | return FALSE; |
7125 | 72 | } |
7126 | | |
7127 | 4.33k | CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str()); |
7128 | | |
7129 | | /* -------------------------------------------------------------------- */ |
7130 | | /* Extract EPSG attribute */ |
7131 | | /* -------------------------------------------------------------------- */ |
7132 | 4.33k | GDALPDFObject *poEPSG = poGCSDict->Get("EPSG"); |
7133 | 4.33k | int nEPSGCode = 0; |
7134 | 4.33k | if (poEPSG != nullptr && poEPSG->GetType() == PDFObjectType_Int) |
7135 | 2.84k | { |
7136 | 2.84k | nEPSGCode = poEPSG->GetInt(); |
7137 | 2.84k | CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode); |
7138 | 2.84k | } |
7139 | | |
7140 | | /* -------------------------------------------------------------------- */ |
7141 | | /* Extract GCS.WKT attribute */ |
7142 | | /* -------------------------------------------------------------------- */ |
7143 | 4.33k | GDALPDFObject *poGCSWKT = poGCSDict->Get("WKT"); |
7144 | 4.33k | if (poGCSWKT != nullptr && poGCSWKT->GetType() != PDFObjectType_String) |
7145 | 4 | { |
7146 | 4 | poGCSWKT = nullptr; |
7147 | 4 | } |
7148 | | |
7149 | 4.33k | if (poGCSWKT != nullptr) |
7150 | 4.30k | CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str()); |
7151 | | |
7152 | 4.33k | if (nEPSGCode <= 0 && poGCSWKT == nullptr) |
7153 | 11 | { |
7154 | 11 | CPLError(CE_Failure, CPLE_AppDefined, |
7155 | 11 | "Cannot find GCS.WKT or GCS.EPSG objects"); |
7156 | 11 | return FALSE; |
7157 | 11 | } |
7158 | | |
7159 | 4.32k | if (poGCSWKT != nullptr) |
7160 | 4.30k | { |
7161 | 4.30k | m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()); |
7162 | 4.30k | } |
7163 | | |
7164 | 4.32k | bool bSRSOK = false; |
7165 | 4.32k | if (nEPSGCode != 0) |
7166 | 2.84k | { |
7167 | | // At time of writing EPSG CRS codes are <= 32767. |
7168 | | // The usual practice is that codes >= 100000 are in the ESRI namespace |
7169 | | // instead |
7170 | 2.84k | if (nEPSGCode >= 100000) |
7171 | 66 | { |
7172 | 66 | CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler); |
7173 | 66 | OGRSpatialReference oSRS_ESRI; |
7174 | 66 | oSRS_ESRI.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
7175 | 66 | if (oSRS_ESRI.SetFromUserInput(CPLSPrintf("ESRI:%d", nEPSGCode)) == |
7176 | 66 | OGRERR_NONE) |
7177 | 64 | { |
7178 | 64 | bSRSOK = true; |
7179 | | |
7180 | | // Check consistency of ESRI:xxxx and WKT definitions |
7181 | 64 | if (poGCSWKT != nullptr) |
7182 | 60 | { |
7183 | 60 | if (!m_oSRS.GetName() || |
7184 | 41 | (!EQUAL(oSRS_ESRI.GetName(), m_oSRS.GetName()) && |
7185 | 13 | !oSRS_ESRI.IsSame(&m_oSRS))) |
7186 | 32 | { |
7187 | 32 | CPLDebug("PDF", |
7188 | 32 | "Definition from ESRI:%d and WKT=%s do not " |
7189 | 32 | "match. Using WKT string", |
7190 | 32 | nEPSGCode, poGCSWKT->GetString().c_str()); |
7191 | 32 | bSRSOK = false; |
7192 | 32 | } |
7193 | 60 | } |
7194 | 64 | if (bSRSOK) |
7195 | 32 | { |
7196 | 32 | m_oSRS = std::move(oSRS_ESRI); |
7197 | 32 | } |
7198 | 64 | } |
7199 | 66 | } |
7200 | 2.77k | else if (m_oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE) |
7201 | 1.78k | { |
7202 | 1.78k | bSRSOK = true; |
7203 | 1.78k | } |
7204 | 2.84k | } |
7205 | | |
7206 | 4.32k | if (!bSRSOK) |
7207 | 2.50k | { |
7208 | 2.50k | if (poGCSWKT == nullptr) |
7209 | 5 | { |
7210 | 5 | CPLError(CE_Failure, CPLE_AppDefined, |
7211 | 5 | "Cannot resolve EPSG object, and GCS.WKT not found"); |
7212 | 5 | return FALSE; |
7213 | 5 | } |
7214 | | |
7215 | 2.50k | if (m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()) != OGRERR_NONE) |
7216 | 691 | { |
7217 | 691 | m_oSRS.Clear(); |
7218 | 691 | return FALSE; |
7219 | 691 | } |
7220 | 2.50k | } |
7221 | | |
7222 | | /* -------------------------------------------------------------------- */ |
7223 | | /* Compute geotransform */ |
7224 | | /* -------------------------------------------------------------------- */ |
7225 | 3.62k | OGRSpatialReference *poSRSGeog = m_oSRS.CloneGeogCS(); |
7226 | | |
7227 | | /* Files found at |
7228 | | * http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ |
7229 | | */ |
7230 | | /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, |
7231 | | * long) as required by the */ |
7232 | | /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is |
7233 | | * able to understand that, */ |
7234 | | /* so let's also try to do it with a heuristics. */ |
7235 | | |
7236 | 3.62k | bool bReproject = true; |
7237 | 3.62k | if (m_oSRS.IsProjected()) |
7238 | 2.62k | { |
7239 | 12.9k | for (int i = 0; i < nGPTSLength / 2; i++) |
7240 | 10.4k | { |
7241 | 10.4k | if (fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361) |
7242 | 69 | { |
7243 | 69 | CPLDebug("PDF", "GPTS coordinates seems to be in (northing, " |
7244 | 69 | "easting), which is non-standard"); |
7245 | 69 | bReproject = false; |
7246 | 69 | break; |
7247 | 69 | } |
7248 | 10.4k | } |
7249 | 2.62k | } |
7250 | | |
7251 | 3.62k | OGRCoordinateTransformation *poCT = nullptr; |
7252 | 3.62k | if (bReproject) |
7253 | 3.56k | { |
7254 | 3.56k | poCT = OGRCreateCoordinateTransformation(poSRSGeog, &m_oSRS); |
7255 | 3.56k | if (poCT == nullptr) |
7256 | 7 | { |
7257 | 7 | delete poSRSGeog; |
7258 | 7 | m_oSRS.Clear(); |
7259 | 7 | return FALSE; |
7260 | 7 | } |
7261 | 3.56k | } |
7262 | | |
7263 | 3.62k | std::vector<GDAL_GCP> asGCPS(nGPTSLength / 2); |
7264 | | |
7265 | | /* Create NEATLINE */ |
7266 | 3.62k | OGRLinearRing *poRing = nullptr; |
7267 | 3.62k | if (nGPTSLength == 8) |
7268 | 3.62k | { |
7269 | 3.62k | m_poNeatLine = new OGRPolygon(); |
7270 | 3.62k | poRing = new OGRLinearRing(); |
7271 | 3.62k | m_poNeatLine->addRingDirectly(poRing); |
7272 | 3.62k | } |
7273 | | |
7274 | 16.5k | for (int i = 0; i < nGPTSLength / 2; i++) |
7275 | 13.3k | { |
7276 | | /* We probably assume LPTS is 0 or 1 */ |
7277 | 13.3k | asGCPS[i].dfGCPPixel = |
7278 | 13.3k | (dfULX * (1 - adfLPTS[2 * i + 0]) + dfLRX * adfLPTS[2 * i + 0]) / |
7279 | 13.3k | dfMediaBoxWidth * nRasterXSize; |
7280 | 13.3k | asGCPS[i].dfGCPLine = |
7281 | 13.3k | (dfULY * (1 - adfLPTS[2 * i + 1]) + dfLRY * adfLPTS[2 * i + 1]) / |
7282 | 13.3k | dfMediaBoxHeight * nRasterYSize; |
7283 | | |
7284 | 13.3k | double lat = adfGPTS[2 * i]; |
7285 | 13.3k | double lon = adfGPTS[2 * i + 1]; |
7286 | 13.3k | double x = lon; |
7287 | 13.3k | double y = lat; |
7288 | 13.3k | if (bReproject) |
7289 | 13.0k | { |
7290 | 13.0k | if (!poCT->Transform(1, &x, &y, nullptr)) |
7291 | 379 | { |
7292 | 379 | CPLError(CE_Failure, CPLE_AppDefined, |
7293 | 379 | "Cannot reproject (%f, %f)", lon, lat); |
7294 | 379 | delete poSRSGeog; |
7295 | 379 | delete poCT; |
7296 | 379 | m_oSRS.Clear(); |
7297 | 379 | return FALSE; |
7298 | 379 | } |
7299 | 13.0k | } |
7300 | | |
7301 | 12.9k | x = ROUND_IF_CLOSE(x); |
7302 | 12.9k | y = ROUND_IF_CLOSE(y); |
7303 | | |
7304 | 12.9k | asGCPS[i].dfGCPX = x; |
7305 | 12.9k | asGCPS[i].dfGCPY = y; |
7306 | | |
7307 | 12.9k | if (poRing) |
7308 | 12.9k | poRing->addPoint(x, y); |
7309 | 12.9k | } |
7310 | | |
7311 | 3.24k | delete poSRSGeog; |
7312 | 3.24k | delete poCT; |
7313 | | |
7314 | 3.24k | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7315 | 3.24k | FALSE)) |
7316 | 1.81k | { |
7317 | 1.81k | CPLDebug("PDF", |
7318 | 1.81k | "Could not compute GT with exact match. Try with approximate"); |
7319 | 1.81k | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7320 | 1.81k | TRUE)) |
7321 | 694 | { |
7322 | 694 | CPLError(CE_Failure, CPLE_AppDefined, |
7323 | 694 | "Could not compute GT with approximate match."); |
7324 | 694 | return FALSE; |
7325 | 694 | } |
7326 | 1.81k | } |
7327 | 2.54k | m_bGeoTransformValid = true; |
7328 | | |
7329 | | // If the non scaling terms of the geotransform are significantly smaller |
7330 | | // than the pixel size, then nullify them as being just artifacts of |
7331 | | // reprojection and GDALGCPsToGeoTransform() numerical imprecisions. |
7332 | 2.54k | const double dfPixelSize = std::min(fabs(m_gt.xscale), fabs(m_gt.yscale)); |
7333 | 2.54k | const double dfRotationShearTerm = |
7334 | 2.54k | std::max(fabs(m_gt.xrot), fabs(m_gt.yrot)); |
7335 | 2.54k | if (dfRotationShearTerm < 1e-5 * dfPixelSize || |
7336 | 1.28k | (m_bUseLib.test(PDFLIB_PDFIUM) && |
7337 | 0 | std::min(fabs(m_gt.xrot), fabs(m_gt.yrot)) < 1e-5 * dfPixelSize)) |
7338 | 1.26k | { |
7339 | 1.26k | dfLRX = |
7340 | 1.26k | m_gt.xorig + nRasterXSize * m_gt.xscale + nRasterYSize * m_gt.xrot; |
7341 | 1.26k | dfLRY = |
7342 | 1.26k | m_gt.yorig + nRasterXSize * m_gt.yrot + nRasterYSize * m_gt.yscale; |
7343 | 1.26k | m_gt.xscale = (dfLRX - m_gt.xorig) / nRasterXSize; |
7344 | 1.26k | m_gt.yscale = (dfLRY - m_gt.yorig) / nRasterYSize; |
7345 | 1.26k | m_gt.xrot = m_gt.yrot = 0; |
7346 | 1.26k | } |
7347 | | |
7348 | 2.54k | return TRUE; |
7349 | 3.24k | } |
7350 | | |
7351 | | /************************************************************************/ |
7352 | | /* GetSpatialRef() */ |
7353 | | /************************************************************************/ |
7354 | | |
7355 | | const OGRSpatialReference *PDFDataset::GetSpatialRef() const |
7356 | 64.4k | { |
7357 | 64.4k | const auto poSRS = GDALPamDataset::GetSpatialRef(); |
7358 | 64.4k | if (poSRS) |
7359 | 906 | return poSRS; |
7360 | | |
7361 | 63.5k | if (!m_oSRS.IsEmpty() && m_bGeoTransformValid) |
7362 | 990 | return &m_oSRS; |
7363 | 62.5k | return nullptr; |
7364 | 63.5k | } |
7365 | | |
7366 | | /************************************************************************/ |
7367 | | /* GetGeoTransform() */ |
7368 | | /************************************************************************/ |
7369 | | |
7370 | | CPLErr PDFDataset::GetGeoTransform(GDALGeoTransform >) const |
7371 | | |
7372 | 7.88k | { |
7373 | 7.88k | if (GDALPamDataset::GetGeoTransform(gt) == CE_None) |
7374 | 0 | { |
7375 | 0 | return CE_None; |
7376 | 0 | } |
7377 | | |
7378 | 7.88k | gt = m_gt; |
7379 | 7.88k | return ((m_bGeoTransformValid) ? CE_None : CE_Failure); |
7380 | 7.88k | } |
7381 | | |
7382 | | /************************************************************************/ |
7383 | | /* SetSpatialRef() */ |
7384 | | /************************************************************************/ |
7385 | | |
7386 | | CPLErr PDFDataset::SetSpatialRef(const OGRSpatialReference *poSRS) |
7387 | 0 | { |
7388 | 0 | if (eAccess == GA_ReadOnly) |
7389 | 0 | GDALPamDataset::SetSpatialRef(poSRS); |
7390 | |
|
7391 | 0 | m_oSRS.Clear(); |
7392 | 0 | if (poSRS) |
7393 | 0 | m_oSRS = *poSRS; |
7394 | 0 | m_bProjDirty = true; |
7395 | 0 | return CE_None; |
7396 | 0 | } |
7397 | | |
7398 | | /************************************************************************/ |
7399 | | /* SetGeoTransform() */ |
7400 | | /************************************************************************/ |
7401 | | |
7402 | | CPLErr PDFDataset::SetGeoTransform(const GDALGeoTransform >) |
7403 | 0 | { |
7404 | 0 | if (eAccess == GA_ReadOnly) |
7405 | 0 | GDALPamDataset::SetGeoTransform(gt); |
7406 | |
|
7407 | 0 | m_gt = gt; |
7408 | 0 | m_bGeoTransformValid = true; |
7409 | 0 | m_bProjDirty = true; |
7410 | | |
7411 | | /* Reset NEATLINE if not explicitly set by the user */ |
7412 | 0 | if (!m_bNeatLineDirty) |
7413 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7414 | 0 | return CE_None; |
7415 | 0 | } |
7416 | | |
7417 | | /************************************************************************/ |
7418 | | /* GetMetadataDomainList() */ |
7419 | | /************************************************************************/ |
7420 | | |
7421 | | char **PDFDataset::GetMetadataDomainList() |
7422 | 0 | { |
7423 | 0 | return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), |
7424 | 0 | TRUE, "xml:XMP", "LAYERS", |
7425 | 0 | "EMBEDDED_METADATA", nullptr); |
7426 | 0 | } |
7427 | | |
7428 | | /************************************************************************/ |
7429 | | /* GetMetadata() */ |
7430 | | /************************************************************************/ |
7431 | | |
7432 | | CSLConstList PDFDataset::GetMetadata(const char *pszDomain) |
7433 | 63.6k | { |
7434 | 63.6k | if (pszDomain != nullptr && EQUAL(pszDomain, "EMBEDDED_METADATA")) |
7435 | 0 | { |
7436 | 0 | char **papszRet = m_oMDMD_PDF.GetMetadata(pszDomain); |
7437 | 0 | if (papszRet) |
7438 | 0 | return papszRet; |
7439 | | |
7440 | 0 | GDALPDFObject *poCatalog = GetCatalog(); |
7441 | 0 | if (poCatalog == nullptr) |
7442 | 0 | return nullptr; |
7443 | 0 | GDALPDFObject *poFirstElt = |
7444 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]"); |
7445 | 0 | GDALPDFObject *poF = |
7446 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F"); |
7447 | |
|
7448 | 0 | if (poFirstElt == nullptr || |
7449 | 0 | poFirstElt->GetType() != PDFObjectType_String || |
7450 | 0 | poFirstElt->GetString() != "Metadata") |
7451 | 0 | return nullptr; |
7452 | 0 | if (poF == nullptr || poF->GetType() != PDFObjectType_Dictionary) |
7453 | 0 | return nullptr; |
7454 | 0 | GDALPDFStream *poStream = poF->GetStream(); |
7455 | 0 | if (poStream == nullptr) |
7456 | 0 | return nullptr; |
7457 | | |
7458 | 0 | char *apszMetadata[2] = {nullptr, nullptr}; |
7459 | 0 | apszMetadata[0] = poStream->GetBytes(); |
7460 | 0 | m_oMDMD_PDF.SetMetadata(apszMetadata, pszDomain); |
7461 | 0 | VSIFree(apszMetadata[0]); |
7462 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7463 | 0 | } |
7464 | 63.6k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7465 | 23.6k | { |
7466 | 23.6k | CSLConstList papszPAMMD = GDALPamDataset::GetMetadata(pszDomain); |
7467 | 113k | for (CSLConstList papszIter = papszPAMMD; papszIter && *papszIter; |
7468 | 90.1k | ++papszIter) |
7469 | 90.1k | { |
7470 | 90.1k | char *pszKey = nullptr; |
7471 | 90.1k | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7472 | 90.1k | if (pszKey && pszValue) |
7473 | 90.1k | { |
7474 | 90.1k | if (m_oMDMD_PDF.GetMetadataItem(pszKey, pszDomain) == nullptr) |
7475 | 30.0k | m_oMDMD_PDF.SetMetadataItem(pszKey, pszValue, pszDomain); |
7476 | 90.1k | } |
7477 | 90.1k | CPLFree(pszKey); |
7478 | 90.1k | } |
7479 | 23.6k | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7480 | 23.6k | } |
7481 | 39.9k | if (EQUAL(pszDomain, "LAYERS") || EQUAL(pszDomain, "xml:XMP") || |
7482 | 39.9k | EQUAL(pszDomain, GDAL_MDD_SUBDATASETS)) |
7483 | 0 | { |
7484 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7485 | 0 | } |
7486 | 39.9k | return GDALPamDataset::GetMetadata(pszDomain); |
7487 | 39.9k | } |
7488 | | |
7489 | | /************************************************************************/ |
7490 | | /* SetMetadata() */ |
7491 | | /************************************************************************/ |
7492 | | |
7493 | | CPLErr PDFDataset::SetMetadata(CSLConstList papszMetadata, |
7494 | | const char *pszDomain) |
7495 | 9.94k | { |
7496 | 9.94k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7497 | 65 | { |
7498 | 65 | char **papszMetadataDup = CSLDuplicate(papszMetadata); |
7499 | 65 | m_oMDMD_PDF.SetMetadata(nullptr, pszDomain); |
7500 | | |
7501 | 398 | for (char **papszIter = papszMetadataDup; papszIter && *papszIter; |
7502 | 333 | ++papszIter) |
7503 | 333 | { |
7504 | 333 | char *pszKey = nullptr; |
7505 | 333 | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7506 | 333 | if (pszKey && pszValue) |
7507 | 333 | { |
7508 | 333 | SetMetadataItem(pszKey, pszValue, pszDomain); |
7509 | 333 | } |
7510 | 333 | CPLFree(pszKey); |
7511 | 333 | } |
7512 | 65 | CSLDestroy(papszMetadataDup); |
7513 | 65 | return CE_None; |
7514 | 65 | } |
7515 | 9.87k | else if (EQUAL(pszDomain, "xml:XMP")) |
7516 | 5.81k | { |
7517 | 5.81k | m_bXMPDirty = true; |
7518 | 5.81k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7519 | 5.81k | } |
7520 | 4.06k | else if (EQUAL(pszDomain, GDAL_MDD_SUBDATASETS)) |
7521 | 4.06k | { |
7522 | 4.06k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7523 | 4.06k | } |
7524 | 0 | else |
7525 | 0 | { |
7526 | 0 | return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); |
7527 | 0 | } |
7528 | 9.94k | } |
7529 | | |
7530 | | /************************************************************************/ |
7531 | | /* GetMetadataItem() */ |
7532 | | /************************************************************************/ |
7533 | | |
7534 | | const char *PDFDataset::GetMetadataItem(const char *pszName, |
7535 | | const char *pszDomain) |
7536 | 55.6k | { |
7537 | 55.6k | if (pszDomain != nullptr && EQUAL(pszDomain, "_INTERNAL_") && |
7538 | 0 | pszName != nullptr && EQUAL(pszName, "PDF_LIB")) |
7539 | 0 | { |
7540 | 0 | if (m_bUseLib.test(PDFLIB_POPPLER)) |
7541 | 0 | return "POPPLER"; |
7542 | 0 | if (m_bUseLib.test(PDFLIB_PODOFO)) |
7543 | 0 | return "PODOFO"; |
7544 | 0 | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
7545 | 0 | return "PDFIUM"; |
7546 | 0 | } |
7547 | 55.6k | return CSLFetchNameValue(GetMetadata(pszDomain), pszName); |
7548 | 55.6k | } |
7549 | | |
7550 | | /************************************************************************/ |
7551 | | /* SetMetadataItem() */ |
7552 | | /************************************************************************/ |
7553 | | |
7554 | | CPLErr PDFDataset::SetMetadataItem(const char *pszName, const char *pszValue, |
7555 | | const char *pszDomain) |
7556 | 133k | { |
7557 | 133k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7558 | 72.2k | { |
7559 | 72.2k | if (EQUAL(pszName, "NEATLINE")) |
7560 | 4.00k | { |
7561 | 4.00k | const char *pszOldValue = |
7562 | 4.00k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7563 | 4.00k | if ((pszValue == nullptr && pszOldValue != nullptr) || |
7564 | 4.00k | (pszValue != nullptr && pszOldValue == nullptr) || |
7565 | 0 | (pszValue != nullptr && pszOldValue != nullptr && |
7566 | 0 | strcmp(pszValue, pszOldValue) != 0)) |
7567 | 4.00k | { |
7568 | 4.00k | m_bProjDirty = true; |
7569 | 4.00k | m_bNeatLineDirty = true; |
7570 | 4.00k | } |
7571 | 4.00k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7572 | 4.00k | } |
7573 | 68.2k | else |
7574 | 68.2k | { |
7575 | 68.2k | if (EQUAL(pszName, "AUTHOR") || EQUAL(pszName, "PRODUCER") || |
7576 | 58.3k | EQUAL(pszName, "CREATOR") || EQUAL(pszName, "CREATION_DATE") || |
7577 | 44.7k | EQUAL(pszName, "SUBJECT") || EQUAL(pszName, "TITLE") || |
7578 | 39.9k | EQUAL(pszName, "KEYWORDS")) |
7579 | 29.3k | { |
7580 | 29.3k | if (pszValue == nullptr) |
7581 | 0 | pszValue = ""; |
7582 | 29.3k | const char *pszOldValue = |
7583 | 29.3k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7584 | 29.3k | if (pszOldValue == nullptr || |
7585 | 0 | strcmp(pszValue, pszOldValue) != 0) |
7586 | 29.3k | { |
7587 | 29.3k | m_bInfoDirty = true; |
7588 | 29.3k | } |
7589 | 29.3k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7590 | 29.3k | pszDomain); |
7591 | 29.3k | } |
7592 | 38.8k | else if (EQUAL(pszName, "DPI")) |
7593 | 38.6k | { |
7594 | 38.6k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7595 | 38.6k | pszDomain); |
7596 | 38.6k | } |
7597 | 249 | else |
7598 | 249 | { |
7599 | 249 | m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7600 | 249 | return GDALPamDataset::SetMetadataItem(pszName, pszValue, |
7601 | 249 | pszDomain); |
7602 | 249 | } |
7603 | 68.2k | } |
7604 | 72.2k | } |
7605 | 60.8k | else if (EQUAL(pszDomain, "xml:XMP")) |
7606 | 0 | { |
7607 | 0 | m_bXMPDirty = true; |
7608 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7609 | 0 | } |
7610 | 60.8k | else if (EQUAL(pszDomain, GDAL_MDD_SUBDATASETS)) |
7611 | 0 | { |
7612 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7613 | 0 | } |
7614 | 60.8k | else |
7615 | 60.8k | { |
7616 | 60.8k | return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); |
7617 | 60.8k | } |
7618 | 133k | } |
7619 | | |
7620 | | /************************************************************************/ |
7621 | | /* GetGCPCount() */ |
7622 | | /************************************************************************/ |
7623 | | |
7624 | | int PDFDataset::GetGCPCount() |
7625 | 7.86k | { |
7626 | 7.86k | return m_nGCPCount; |
7627 | 7.86k | } |
7628 | | |
7629 | | /************************************************************************/ |
7630 | | /* GetGCPSpatialRef() */ |
7631 | | /************************************************************************/ |
7632 | | |
7633 | | const OGRSpatialReference *PDFDataset::GetGCPSpatialRef() const |
7634 | 7.86k | { |
7635 | 7.86k | if (!m_oSRS.IsEmpty() && m_nGCPCount != 0) |
7636 | 3 | return &m_oSRS; |
7637 | 7.86k | return nullptr; |
7638 | 7.86k | } |
7639 | | |
7640 | | /************************************************************************/ |
7641 | | /* GetGCPs() */ |
7642 | | /************************************************************************/ |
7643 | | |
7644 | | const GDAL_GCP *PDFDataset::GetGCPs() |
7645 | 7.86k | { |
7646 | 7.86k | return m_pasGCPList; |
7647 | 7.86k | } |
7648 | | |
7649 | | /************************************************************************/ |
7650 | | /* SetGCPs() */ |
7651 | | /************************************************************************/ |
7652 | | |
7653 | | CPLErr PDFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn, |
7654 | | const OGRSpatialReference *poSRS) |
7655 | 0 | { |
7656 | 0 | const char *pszGEO_ENCODING = |
7657 | 0 | CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); |
7658 | 0 | if (nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000")) |
7659 | 0 | { |
7660 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
7661 | 0 | "PDF driver only supports writing 4 GCPs when " |
7662 | 0 | "GDAL_PDF_GEO_ENCODING=ISO32000."); |
7663 | 0 | return CE_Failure; |
7664 | 0 | } |
7665 | | |
7666 | | /* Free previous GCPs */ |
7667 | 0 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
7668 | 0 | CPLFree(m_pasGCPList); |
7669 | | |
7670 | | /* Duplicate in GCPs */ |
7671 | 0 | m_nGCPCount = nGCPCountIn; |
7672 | 0 | m_pasGCPList = GDALDuplicateGCPs(m_nGCPCount, pasGCPListIn); |
7673 | |
|
7674 | 0 | m_oSRS.Clear(); |
7675 | 0 | if (poSRS) |
7676 | 0 | m_oSRS = *poSRS; |
7677 | |
|
7678 | 0 | m_bProjDirty = true; |
7679 | | |
7680 | | /* Reset NEATLINE if not explicitly set by the user */ |
7681 | 0 | if (!m_bNeatLineDirty) |
7682 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7683 | |
|
7684 | 0 | return CE_None; |
7685 | 0 | } |
7686 | | |
7687 | | #endif // #ifdef HAVE_PDF_READ_SUPPORT |
7688 | | |
7689 | | /************************************************************************/ |
7690 | | /* GDALPDFOpen() */ |
7691 | | /************************************************************************/ |
7692 | | |
7693 | | GDALDataset *GDALPDFOpen( |
7694 | | #ifdef HAVE_PDF_READ_SUPPORT |
7695 | | const char *pszFilename, GDALAccess eAccess |
7696 | | #else |
7697 | | CPL_UNUSED const char *pszFilename, CPL_UNUSED GDALAccess eAccess |
7698 | | #endif |
7699 | | ) |
7700 | 65 | { |
7701 | 65 | #ifdef HAVE_PDF_READ_SUPPORT |
7702 | 65 | GDALOpenInfo oOpenInfo(pszFilename, eAccess); |
7703 | 65 | return PDFDataset::Open(&oOpenInfo); |
7704 | | #else |
7705 | | return nullptr; |
7706 | | #endif |
7707 | 65 | } |
7708 | | |
7709 | | /************************************************************************/ |
7710 | | /* GDALPDFUnloadDriver() */ |
7711 | | /************************************************************************/ |
7712 | | |
7713 | | static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver *poDriver) |
7714 | 0 | { |
7715 | 0 | #ifdef HAVE_POPPLER |
7716 | 0 | if (hGlobalParamsMutex != nullptr) |
7717 | 0 | CPLDestroyMutex(hGlobalParamsMutex); |
7718 | 0 | #endif |
7719 | | #ifdef HAVE_PDFIUM |
7720 | | if (PDFDataset::g_bPdfiumInit) |
7721 | | { |
7722 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
7723 | | // Destroy every loaded document or page |
7724 | | TMapPdfiumDatasets::iterator itDoc; |
7725 | | TMapPdfiumPages::iterator itPage; |
7726 | | for (itDoc = g_mPdfiumDatasets.begin(); |
7727 | | itDoc != g_mPdfiumDatasets.end(); ++itDoc) |
7728 | | { |
7729 | | TPdfiumDocumentStruct *pDoc = itDoc->second; |
7730 | | for (itPage = pDoc->pages.begin(); itPage != pDoc->pages.end(); |
7731 | | ++itPage) |
7732 | | { |
7733 | | TPdfiumPageStruct *pPage = itPage->second; |
7734 | | |
7735 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, |
7736 | | PDFIUM_MUTEX_TIMEOUT); |
7737 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), |
7738 | | PDFIUM_MUTEX_TIMEOUT); |
7739 | | CPLReleaseMutex(pPage->readMutex); |
7740 | | CPLDestroyMutex(pPage->readMutex); |
7741 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
7742 | | delete pPage; |
7743 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
7744 | | } // ~ foreach page |
7745 | | |
7746 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
7747 | | CPLFree(pDoc->filename); |
7748 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
7749 | | delete pDoc->psFileAccess; |
7750 | | pDoc->pages.clear(); |
7751 | | |
7752 | | delete pDoc; |
7753 | | } // ~ foreach document |
7754 | | g_mPdfiumDatasets.clear(); |
7755 | | FPDF_DestroyLibrary(); |
7756 | | PDFDataset::g_bPdfiumInit = FALSE; |
7757 | | |
7758 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
7759 | | |
7760 | | if (g_oPdfiumReadMutex) |
7761 | | CPLDestroyMutex(g_oPdfiumReadMutex); |
7762 | | CPLDestroyMutex(g_oPdfiumLoadDocMutex); |
7763 | | } |
7764 | | #endif |
7765 | 0 | } |
7766 | | |
7767 | | /************************************************************************/ |
7768 | | /* PDFSanitizeLayerName() */ |
7769 | | /************************************************************************/ |
7770 | | |
7771 | | CPLString PDFSanitizeLayerName(const char *pszName) |
7772 | 1.93M | { |
7773 | 1.93M | if (!CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES"))) |
7774 | 0 | return pszName; |
7775 | | |
7776 | 1.93M | CPLString osName; |
7777 | 129M | for (int i = 0; pszName[i] != '\0'; i++) |
7778 | 127M | { |
7779 | 127M | if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',') |
7780 | 12.9M | osName += "_"; |
7781 | 114M | else if (pszName[i] != '"') |
7782 | 114M | osName += pszName[i]; |
7783 | 127M | } |
7784 | 1.93M | if (osName.empty()) |
7785 | 633 | osName = "unnamed"; |
7786 | 1.93M | return osName; |
7787 | 1.93M | } |
7788 | | |
7789 | | /************************************************************************/ |
7790 | | /* GDALPDFListLayersAlgorithm */ |
7791 | | /************************************************************************/ |
7792 | | |
7793 | | #ifdef HAVE_PDF_READ_SUPPORT |
7794 | | |
7795 | | class GDALPDFListLayersAlgorithm final : public GDALAlgorithm |
7796 | | { |
7797 | | public: |
7798 | | GDALPDFListLayersAlgorithm() |
7799 | 0 | : GDALAlgorithm("list-layers", |
7800 | 0 | std::string("List layers of a PDF dataset"), |
7801 | 0 | "/drivers/raster/pdf.html") |
7802 | 0 | { |
7803 | 0 | AddProgressArg(/* hidden = */ true); |
7804 | 0 | AddInputDatasetArg(&m_dataset, GDAL_OF_RASTER | GDAL_OF_VECTOR); |
7805 | 0 | AddOutputFormatArg(&m_format).SetDefault(m_format).SetChoices("json", |
7806 | 0 | "text"); |
7807 | 0 | AddOutputStringArg(&m_output); |
7808 | 0 | } |
7809 | | |
7810 | | protected: |
7811 | | bool RunImpl(GDALProgressFunc, void *) override; |
7812 | | |
7813 | | private: |
7814 | | GDALArgDatasetValue m_dataset{}; |
7815 | | std::string m_format = "json"; |
7816 | | std::string m_output{}; |
7817 | | }; |
7818 | | |
7819 | | bool GDALPDFListLayersAlgorithm::RunImpl(GDALProgressFunc, void *) |
7820 | 0 | { |
7821 | 0 | auto poDS = dynamic_cast<PDFDataset *>(m_dataset.GetDatasetRef()); |
7822 | 0 | if (!poDS) |
7823 | 0 | { |
7824 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "%s is not a PDF", |
7825 | 0 | m_dataset.GetName().c_str()); |
7826 | 0 | return false; |
7827 | 0 | } |
7828 | 0 | if (m_format == "json") |
7829 | 0 | { |
7830 | 0 | CPLJSonStreamingWriter oWriter(nullptr, nullptr); |
7831 | 0 | oWriter.StartArray(); |
7832 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7833 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7834 | 0 | { |
7835 | 0 | CPL_IGNORE_RET_VAL(key); |
7836 | 0 | oWriter.Add(value); |
7837 | 0 | } |
7838 | 0 | oWriter.EndArray(); |
7839 | 0 | m_output = oWriter.GetString(); |
7840 | 0 | m_output += '\n'; |
7841 | 0 | } |
7842 | 0 | else |
7843 | 0 | { |
7844 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7845 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7846 | 0 | { |
7847 | 0 | CPL_IGNORE_RET_VAL(key); |
7848 | 0 | m_output += value; |
7849 | 0 | m_output += '\n'; |
7850 | 0 | } |
7851 | 0 | } |
7852 | 0 | return true; |
7853 | 0 | } |
7854 | | |
7855 | | /************************************************************************/ |
7856 | | /* GDALPDFInstantiateAlgorithm() */ |
7857 | | /************************************************************************/ |
7858 | | |
7859 | | static GDALAlgorithm * |
7860 | | GDALPDFInstantiateAlgorithm(const std::vector<std::string> &aosPath) |
7861 | 0 | { |
7862 | 0 | if (aosPath.size() == 1 && aosPath[0] == "list-layers") |
7863 | 0 | { |
7864 | 0 | return std::make_unique<GDALPDFListLayersAlgorithm>().release(); |
7865 | 0 | } |
7866 | 0 | else |
7867 | 0 | { |
7868 | 0 | return nullptr; |
7869 | 0 | } |
7870 | 0 | } |
7871 | | |
7872 | | #endif // HAVE_PDF_READ_SUPPORT |
7873 | | |
7874 | | /************************************************************************/ |
7875 | | /* GDALRegister_PDF() */ |
7876 | | /************************************************************************/ |
7877 | | |
7878 | | void GDALRegister_PDF() |
7879 | | |
7880 | 22 | { |
7881 | 22 | if (!GDAL_CHECK_VERSION("PDF driver")) |
7882 | 0 | return; |
7883 | | |
7884 | 22 | if (GDALGetDriverByName(DRIVER_NAME) != nullptr) |
7885 | 0 | return; |
7886 | | |
7887 | 22 | GDALDriver *poDriver = new GDALDriver(); |
7888 | 22 | PDFDriverSetCommonMetadata(poDriver); |
7889 | | |
7890 | 22 | #ifdef HAVE_PDF_READ_SUPPORT |
7891 | 22 | poDriver->pfnOpen = PDFDataset::OpenWrapper; |
7892 | 22 | poDriver->pfnInstantiateAlgorithm = GDALPDFInstantiateAlgorithm; |
7893 | 22 | #endif // HAVE_PDF_READ_SUPPORT |
7894 | | |
7895 | 22 | poDriver->pfnCreateCopy = GDALPDFCreateCopy; |
7896 | 22 | poDriver->pfnCreate = PDFWritableVectorDataset::Create; |
7897 | 22 | poDriver->pfnUnloadDriver = GDALPDFUnloadDriver; |
7898 | | |
7899 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
7900 | 22 | } |