/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.32k | : SplashOutputDev(colorModeA, bitmapRowPadA, |
101 | 7.32k | #if POPPLER_MAJOR_VERSION < 26 || \ |
102 | 7.32k | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION < 2) |
103 | 7.32k | reverseVideoA, |
104 | 7.32k | #endif |
105 | 7.32k | paperColorA), |
106 | 7.32k | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE) |
107 | 7.32k | { |
108 | 7.32k | } |
109 | | |
110 | | void SetEnableVector(int bFlag) |
111 | 14.6k | { |
112 | 14.6k | bEnableVector = bFlag; |
113 | 14.6k | } |
114 | | |
115 | | void SetEnableText(int bFlag) |
116 | 7.32k | { |
117 | 7.32k | bEnableText = bFlag; |
118 | 7.32k | } |
119 | | |
120 | | void SetEnableBitmap(int bFlag) |
121 | 14.6k | { |
122 | 14.6k | bEnableBitmap = bFlag; |
123 | 14.6k | } |
124 | | |
125 | | void startPage(int pageNum, GfxState *state, XRef *xrefIn) override; |
126 | | |
127 | | void stroke(GfxState *state) override |
128 | 1.12M | { |
129 | 1.12M | if (bEnableVector) |
130 | 1.12M | SplashOutputDev::stroke(state); |
131 | 1.12M | } |
132 | | |
133 | | void fill(GfxState *state) override |
134 | 5.82M | { |
135 | 5.82M | if (bEnableVector) |
136 | 5.82M | SplashOutputDev::fill(state); |
137 | 5.82M | } |
138 | | |
139 | | void eoFill(GfxState *state) override |
140 | 4.03k | { |
141 | 4.03k | if (bEnableVector) |
142 | 4.03k | SplashOutputDev::eoFill(state); |
143 | 4.03k | } |
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.55M | { |
150 | 1.55M | if (bEnableText) |
151 | 0 | SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY, |
152 | 0 | code, nBytes, u, uLen); |
153 | 1.55M | } |
154 | | |
155 | | void beginTextObject(GfxState *state) override |
156 | 150k | { |
157 | 150k | if (bEnableText) |
158 | 0 | SplashOutputDev::beginTextObject(state); |
159 | 150k | } |
160 | | |
161 | | void endTextObject(GfxState *state) override |
162 | 155k | { |
163 | 155k | if (bEnableText) |
164 | 0 | SplashOutputDev::endTextObject(state); |
165 | 155k | } |
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 | 30.5k | { |
171 | 30.5k | if (bEnableBitmap) |
172 | 30.5k | SplashOutputDev::drawImageMask(state, ref, str, width, height, |
173 | 30.5k | 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 | 30.5k | } |
184 | | |
185 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
186 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
187 | | void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
188 | | int width, int height, bool invert, |
189 | | bool inlineImg, |
190 | | std::array<double, 6> &baseMatrix) override |
191 | | #else |
192 | | void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
193 | | int width, int height, bool invert, |
194 | | bool inlineImg, double *baseMatrix) override |
195 | | #endif |
196 | 1.96M | { |
197 | 1.96M | if (bEnableBitmap) |
198 | 1.96M | SplashOutputDev::setSoftMaskFromImageMask( |
199 | 1.96M | state, ref, str, width, height, invert, inlineImg, baseMatrix); |
200 | 0 | else |
201 | 0 | str->close(); |
202 | 1.96M | } |
203 | | |
204 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
205 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
206 | | void unsetSoftMaskFromImageMask(GfxState *state, |
207 | | std::array<double, 6> &baseMatrix) override |
208 | | #else |
209 | | void unsetSoftMaskFromImageMask(GfxState *state, |
210 | | double *baseMatrix) override |
211 | | #endif |
212 | 1.96M | { |
213 | 1.96M | if (bEnableBitmap) |
214 | 1.96M | SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix); |
215 | 1.96M | } |
216 | | |
217 | | virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, |
218 | | int height, GfxImageColorMap *colorMap, |
219 | | bool interpolate, const int *maskColors, |
220 | | bool inlineImg) override |
221 | 3.54k | { |
222 | 3.54k | if (bEnableBitmap) |
223 | 3.54k | SplashOutputDev::drawImage(state, ref, str, width, height, colorMap, |
224 | 3.54k | interpolate, maskColors, inlineImg); |
225 | 0 | else |
226 | 0 | { |
227 | 0 | VSIPDFFileStream::resetNoCheckReturnValue(str); |
228 | 0 | if (inlineImg) |
229 | 0 | { |
230 | 0 | skipBytes(str, width, height, colorMap->getNumPixelComps(), |
231 | 0 | colorMap->getBits()); |
232 | 0 | } |
233 | 0 | str->close(); |
234 | 0 | } |
235 | 3.54k | } |
236 | | |
237 | | virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, |
238 | | int width, int height, |
239 | | GfxImageColorMap *colorMap, bool interpolate, |
240 | | Stream *maskStr, int maskWidth, int maskHeight, |
241 | | bool maskInvert, bool maskInterpolate) override |
242 | 81 | { |
243 | 81 | if (bEnableBitmap) |
244 | 81 | SplashOutputDev::drawMaskedImage( |
245 | 81 | state, ref, str, width, height, colorMap, interpolate, maskStr, |
246 | 81 | maskWidth, maskHeight, maskInvert, maskInterpolate); |
247 | 0 | else |
248 | 0 | str->close(); |
249 | 81 | } |
250 | | |
251 | | virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, |
252 | | int width, int height, |
253 | | GfxImageColorMap *colorMap, |
254 | | bool interpolate, Stream *maskStr, |
255 | | int maskWidth, int maskHeight, |
256 | | GfxImageColorMap *maskColorMap, |
257 | | bool maskInterpolate) override |
258 | 3.37k | { |
259 | 3.37k | if (bEnableBitmap) |
260 | 3.37k | { |
261 | 3.37k | if (maskColorMap->getBits() <= |
262 | 3.37k | 0) /* workaround poppler bug (robustness) */ |
263 | 0 | { |
264 | 0 | str->close(); |
265 | 0 | return; |
266 | 0 | } |
267 | 3.37k | SplashOutputDev::drawSoftMaskedImage( |
268 | 3.37k | state, ref, str, width, height, colorMap, interpolate, maskStr, |
269 | 3.37k | maskWidth, maskHeight, maskColorMap, maskInterpolate); |
270 | 3.37k | } |
271 | 0 | else |
272 | 0 | str->close(); |
273 | 3.37k | } |
274 | | }; |
275 | | |
276 | | void GDALPDFOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefIn) |
277 | 7.32k | { |
278 | 7.32k | SplashOutputDev::startPage(pageNum, state, xrefIn); |
279 | 7.32k | SplashBitmap *poBitmap = getBitmap(); |
280 | 7.32k | memset(poBitmap->getDataPtr(), 255, |
281 | 7.32k | static_cast<size_t>(poBitmap->getRowSize()) * poBitmap->getHeight()); |
282 | 7.32k | } |
283 | | |
284 | | #endif // ~ HAVE_POPPLER |
285 | | |
286 | | /************************************************************************/ |
287 | | /* Dump routines */ |
288 | | /************************************************************************/ |
289 | | |
290 | | class GDALPDFDumper |
291 | | { |
292 | | private: |
293 | | FILE *f = nullptr; |
294 | | const int nDepthLimit; |
295 | | std::set<int> aoSetObjectExplored{}; |
296 | | const bool bDumpParent; |
297 | | |
298 | | void DumpSimplified(GDALPDFObject *poObj); |
299 | | |
300 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFDumper) |
301 | | |
302 | | public: |
303 | | GDALPDFDumper(const char *pszFilename, const char *pszDumpFile, |
304 | | int nDepthLimitIn = -1) |
305 | 0 | : nDepthLimit(nDepthLimitIn), |
306 | 0 | bDumpParent(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE")) |
307 | 0 | { |
308 | 0 | if (strcmp(pszDumpFile, "stderr") == 0) |
309 | 0 | f = stderr; |
310 | 0 | else if (EQUAL(pszDumpFile, "YES")) |
311 | 0 | f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), |
312 | 0 | "wt"); |
313 | 0 | else |
314 | 0 | f = fopen(pszDumpFile, "wt"); |
315 | 0 | if (f == nullptr) |
316 | 0 | f = stderr; |
317 | 0 | } |
318 | | |
319 | | ~GDALPDFDumper() |
320 | 0 | { |
321 | 0 | if (f != stderr) |
322 | 0 | fclose(f); |
323 | 0 | } |
324 | | |
325 | | void Dump(GDALPDFObject *poObj, int nDepth = 0); |
326 | | void Dump(GDALPDFDictionary *poDict, int nDepth = 0); |
327 | | void Dump(GDALPDFArray *poArray, int nDepth = 0); |
328 | | }; |
329 | | |
330 | | void GDALPDFDumper::Dump(GDALPDFArray *poArray, int nDepth) |
331 | 0 | { |
332 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
333 | 0 | return; |
334 | | |
335 | 0 | int nLength = poArray->GetLength(); |
336 | 0 | int i; |
337 | 0 | CPLString osIndent; |
338 | 0 | for (i = 0; i < nDepth; i++) |
339 | 0 | osIndent += " "; |
340 | 0 | for (i = 0; i < nLength; i++) |
341 | 0 | { |
342 | 0 | fprintf(f, "%sItem[%d]:", osIndent.c_str(), i); |
343 | 0 | GDALPDFObject *poObj = nullptr; |
344 | 0 | if ((poObj = poArray->Get(i)) != nullptr) |
345 | 0 | { |
346 | 0 | if (poObj->GetType() == PDFObjectType_String || |
347 | 0 | poObj->GetType() == PDFObjectType_Null || |
348 | 0 | poObj->GetType() == PDFObjectType_Bool || |
349 | 0 | poObj->GetType() == PDFObjectType_Int || |
350 | 0 | poObj->GetType() == PDFObjectType_Real || |
351 | 0 | poObj->GetType() == PDFObjectType_Name) |
352 | 0 | { |
353 | 0 | fprintf(f, " "); |
354 | 0 | DumpSimplified(poObj); |
355 | 0 | fprintf(f, "\n"); |
356 | 0 | } |
357 | 0 | else |
358 | 0 | { |
359 | 0 | fprintf(f, "\n"); |
360 | 0 | Dump(poObj, nDepth + 1); |
361 | 0 | } |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | void GDALPDFDumper::DumpSimplified(GDALPDFObject *poObj) |
367 | 0 | { |
368 | 0 | switch (poObj->GetType()) |
369 | 0 | { |
370 | 0 | case PDFObjectType_String: |
371 | 0 | fprintf(f, "%s (string)", poObj->GetString().c_str()); |
372 | 0 | break; |
373 | | |
374 | 0 | case PDFObjectType_Null: |
375 | 0 | fprintf(f, "null"); |
376 | 0 | break; |
377 | | |
378 | 0 | case PDFObjectType_Bool: |
379 | 0 | fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false"); |
380 | 0 | break; |
381 | | |
382 | 0 | case PDFObjectType_Int: |
383 | 0 | fprintf(f, "%d (int)", poObj->GetInt()); |
384 | 0 | break; |
385 | | |
386 | 0 | case PDFObjectType_Real: |
387 | 0 | fprintf(f, "%f (real)", poObj->GetReal()); |
388 | 0 | break; |
389 | | |
390 | 0 | case PDFObjectType_Name: |
391 | 0 | fprintf(f, "%s (name)", poObj->GetName().c_str()); |
392 | 0 | break; |
393 | | |
394 | 0 | default: |
395 | 0 | fprintf(f, "unknown !"); |
396 | 0 | break; |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | | void GDALPDFDumper::Dump(GDALPDFObject *poObj, int nDepth) |
401 | 0 | { |
402 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
403 | 0 | return; |
404 | | |
405 | 0 | int i; |
406 | 0 | CPLString osIndent; |
407 | 0 | for (i = 0; i < nDepth; i++) |
408 | 0 | osIndent += " "; |
409 | 0 | fprintf(f, "%sType = %s", osIndent.c_str(), poObj->GetTypeName()); |
410 | 0 | int nRefNum = poObj->GetRefNum().toInt(); |
411 | 0 | if (nRefNum != 0) |
412 | 0 | fprintf(f, ", Num = %d, Gen = %d", nRefNum, poObj->GetRefGen()); |
413 | 0 | fprintf(f, "\n"); |
414 | |
|
415 | 0 | if (nRefNum != 0) |
416 | 0 | { |
417 | 0 | if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end()) |
418 | 0 | return; |
419 | 0 | aoSetObjectExplored.insert(nRefNum); |
420 | 0 | } |
421 | | |
422 | 0 | switch (poObj->GetType()) |
423 | 0 | { |
424 | 0 | case PDFObjectType_Array: |
425 | 0 | Dump(poObj->GetArray(), nDepth + 1); |
426 | 0 | break; |
427 | | |
428 | 0 | case PDFObjectType_Dictionary: |
429 | 0 | Dump(poObj->GetDictionary(), nDepth + 1); |
430 | 0 | break; |
431 | | |
432 | 0 | case PDFObjectType_String: |
433 | 0 | case PDFObjectType_Null: |
434 | 0 | case PDFObjectType_Bool: |
435 | 0 | case PDFObjectType_Int: |
436 | 0 | case PDFObjectType_Real: |
437 | 0 | case PDFObjectType_Name: |
438 | 0 | fprintf(f, "%s", osIndent.c_str()); |
439 | 0 | DumpSimplified(poObj); |
440 | 0 | fprintf(f, "\n"); |
441 | 0 | break; |
442 | | |
443 | 0 | default: |
444 | 0 | fprintf(f, "%s", osIndent.c_str()); |
445 | 0 | fprintf(f, "unknown !\n"); |
446 | 0 | break; |
447 | 0 | } |
448 | | |
449 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
450 | 0 | if (poStream != nullptr) |
451 | 0 | { |
452 | 0 | fprintf(f, |
453 | 0 | "%sHas stream (" CPL_FRMT_GIB |
454 | 0 | " uncompressed bytes, " CPL_FRMT_GIB " raw bytes)\n", |
455 | 0 | osIndent.c_str(), static_cast<GIntBig>(poStream->GetLength()), |
456 | 0 | static_cast<GIntBig>(poStream->GetRawLength())); |
457 | 0 | } |
458 | 0 | } |
459 | | |
460 | | void GDALPDFDumper::Dump(GDALPDFDictionary *poDict, int nDepth) |
461 | 0 | { |
462 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
463 | 0 | return; |
464 | | |
465 | 0 | CPLString osIndent; |
466 | 0 | for (int i = 0; i < nDepth; i++) |
467 | 0 | osIndent += " "; |
468 | 0 | int i = 0; |
469 | 0 | const auto &oMap = poDict->GetValues(); |
470 | 0 | for (const auto &[osKey, poObj] : oMap) |
471 | 0 | { |
472 | 0 | fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, osKey.c_str()); |
473 | 0 | ++i; |
474 | 0 | if (osKey == "Parent" && !bDumpParent) |
475 | 0 | { |
476 | 0 | if (poObj->GetRefNum().toBool()) |
477 | 0 | fprintf(f, ", Num = %d, Gen = %d", poObj->GetRefNum().toInt(), |
478 | 0 | poObj->GetRefGen()); |
479 | 0 | fprintf(f, "\n"); |
480 | 0 | continue; |
481 | 0 | } |
482 | 0 | if (poObj != nullptr) |
483 | 0 | { |
484 | 0 | if (poObj->GetType() == PDFObjectType_String || |
485 | 0 | poObj->GetType() == PDFObjectType_Null || |
486 | 0 | poObj->GetType() == PDFObjectType_Bool || |
487 | 0 | poObj->GetType() == PDFObjectType_Int || |
488 | 0 | poObj->GetType() == PDFObjectType_Real || |
489 | 0 | poObj->GetType() == PDFObjectType_Name) |
490 | 0 | { |
491 | 0 | fprintf(f, " = "); |
492 | 0 | DumpSimplified(poObj); |
493 | 0 | fprintf(f, "\n"); |
494 | 0 | } |
495 | 0 | else |
496 | 0 | { |
497 | 0 | fprintf(f, "\n"); |
498 | 0 | Dump(poObj, nDepth + 1); |
499 | 0 | } |
500 | 0 | } |
501 | 0 | } |
502 | 0 | } |
503 | | |
504 | | /************************************************************************/ |
505 | | /* PDFRasterBand() */ |
506 | | /************************************************************************/ |
507 | | |
508 | | PDFRasterBand::PDFRasterBand(PDFDataset *poDSIn, int nBandIn, |
509 | | int nResolutionLevelIn) |
510 | 98.0k | : nResolutionLevel(nResolutionLevelIn) |
511 | 98.0k | { |
512 | 98.0k | poDS = poDSIn; |
513 | 98.0k | nBand = nBandIn; |
514 | | |
515 | 98.0k | eDataType = GDT_UInt8; |
516 | 98.0k | } |
517 | | |
518 | | /************************************************************************/ |
519 | | /* SetSize() */ |
520 | | /************************************************************************/ |
521 | | |
522 | | void PDFRasterBand::SetSize(int nXSize, int nYSize) |
523 | 98.0k | { |
524 | 98.0k | nRasterXSize = nXSize; |
525 | 98.0k | nRasterYSize = nYSize; |
526 | | |
527 | 98.0k | const auto poPDFDS = cpl::down_cast<const PDFDataset *>(poDS); |
528 | 98.0k | if (nResolutionLevel > 0) |
529 | 0 | { |
530 | 0 | nBlockXSize = 256; |
531 | 0 | nBlockYSize = 256; |
532 | 0 | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
533 | 0 | } |
534 | 98.0k | else if (poPDFDS->m_nBlockXSize) |
535 | 56.3k | { |
536 | 56.3k | nBlockXSize = poPDFDS->m_nBlockXSize; |
537 | 56.3k | nBlockYSize = poPDFDS->m_nBlockYSize; |
538 | 56.3k | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
539 | 56.3k | } |
540 | 41.6k | else if (nRasterXSize < 64 * 1024 * 1024 / nRasterYSize) |
541 | 41.5k | { |
542 | 41.5k | nBlockXSize = nRasterXSize; |
543 | 41.5k | nBlockYSize = 1; |
544 | 41.5k | } |
545 | 81 | else |
546 | 81 | { |
547 | 81 | nBlockXSize = std::min(1024, nRasterXSize); |
548 | 81 | nBlockYSize = std::min(1024, nRasterYSize); |
549 | 81 | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
550 | 81 | } |
551 | 98.0k | } |
552 | | |
553 | | /************************************************************************/ |
554 | | /* InitOverviews() */ |
555 | | /************************************************************************/ |
556 | | |
557 | | void PDFDataset::InitOverviews() |
558 | 26.6k | { |
559 | | #ifdef HAVE_PDFIUM |
560 | | // Only if used pdfium, make "arbitrary overviews" |
561 | | // Blocks are 256x256 |
562 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
563 | | m_apoOvrDSBackup.empty()) |
564 | | { |
565 | | int nXSize = nRasterXSize; |
566 | | int nYSize = nRasterYSize; |
567 | | constexpr int minSize = 256; |
568 | | int nDiscard = 1; |
569 | | while (nXSize > minSize || nYSize > minSize) |
570 | | { |
571 | | nXSize = (nXSize + 1) / 2; |
572 | | nYSize = (nYSize + 1) / 2; |
573 | | |
574 | | auto poOvrDS = std::make_unique<PDFDataset>(this, nXSize, nYSize); |
575 | | |
576 | | for (int i = 0; i < nBands; i++) |
577 | | { |
578 | | auto poBand = std::make_unique<PDFRasterBand>(poOvrDS.get(), |
579 | | i + 1, nDiscard); |
580 | | poBand->SetSize(nXSize, nYSize); |
581 | | poOvrDS->SetBand(i + 1, std::move(poBand)); |
582 | | } |
583 | | |
584 | | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
585 | | ++nDiscard; |
586 | | } |
587 | | } |
588 | | #endif |
589 | 26.6k | #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) |
590 | 26.6k | if (!m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
591 | 7.36k | m_apoOvrDSBackup.empty() && m_osUserPwd != "ASK_INTERACTIVE") |
592 | 7.36k | { |
593 | 7.36k | int nXSize = nRasterXSize; |
594 | 7.36k | int nYSize = nRasterYSize; |
595 | 7.36k | constexpr int minSize = 256; |
596 | 7.36k | double dfDPI = m_dfDPI; |
597 | 26.1k | while (nXSize > minSize || nYSize > minSize) |
598 | 18.8k | { |
599 | 18.8k | nXSize = (nXSize + 1) / 2; |
600 | 18.8k | nYSize = (nYSize + 1) / 2; |
601 | 18.8k | dfDPI /= 2; |
602 | | |
603 | 18.8k | GDALOpenInfo oOpenInfo(GetDescription(), GA_ReadOnly); |
604 | 18.8k | CPLStringList aosOpenOptions(CSLDuplicate(papszOpenOptions)); |
605 | 18.8k | aosOpenOptions.SetNameValue("DPI", CPLSPrintf("%g", dfDPI)); |
606 | 18.8k | aosOpenOptions.SetNameValue("BANDS", CPLSPrintf("%d", nBands)); |
607 | 18.8k | aosOpenOptions.SetNameValue("@OPEN_FOR_OVERVIEW", "YES"); |
608 | 18.8k | if (!m_osUserPwd.empty()) |
609 | 0 | aosOpenOptions.SetNameValue("USER_PWD", m_osUserPwd.c_str()); |
610 | 18.8k | oOpenInfo.papszOpenOptions = aosOpenOptions.List(); |
611 | 18.8k | auto poOvrDS = std::unique_ptr<PDFDataset>(Open(&oOpenInfo)); |
612 | 18.8k | if (!poOvrDS || poOvrDS->nBands != nBands) |
613 | 17 | break; |
614 | 18.7k | poOvrDS->m_bIsOvrDS = true; |
615 | 18.7k | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
616 | 18.7k | } |
617 | 7.36k | } |
618 | 26.6k | #endif |
619 | 26.6k | } |
620 | | |
621 | | /************************************************************************/ |
622 | | /* GetColorInterpretation() */ |
623 | | /************************************************************************/ |
624 | | |
625 | | GDALColorInterp PDFRasterBand::GetColorInterpretation() |
626 | 0 | { |
627 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
628 | 0 | if (poGDS->nBands == 1) |
629 | 0 | return GCI_GrayIndex; |
630 | 0 | else |
631 | 0 | return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1)); |
632 | 0 | } |
633 | | |
634 | | /************************************************************************/ |
635 | | /* GetOverviewCount() */ |
636 | | /************************************************************************/ |
637 | | |
638 | | int PDFRasterBand::GetOverviewCount() |
639 | 27.7k | { |
640 | 27.7k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
641 | 27.7k | if (poGDS->m_bIsOvrDS) |
642 | 0 | return 0; |
643 | 27.7k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
644 | 1.12k | return GDALPamRasterBand::GetOverviewCount(); |
645 | 26.6k | else |
646 | 26.6k | { |
647 | 26.6k | poGDS->InitOverviews(); |
648 | 26.6k | return static_cast<int>(poGDS->m_apoOvrDS.size()); |
649 | 26.6k | } |
650 | 27.7k | } |
651 | | |
652 | | /************************************************************************/ |
653 | | /* GetOverview() */ |
654 | | /************************************************************************/ |
655 | | |
656 | | GDALRasterBand *PDFRasterBand::GetOverview(int iOverviewIndex) |
657 | 19.4k | { |
658 | 19.4k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
659 | 562 | return GDALPamRasterBand::GetOverview(iOverviewIndex); |
660 | | |
661 | 18.9k | else if (iOverviewIndex < 0 || iOverviewIndex >= GetOverviewCount()) |
662 | 0 | return nullptr; |
663 | 18.9k | else |
664 | 18.9k | { |
665 | 18.9k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
666 | 18.9k | return poGDS->m_apoOvrDS[iOverviewIndex]->GetRasterBand(nBand); |
667 | 18.9k | } |
668 | 19.4k | } |
669 | | |
670 | | /************************************************************************/ |
671 | | /* ~PDFRasterBand() */ |
672 | | /************************************************************************/ |
673 | | |
674 | | PDFRasterBand::~PDFRasterBand() |
675 | 98.0k | { |
676 | 98.0k | } |
677 | | |
678 | | /************************************************************************/ |
679 | | /* IReadBlockFromTile() */ |
680 | | /************************************************************************/ |
681 | | |
682 | | CPLErr PDFRasterBand::IReadBlockFromTile(int nBlockXOff, int nBlockYOff, |
683 | | void *pImage) |
684 | | |
685 | 0 | { |
686 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
687 | |
|
688 | 0 | const int nXOff = nBlockXOff * nBlockXSize; |
689 | 0 | const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff); |
690 | 0 | const int nYOff = nBlockYOff * nBlockYSize; |
691 | 0 | const int nReqYSize = std::min(nBlockYSize, nRasterYSize - nYOff); |
692 | |
|
693 | 0 | const int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize); |
694 | 0 | int iTile = poGDS->m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff]; |
695 | 0 | if (iTile < 0) |
696 | 0 | { |
697 | 0 | memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
698 | 0 | return CE_None; |
699 | 0 | } |
700 | | |
701 | 0 | GDALPDFTileDesc &sTile = poGDS->m_asTiles[iTile]; |
702 | 0 | GDALPDFObject *poImage = sTile.poImage; |
703 | |
|
704 | 0 | if (nBand == 4) |
705 | 0 | { |
706 | 0 | GDALPDFDictionary *poImageDict = poImage->GetDictionary(); |
707 | 0 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
708 | 0 | if (poSMask != nullptr && |
709 | 0 | poSMask->GetType() == PDFObjectType_Dictionary) |
710 | 0 | { |
711 | 0 | GDALPDFDictionary *poSMaskDict = poSMask->GetDictionary(); |
712 | 0 | GDALPDFObject *poWidth = poSMaskDict->Get("Width"); |
713 | 0 | GDALPDFObject *poHeight = poSMaskDict->Get("Height"); |
714 | 0 | GDALPDFObject *poColorSpace = poSMaskDict->Get("ColorSpace"); |
715 | 0 | GDALPDFObject *poBitsPerComponent = |
716 | 0 | poSMaskDict->Get("BitsPerComponent"); |
717 | 0 | double dfBits = 0; |
718 | 0 | if (poBitsPerComponent) |
719 | 0 | dfBits = Get(poBitsPerComponent); |
720 | 0 | if (poWidth && Get(poWidth) == nReqXSize && poHeight && |
721 | 0 | Get(poHeight) == nReqYSize && poColorSpace && |
722 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
723 | 0 | poColorSpace->GetName() == "DeviceGray" && |
724 | 0 | (dfBits == 1 || dfBits == 8)) |
725 | 0 | { |
726 | 0 | GDALPDFStream *poStream = poSMask->GetStream(); |
727 | 0 | GByte *pabyStream = nullptr; |
728 | |
|
729 | 0 | if (poStream == nullptr) |
730 | 0 | return CE_Failure; |
731 | | |
732 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
733 | 0 | if (pabyStream == nullptr) |
734 | 0 | return CE_Failure; |
735 | | |
736 | 0 | const int nReqXSize1 = (nReqXSize + 7) / 8; |
737 | 0 | if ((dfBits == 8 && |
738 | 0 | static_cast<size_t>(poStream->GetLength()) != |
739 | 0 | static_cast<size_t>(nReqXSize) * nReqYSize) || |
740 | 0 | (dfBits == 1 && |
741 | 0 | static_cast<size_t>(poStream->GetLength()) != |
742 | 0 | static_cast<size_t>(nReqXSize1) * nReqYSize)) |
743 | 0 | { |
744 | 0 | VSIFree(pabyStream); |
745 | 0 | return CE_Failure; |
746 | 0 | } |
747 | | |
748 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
749 | 0 | if (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize) |
750 | 0 | { |
751 | 0 | memset(pabyData, 0, |
752 | 0 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
753 | 0 | } |
754 | |
|
755 | 0 | if (dfBits == 8) |
756 | 0 | { |
757 | 0 | for (int j = 0; j < nReqYSize; j++) |
758 | 0 | { |
759 | 0 | for (int i = 0; i < nReqXSize; i++) |
760 | 0 | { |
761 | 0 | pabyData[j * nBlockXSize + i] = |
762 | 0 | pabyStream[j * nReqXSize + i]; |
763 | 0 | } |
764 | 0 | } |
765 | 0 | } |
766 | 0 | else |
767 | 0 | { |
768 | 0 | for (int j = 0; j < nReqYSize; j++) |
769 | 0 | { |
770 | 0 | for (int i = 0; i < nReqXSize; i++) |
771 | 0 | { |
772 | 0 | if (pabyStream[j * nReqXSize1 + i / 8] & |
773 | 0 | (1 << (7 - (i % 8)))) |
774 | 0 | pabyData[j * nBlockXSize + i] = 255; |
775 | 0 | else |
776 | 0 | pabyData[j * nBlockXSize + i] = 0; |
777 | 0 | } |
778 | 0 | } |
779 | 0 | } |
780 | |
|
781 | 0 | VSIFree(pabyStream); |
782 | 0 | return CE_None; |
783 | 0 | } |
784 | 0 | } |
785 | | |
786 | 0 | memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
787 | 0 | return CE_None; |
788 | 0 | } |
789 | | |
790 | 0 | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
791 | 0 | poGDS->m_nLastBlockYOff == nBlockYOff && |
792 | 0 | poGDS->m_pabyCachedData != nullptr) |
793 | 0 | { |
794 | | #ifdef DEBUG |
795 | | CPLDebug("PDF", "Using cached block (%d, %d)", nBlockXOff, nBlockYOff); |
796 | | #endif |
797 | | // do nothing |
798 | 0 | } |
799 | 0 | else |
800 | 0 | { |
801 | 0 | if (!poGDS->m_bTried) |
802 | 0 | { |
803 | 0 | poGDS->m_bTried = true; |
804 | 0 | poGDS->m_pabyCachedData = |
805 | 0 | static_cast<GByte *>(VSIMalloc3(3, nBlockXSize, nBlockYSize)); |
806 | 0 | } |
807 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
808 | 0 | return CE_Failure; |
809 | | |
810 | 0 | GDALPDFStream *poStream = poImage->GetStream(); |
811 | 0 | GByte *pabyStream = nullptr; |
812 | |
|
813 | 0 | if (poStream == nullptr) |
814 | 0 | return CE_Failure; |
815 | | |
816 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
817 | 0 | if (pabyStream == nullptr) |
818 | 0 | return CE_Failure; |
819 | | |
820 | 0 | if (static_cast<size_t>(poStream->GetLength()) != |
821 | 0 | static_cast<size_t>(sTile.nBands) * nReqXSize * nReqYSize) |
822 | 0 | { |
823 | 0 | VSIFree(pabyStream); |
824 | 0 | return CE_Failure; |
825 | 0 | } |
826 | | |
827 | 0 | memcpy(poGDS->m_pabyCachedData, pabyStream, |
828 | 0 | static_cast<size_t>(poStream->GetLength())); |
829 | 0 | VSIFree(pabyStream); |
830 | 0 | poGDS->m_nLastBlockXOff = nBlockXOff; |
831 | 0 | poGDS->m_nLastBlockYOff = nBlockYOff; |
832 | 0 | } |
833 | | |
834 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
835 | 0 | if (nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize)) |
836 | 0 | { |
837 | 0 | memset(pabyData, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
838 | 0 | } |
839 | |
|
840 | 0 | if (poGDS->nBands >= 3 && sTile.nBands == 3) |
841 | 0 | { |
842 | 0 | for (int j = 0; j < nReqYSize; j++) |
843 | 0 | { |
844 | 0 | for (int i = 0; i < nReqXSize; i++) |
845 | 0 | { |
846 | 0 | pabyData[j * nBlockXSize + i] = |
847 | 0 | poGDS |
848 | 0 | ->m_pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1]; |
849 | 0 | } |
850 | 0 | } |
851 | 0 | } |
852 | 0 | else if (sTile.nBands == 1) |
853 | 0 | { |
854 | 0 | for (int j = 0; j < nReqYSize; j++) |
855 | 0 | { |
856 | 0 | for (int i = 0; i < nReqXSize; i++) |
857 | 0 | { |
858 | 0 | pabyData[j * nBlockXSize + i] = |
859 | 0 | poGDS->m_pabyCachedData[j * nReqXSize + i]; |
860 | 0 | } |
861 | 0 | } |
862 | 0 | } |
863 | |
|
864 | 0 | return CE_None; |
865 | 0 | } |
866 | | |
867 | | /************************************************************************/ |
868 | | /* GetSuggestedBlockAccessPattern() */ |
869 | | /************************************************************************/ |
870 | | |
871 | | GDALSuggestedBlockAccessPattern |
872 | | PDFRasterBand::GetSuggestedBlockAccessPattern() const |
873 | 0 | { |
874 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
875 | 0 | if (!poGDS->m_aiTiles.empty()) |
876 | 0 | return GSBAP_RANDOM; |
877 | 0 | return GSBAP_LARGEST_CHUNK_POSSIBLE; |
878 | 0 | } |
879 | | |
880 | | /************************************************************************/ |
881 | | /* IReadBlock() */ |
882 | | /************************************************************************/ |
883 | | |
884 | | CPLErr PDFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
885 | | |
886 | 9.39M | { |
887 | 9.39M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
888 | | |
889 | 9.39M | if (!poGDS->m_aiTiles.empty()) |
890 | 0 | { |
891 | 0 | if (IReadBlockFromTile(nBlockXOff, nBlockYOff, pImage) == CE_None) |
892 | 0 | { |
893 | 0 | return CE_None; |
894 | 0 | } |
895 | 0 | else |
896 | 0 | { |
897 | 0 | poGDS->m_aiTiles.resize(0); |
898 | 0 | poGDS->m_bTried = false; |
899 | 0 | CPLFree(poGDS->m_pabyCachedData); |
900 | 0 | poGDS->m_pabyCachedData = nullptr; |
901 | 0 | poGDS->m_nLastBlockXOff = -1; |
902 | 0 | poGDS->m_nLastBlockYOff = -1; |
903 | 0 | } |
904 | 0 | } |
905 | | |
906 | 9.39M | const int nXOff = nBlockXOff * nBlockXSize; |
907 | 9.39M | const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff); |
908 | 9.39M | const int nReqYSize = |
909 | 9.39M | nBlockYSize == 1 |
910 | 9.39M | ? nRasterYSize |
911 | 9.39M | : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize); |
912 | | |
913 | 9.39M | if (!poGDS->m_bTried) |
914 | 7.32k | { |
915 | 7.32k | poGDS->m_bTried = true; |
916 | 7.32k | if (nBlockYSize == 1) |
917 | 7.31k | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
918 | 7.31k | std::max(3, poGDS->nBands), nRasterXSize, nRasterYSize)); |
919 | 15 | else |
920 | 15 | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
921 | 15 | std::max(3, poGDS->nBands), nBlockXSize, nBlockYSize)); |
922 | 7.32k | } |
923 | 9.39M | if (poGDS->m_pabyCachedData == nullptr) |
924 | 0 | return CE_Failure; |
925 | | |
926 | 9.39M | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
927 | 9.38M | (nBlockYSize == 1 || poGDS->m_nLastBlockYOff == nBlockYOff) && |
928 | 9.38M | poGDS->m_pabyCachedData != nullptr) |
929 | 9.38M | { |
930 | | /*CPLDebug("PDF", "Using cached block (%d, %d)", |
931 | | nBlockXOff, nBlockYOff);*/ |
932 | | // do nothing |
933 | 9.38M | } |
934 | 7.32k | else |
935 | 7.32k | { |
936 | | #ifdef HAVE_PODOFO |
937 | | if (poGDS->m_bUseLib.test(PDFLIB_PODOFO) && nBand == 4) |
938 | | { |
939 | | memset(pImage, 255, nBlockXSize * nBlockYSize); |
940 | | return CE_None; |
941 | | } |
942 | | #endif |
943 | | |
944 | 7.32k | const int nReqXOff = nBlockXOff * nBlockXSize; |
945 | 7.32k | const int nReqYOff = (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize; |
946 | 7.32k | const GSpacing nPixelSpace = 1; |
947 | 7.32k | const GSpacing nLineSpace = nBlockXSize; |
948 | 7.32k | const GSpacing nBandSpace = |
949 | 7.32k | static_cast<GSpacing>(nBlockXSize) * |
950 | 7.32k | ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize); |
951 | | |
952 | 7.32k | CPLErr eErr = poGDS->ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
953 | 7.32k | nReqYSize, nPixelSpace, nLineSpace, |
954 | 7.32k | nBandSpace, poGDS->m_pabyCachedData); |
955 | | |
956 | 7.32k | if (eErr == CE_None) |
957 | 7.32k | { |
958 | 7.32k | poGDS->m_nLastBlockXOff = nBlockXOff; |
959 | 7.32k | poGDS->m_nLastBlockYOff = nBlockYOff; |
960 | 7.32k | } |
961 | 0 | else |
962 | 0 | { |
963 | 0 | CPLFree(poGDS->m_pabyCachedData); |
964 | 0 | poGDS->m_pabyCachedData = nullptr; |
965 | 0 | } |
966 | 7.32k | } |
967 | 9.39M | if (poGDS->m_pabyCachedData == nullptr) |
968 | 0 | return CE_Failure; |
969 | | |
970 | 9.39M | if (nBlockYSize == 1) |
971 | 9.39M | memcpy(pImage, |
972 | 9.39M | poGDS->m_pabyCachedData + |
973 | 9.39M | (nBand - 1) * nBlockXSize * nRasterYSize + |
974 | 9.39M | nBlockYOff * nBlockXSize, |
975 | 9.39M | nBlockXSize); |
976 | 15 | else |
977 | 15 | { |
978 | 15 | memcpy(pImage, |
979 | 15 | poGDS->m_pabyCachedData + |
980 | 15 | static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize, |
981 | 15 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
982 | | |
983 | 15 | if (poGDS->m_bCacheBlocksForOtherBands && nBand == 1) |
984 | 15 | { |
985 | 45 | for (int iBand = 2; iBand <= poGDS->nBands; ++iBand) |
986 | 30 | { |
987 | 30 | auto poOtherBand = cpl::down_cast<PDFRasterBand *>( |
988 | 30 | poGDS->papoBands[iBand - 1]); |
989 | 30 | GDALRasterBlock *poBlock = |
990 | 30 | poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff); |
991 | 30 | if (poBlock) |
992 | 0 | { |
993 | 0 | poBlock->DropLock(); |
994 | 0 | } |
995 | 30 | else |
996 | 30 | { |
997 | 30 | poBlock = poOtherBand->GetLockedBlockRef(nBlockXOff, |
998 | 30 | nBlockYOff, TRUE); |
999 | 30 | if (poBlock) |
1000 | 30 | { |
1001 | 30 | memcpy(poBlock->GetDataRef(), |
1002 | 30 | poGDS->m_pabyCachedData + |
1003 | 30 | static_cast<size_t>(iBand - 1) * |
1004 | 30 | nBlockXSize * nBlockYSize, |
1005 | 30 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
1006 | 30 | poBlock->DropLock(); |
1007 | 30 | } |
1008 | 30 | } |
1009 | 30 | } |
1010 | 15 | } |
1011 | 15 | } |
1012 | | |
1013 | 9.39M | return CE_None; |
1014 | 9.39M | } |
1015 | | |
1016 | | /************************************************************************/ |
1017 | | /* PDFEnterPasswordFromConsoleIfNeeded() */ |
1018 | | /************************************************************************/ |
1019 | | |
1020 | | static const char *PDFEnterPasswordFromConsoleIfNeeded(const char *pszUserPwd) |
1021 | 0 | { |
1022 | 0 | if (EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
1023 | 0 | { |
1024 | 0 | static char szPassword[81]; |
1025 | 0 | printf("Enter password (will be echo'ed in the console): "); /*ok*/ |
1026 | 0 | if (nullptr == fgets(szPassword, sizeof(szPassword), stdin)) |
1027 | 0 | { |
1028 | 0 | fprintf(stderr, "WARNING: Error getting password.\n"); /*ok*/ |
1029 | 0 | } |
1030 | 0 | szPassword[sizeof(szPassword) - 1] = 0; |
1031 | 0 | char *sz10 = strchr(szPassword, '\n'); |
1032 | 0 | if (sz10) |
1033 | 0 | *sz10 = 0; |
1034 | 0 | return szPassword; |
1035 | 0 | } |
1036 | 0 | return pszUserPwd; |
1037 | 0 | } |
1038 | | |
1039 | | #ifdef HAVE_PDFIUM |
1040 | | |
1041 | | /************************************************************************/ |
1042 | | /* Pdfium Load/Unload */ |
1043 | | /* Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/) */ |
1044 | | /* Author: Martin Mikita <martin.mikita@klokantech.com> */ |
1045 | | /************************************************************************/ |
1046 | | |
1047 | | // Flag for calling PDFium Init and Destroy methods |
1048 | | bool PDFDataset::g_bPdfiumInit = false; |
1049 | | |
1050 | | // Pdfium global read mutex - Pdfium is not multi-thread |
1051 | | static CPLMutex *g_oPdfiumReadMutex = nullptr; |
1052 | | static CPLMutex *g_oPdfiumLoadDocMutex = nullptr; |
1053 | | |
1054 | | // Comparison of char* for std::map |
1055 | | struct cmp_str |
1056 | | { |
1057 | | bool operator()(char const *a, char const *b) const |
1058 | | { |
1059 | | return strcmp(a, b) < 0; |
1060 | | } |
1061 | | }; |
1062 | | |
1063 | | static int GDALPdfiumGetBlock(void *param, unsigned long position, |
1064 | | unsigned char *pBuf, unsigned long size) |
1065 | | { |
1066 | | VSILFILE *fp = static_cast<VSILFILE *>(param); |
1067 | | VSIFSeekL(fp, static_cast<vsi_l_offset>(position), SEEK_SET); |
1068 | | return VSIFReadL(pBuf, size, 1, fp) == 1; |
1069 | | } |
1070 | | |
1071 | | // List of all PDF datasets |
1072 | | typedef std::map<const char *, TPdfiumDocumentStruct *, cmp_str> |
1073 | | TMapPdfiumDatasets; |
1074 | | static TMapPdfiumDatasets g_mPdfiumDatasets; |
1075 | | |
1076 | | /** |
1077 | | * Loading PDFIUM page |
1078 | | * - multithreading requires "mutex" |
1079 | | * - one page can require too much RAM |
1080 | | * - we will have one document per filename and one object per page |
1081 | | */ |
1082 | | |
1083 | | static int LoadPdfiumDocumentPage(const char *pszFilename, |
1084 | | const char *pszUserPwd, int pageNum, |
1085 | | TPdfiumDocumentStruct **doc, |
1086 | | TPdfiumPageStruct **page, int *pnPageCount) |
1087 | | { |
1088 | | // Prepare nullptr for error returning |
1089 | | if (doc) |
1090 | | *doc = nullptr; |
1091 | | if (page) |
1092 | | *page = nullptr; |
1093 | | if (pnPageCount) |
1094 | | *pnPageCount = 0; |
1095 | | |
1096 | | // Loading document and page must be only in one thread! |
1097 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1098 | | |
1099 | | // Library can be destroyed if every PDF dataset was closed! |
1100 | | if (!PDFDataset::g_bPdfiumInit) |
1101 | | { |
1102 | | FPDF_InitLibrary(); |
1103 | | PDFDataset::g_bPdfiumInit = TRUE; |
1104 | | } |
1105 | | |
1106 | | TMapPdfiumDatasets::iterator it; |
1107 | | it = g_mPdfiumDatasets.find(pszFilename); |
1108 | | TPdfiumDocumentStruct *poDoc = nullptr; |
1109 | | // Load new document if missing |
1110 | | if (it == g_mPdfiumDatasets.end()) |
1111 | | { |
1112 | | // Try without password (if PDF not requires password it can fail) |
1113 | | |
1114 | | VSILFILE *fp = VSIFOpenL(pszFilename, "rb"); |
1115 | | if (fp == nullptr) |
1116 | | { |
1117 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1118 | | return FALSE; |
1119 | | } |
1120 | | VSIFSeekL(fp, 0, SEEK_END); |
1121 | | const auto nFileLen64 = VSIFTellL(fp); |
1122 | | if constexpr (LONG_MAX < std::numeric_limits<vsi_l_offset>::max()) |
1123 | | { |
1124 | | if (nFileLen64 > LONG_MAX) |
1125 | | { |
1126 | | VSIFCloseL(fp); |
1127 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1128 | | return FALSE; |
1129 | | } |
1130 | | } |
1131 | | |
1132 | | FPDF_FILEACCESS *psFileAccess = new FPDF_FILEACCESS; |
1133 | | psFileAccess->m_Param = fp; |
1134 | | psFileAccess->m_FileLen = static_cast<unsigned long>(nFileLen64); |
1135 | | psFileAccess->m_GetBlock = GDALPdfiumGetBlock; |
1136 | | CPDF_Document *docPdfium = CPDFDocumentFromFPDFDocument( |
1137 | | FPDF_LoadCustomDocument(psFileAccess, nullptr)); |
1138 | | if (docPdfium == nullptr) |
1139 | | { |
1140 | | unsigned long err = FPDF_GetLastError(); |
1141 | | if (err == FPDF_ERR_PASSWORD) |
1142 | | { |
1143 | | if (pszUserPwd) |
1144 | | { |
1145 | | pszUserPwd = |
1146 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
1147 | | docPdfium = CPDFDocumentFromFPDFDocument( |
1148 | | FPDF_LoadCustomDocument(psFileAccess, pszUserPwd)); |
1149 | | if (docPdfium == nullptr) |
1150 | | err = FPDF_GetLastError(); |
1151 | | else |
1152 | | err = FPDF_ERR_SUCCESS; |
1153 | | } |
1154 | | else |
1155 | | { |
1156 | | CPLError(CE_Failure, CPLE_AppDefined, |
1157 | | "A password is needed. You can specify it through " |
1158 | | "the PDF_USER_PWD " |
1159 | | "configuration option / USER_PWD open option " |
1160 | | "(that can be set to ASK_INTERACTIVE)"); |
1161 | | |
1162 | | VSIFCloseL(fp); |
1163 | | delete psFileAccess; |
1164 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1165 | | return FALSE; |
1166 | | } |
1167 | | } // First Error Password [null password given] |
1168 | | if (err != FPDF_ERR_SUCCESS) |
1169 | | { |
1170 | | if (err == FPDF_ERR_PASSWORD) |
1171 | | CPLError(CE_Failure, CPLE_AppDefined, |
1172 | | "PDFium Invalid password."); |
1173 | | else if (err == FPDF_ERR_SECURITY) |
1174 | | CPLError(CE_Failure, CPLE_AppDefined, |
1175 | | "PDFium Unsupported security scheme."); |
1176 | | else if (err == FPDF_ERR_FORMAT) |
1177 | | CPLError(CE_Failure, CPLE_AppDefined, |
1178 | | "PDFium File not in PDF format or corrupted."); |
1179 | | else if (err == FPDF_ERR_FILE) |
1180 | | CPLError(CE_Failure, CPLE_AppDefined, |
1181 | | "PDFium File not found or could not be opened."); |
1182 | | else |
1183 | | CPLError(CE_Failure, CPLE_AppDefined, |
1184 | | "PDFium Unknown PDF error or invalid PDF."); |
1185 | | |
1186 | | VSIFCloseL(fp); |
1187 | | delete psFileAccess; |
1188 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1189 | | return FALSE; |
1190 | | } |
1191 | | } // ~ wrong PDF or password required |
1192 | | |
1193 | | // Create new poDoc |
1194 | | poDoc = new TPdfiumDocumentStruct; |
1195 | | if (!poDoc) |
1196 | | { |
1197 | | CPLError(CE_Failure, CPLE_AppDefined, |
1198 | | "Not enough memory for Pdfium Document object"); |
1199 | | |
1200 | | VSIFCloseL(fp); |
1201 | | delete psFileAccess; |
1202 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1203 | | return FALSE; |
1204 | | } |
1205 | | poDoc->filename = CPLStrdup(pszFilename); |
1206 | | poDoc->doc = docPdfium; |
1207 | | poDoc->psFileAccess = psFileAccess; |
1208 | | |
1209 | | g_mPdfiumDatasets[poDoc->filename] = poDoc; |
1210 | | } |
1211 | | // Document already loaded |
1212 | | else |
1213 | | { |
1214 | | poDoc = it->second; |
1215 | | } |
1216 | | |
1217 | | // Check page num in document |
1218 | | int nPages = poDoc->doc->GetPageCount(); |
1219 | | if (pageNum < 1 || pageNum > nPages) |
1220 | | { |
1221 | | CPLError(CE_Failure, CPLE_AppDefined, |
1222 | | "PDFium Invalid page number (%d/%d) for document %s", pageNum, |
1223 | | nPages, pszFilename); |
1224 | | |
1225 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1226 | | return FALSE; |
1227 | | } |
1228 | | |
1229 | | /* Sanity check to validate page count */ |
1230 | | if (pageNum != nPages) |
1231 | | { |
1232 | | if (poDoc->doc->GetPageDictionary(nPages - 1) == nullptr) |
1233 | | { |
1234 | | CPLError(CE_Failure, CPLE_AppDefined, |
1235 | | "Invalid PDF : invalid page count"); |
1236 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1237 | | return FALSE; |
1238 | | } |
1239 | | } |
1240 | | |
1241 | | TMapPdfiumPages::iterator itPage; |
1242 | | itPage = poDoc->pages.find(pageNum); |
1243 | | TPdfiumPageStruct *poPage = nullptr; |
1244 | | // Page not loaded |
1245 | | if (itPage == poDoc->pages.end()) |
1246 | | { |
1247 | | auto pDict = poDoc->doc->GetMutablePageDictionary(pageNum - 1); |
1248 | | if (pDict == nullptr) |
1249 | | { |
1250 | | CPLError(CE_Failure, CPLE_AppDefined, |
1251 | | "Invalid PDFium : invalid page"); |
1252 | | |
1253 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1254 | | return FALSE; |
1255 | | } |
1256 | | auto pPage = pdfium::MakeRetain<CPDF_Page>(poDoc->doc, pDict); |
1257 | | |
1258 | | poPage = new TPdfiumPageStruct; |
1259 | | if (!poPage) |
1260 | | { |
1261 | | CPLError(CE_Failure, CPLE_AppDefined, |
1262 | | "Not enough memory for Pdfium Page object"); |
1263 | | |
1264 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1265 | | return FALSE; |
1266 | | } |
1267 | | poPage->pageNum = pageNum; |
1268 | | poPage->page = pPage.Leak(); |
1269 | | poPage->readMutex = nullptr; |
1270 | | poPage->sharedNum = 0; |
1271 | | |
1272 | | poDoc->pages[pageNum] = poPage; |
1273 | | } |
1274 | | // Page already loaded |
1275 | | else |
1276 | | { |
1277 | | poPage = itPage->second; |
1278 | | } |
1279 | | |
1280 | | // Increase number of used |
1281 | | ++poPage->sharedNum; |
1282 | | |
1283 | | if (doc) |
1284 | | *doc = poDoc; |
1285 | | if (page) |
1286 | | *page = poPage; |
1287 | | if (pnPageCount) |
1288 | | *pnPageCount = nPages; |
1289 | | |
1290 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1291 | | |
1292 | | return TRUE; |
1293 | | } |
1294 | | |
1295 | | // ~ static int LoadPdfiumDocumentPage() |
1296 | | |
1297 | | static int UnloadPdfiumDocumentPage(TPdfiumDocumentStruct **doc, |
1298 | | TPdfiumPageStruct **page) |
1299 | | { |
1300 | | if (!doc || !page) |
1301 | | return FALSE; |
1302 | | |
1303 | | TPdfiumPageStruct *pPage = *page; |
1304 | | TPdfiumDocumentStruct *pDoc = *doc; |
1305 | | |
1306 | | // Get mutex for loading pdfium |
1307 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1308 | | |
1309 | | // Decrease page use |
1310 | | --pPage->sharedNum; |
1311 | | |
1312 | | #ifdef DEBUG |
1313 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: page shared num %d", |
1314 | | pPage->sharedNum); |
1315 | | #endif |
1316 | | // Page is used (also document) |
1317 | | if (pPage->sharedNum != 0) |
1318 | | { |
1319 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1320 | | return TRUE; |
1321 | | } |
1322 | | |
1323 | | // Get mutex, release and destroy it |
1324 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), PDFIUM_MUTEX_TIMEOUT); |
1325 | | CPLReleaseMutex(pPage->readMutex); |
1326 | | CPLDestroyMutex(pPage->readMutex); |
1327 | | // Close page and remove from map |
1328 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
1329 | | |
1330 | | pDoc->pages.erase(pPage->pageNum); |
1331 | | delete pPage; |
1332 | | pPage = nullptr; |
1333 | | |
1334 | | #ifdef DEBUG |
1335 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: pages %lu", |
1336 | | pDoc->pages.size()); |
1337 | | #endif |
1338 | | // Another page is used |
1339 | | if (!pDoc->pages.empty()) |
1340 | | { |
1341 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1342 | | return TRUE; |
1343 | | } |
1344 | | |
1345 | | // Close document and remove from map |
1346 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
1347 | | g_mPdfiumDatasets.erase(pDoc->filename); |
1348 | | CPLFree(pDoc->filename); |
1349 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
1350 | | delete pDoc->psFileAccess; |
1351 | | delete pDoc; |
1352 | | pDoc = nullptr; |
1353 | | |
1354 | | #ifdef DEBUG |
1355 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: documents %lu", |
1356 | | g_mPdfiumDatasets.size()); |
1357 | | #endif |
1358 | | // Another document is used |
1359 | | if (!g_mPdfiumDatasets.empty()) |
1360 | | { |
1361 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1362 | | return TRUE; |
1363 | | } |
1364 | | |
1365 | | #ifdef DEBUG |
1366 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: Nothing loaded, " |
1367 | | "destroy Library"); |
1368 | | #endif |
1369 | | // No document loaded, destroy pdfium |
1370 | | FPDF_DestroyLibrary(); |
1371 | | PDFDataset::g_bPdfiumInit = FALSE; |
1372 | | |
1373 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1374 | | |
1375 | | return TRUE; |
1376 | | } |
1377 | | |
1378 | | // ~ static int UnloadPdfiumDocumentPage() |
1379 | | |
1380 | | #endif // ~ HAVE_PDFIUM |
1381 | | |
1382 | | /************************************************************************/ |
1383 | | /* GetOption() */ |
1384 | | /************************************************************************/ |
1385 | | |
1386 | | const char *PDFDataset::GetOption(char **papszOpenOptionsIn, |
1387 | | const char *pszOptionName, |
1388 | | const char *pszDefaultVal) |
1389 | 136k | { |
1390 | 136k | CPLErr eLastErrType = CPLGetLastErrorType(); |
1391 | 136k | CPLErrorNum nLastErrno = CPLGetLastErrorNo(); |
1392 | 136k | CPLString osLastErrorMsg(CPLGetLastErrorMsg()); |
1393 | 136k | CPLXMLNode *psNode = CPLParseXMLString(PDFGetOpenOptionList()); |
1394 | 136k | CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg); |
1395 | 136k | if (psNode == nullptr) |
1396 | 0 | return pszDefaultVal; |
1397 | 136k | CPLXMLNode *psIter = psNode->psChild; |
1398 | 601k | while (psIter != nullptr) |
1399 | 601k | { |
1400 | 601k | if (EQUAL(CPLGetXMLValue(psIter, "name", ""), pszOptionName)) |
1401 | 136k | { |
1402 | 136k | const char *pszVal = |
1403 | 136k | CSLFetchNameValue(papszOpenOptionsIn, pszOptionName); |
1404 | 136k | if (pszVal != nullptr) |
1405 | 37.6k | { |
1406 | 37.6k | CPLDestroyXMLNode(psNode); |
1407 | 37.6k | return pszVal; |
1408 | 37.6k | } |
1409 | 98.8k | const char *pszAltConfigOption = |
1410 | 98.8k | CPLGetXMLValue(psIter, "alt_config_option", nullptr); |
1411 | 98.8k | if (pszAltConfigOption != nullptr) |
1412 | 98.8k | { |
1413 | 98.8k | pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal); |
1414 | 98.8k | CPLDestroyXMLNode(psNode); |
1415 | 98.8k | return pszVal; |
1416 | 98.8k | } |
1417 | 0 | CPLDestroyXMLNode(psNode); |
1418 | 0 | return pszDefaultVal; |
1419 | 98.8k | } |
1420 | 465k | psIter = psIter->psNext; |
1421 | 465k | } |
1422 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1423 | 0 | "Requesting an undocumented open option '%s'", pszOptionName); |
1424 | 0 | CPLDestroyXMLNode(psNode); |
1425 | 0 | return pszDefaultVal; |
1426 | 136k | } |
1427 | | |
1428 | | #ifdef HAVE_PDFIUM |
1429 | | |
1430 | | /************************************************************************/ |
1431 | | /* GDALPDFiumOCContext */ |
1432 | | /************************************************************************/ |
1433 | | |
1434 | | class GDALPDFiumOCContext final : public CPDF_OCContextInterface |
1435 | | { |
1436 | | PDFDataset *m_poDS; |
1437 | | RetainPtr<CPDF_OCContext> m_DefaultOCContext; |
1438 | | |
1439 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumOCContext) |
1440 | | |
1441 | | public: |
1442 | | GDALPDFiumOCContext(PDFDataset *poDS, CPDF_Document *pDoc, |
1443 | | CPDF_OCContext::UsageType usage) |
1444 | | : m_poDS(poDS), |
1445 | | m_DefaultOCContext(pdfium::MakeRetain<CPDF_OCContext>(pDoc, usage)) |
1446 | | { |
1447 | | } |
1448 | | |
1449 | | virtual bool |
1450 | | CheckOCGDictVisible(const CPDF_Dictionary *pOCGDict) const override |
1451 | | { |
1452 | | // CPLDebug("PDF", "CheckOCGDictVisible(%d,%d)", |
1453 | | // pOCGDict->GetObjNum(), pOCGDict->GetGenNum() ); |
1454 | | PDFDataset::VisibilityState eVisibility = |
1455 | | m_poDS->GetVisibilityStateForOGCPdfium(pOCGDict->GetObjNum(), |
1456 | | pOCGDict->GetGenNum()); |
1457 | | if (eVisibility == PDFDataset::VISIBILITY_ON) |
1458 | | return true; |
1459 | | if (eVisibility == PDFDataset::VISIBILITY_OFF) |
1460 | | return false; |
1461 | | return m_DefaultOCContext->CheckOCGDictVisible(pOCGDict); |
1462 | | } |
1463 | | }; |
1464 | | |
1465 | | /************************************************************************/ |
1466 | | /* GDALPDFiumRenderDeviceDriver */ |
1467 | | /************************************************************************/ |
1468 | | |
1469 | | class GDALPDFiumRenderDeviceDriver final : public RenderDeviceDriverIface |
1470 | | { |
1471 | | std::unique_ptr<RenderDeviceDriverIface> m_poParent; |
1472 | | CFX_RenderDevice *device_; |
1473 | | |
1474 | | int bEnableVector; |
1475 | | int bEnableText; |
1476 | | int bEnableBitmap; |
1477 | | int bTemporaryEnableVectorForTextStroking; |
1478 | | |
1479 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumRenderDeviceDriver) |
1480 | | |
1481 | | public: |
1482 | | GDALPDFiumRenderDeviceDriver( |
1483 | | std::unique_ptr<RenderDeviceDriverIface> &&poParent, |
1484 | | CFX_RenderDevice *pDevice) |
1485 | | : m_poParent(std::move(poParent)), device_(pDevice), |
1486 | | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE), |
1487 | | bTemporaryEnableVectorForTextStroking(FALSE) |
1488 | | { |
1489 | | } |
1490 | | |
1491 | | virtual ~GDALPDFiumRenderDeviceDriver() = default; |
1492 | | |
1493 | | void SetEnableVector(int bFlag) |
1494 | | { |
1495 | | bEnableVector = bFlag; |
1496 | | } |
1497 | | |
1498 | | void SetEnableText(int bFlag) |
1499 | | { |
1500 | | bEnableText = bFlag; |
1501 | | } |
1502 | | |
1503 | | void SetEnableBitmap(int bFlag) |
1504 | | { |
1505 | | bEnableBitmap = bFlag; |
1506 | | } |
1507 | | |
1508 | | DeviceType GetDeviceType() const override |
1509 | | { |
1510 | | return m_poParent->GetDeviceType(); |
1511 | | } |
1512 | | |
1513 | | int GetDeviceCaps(int caps_id) const override |
1514 | | { |
1515 | | return m_poParent->GetDeviceCaps(caps_id); |
1516 | | } |
1517 | | |
1518 | | void SaveState() override |
1519 | | { |
1520 | | m_poParent->SaveState(); |
1521 | | } |
1522 | | |
1523 | | void RestoreState(bool bKeepSaved) override |
1524 | | { |
1525 | | m_poParent->RestoreState(bKeepSaved); |
1526 | | } |
1527 | | |
1528 | | void SetBaseClip(const FX_RECT &rect) override |
1529 | | { |
1530 | | m_poParent->SetBaseClip(rect); |
1531 | | } |
1532 | | |
1533 | | virtual bool |
1534 | | SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1535 | | const CFX_FillRenderOptions &fill_options) override |
1536 | | { |
1537 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1538 | | return true; |
1539 | | return m_poParent->SetClip_PathFill(path, pObject2Device, fill_options); |
1540 | | } |
1541 | | |
1542 | | virtual bool |
1543 | | SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1544 | | const CFX_GraphStateData *pGraphState) override |
1545 | | { |
1546 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1547 | | return true; |
1548 | | return m_poParent->SetClip_PathStroke(path, pObject2Device, |
1549 | | pGraphState); |
1550 | | } |
1551 | | |
1552 | | virtual bool DrawPath(const CFX_Path &path, |
1553 | | const CFX_Matrix *pObject2Device, |
1554 | | const CFX_GraphStateData *pGraphState, |
1555 | | uint32_t fill_color, uint32_t stroke_color, |
1556 | | const CFX_FillRenderOptions &fill_options) override |
1557 | | { |
1558 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1559 | | return true; |
1560 | | return m_poParent->DrawPath(path, pObject2Device, pGraphState, |
1561 | | fill_color, stroke_color, fill_options); |
1562 | | } |
1563 | | |
1564 | | bool FillRect(const FX_RECT &rect, uint32_t fill_color) override |
1565 | | { |
1566 | | return m_poParent->FillRect(rect, fill_color); |
1567 | | } |
1568 | | |
1569 | | virtual bool DrawCosmeticLine(const CFX_PointF &ptMoveTo, |
1570 | | const CFX_PointF &ptLineTo, |
1571 | | uint32_t color) override |
1572 | | { |
1573 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1574 | | return TRUE; |
1575 | | return m_poParent->DrawCosmeticLine(ptMoveTo, ptLineTo, color); |
1576 | | } |
1577 | | |
1578 | | FX_RECT GetClipBox() const override |
1579 | | { |
1580 | | return m_poParent->GetClipBox(); |
1581 | | } |
1582 | | |
1583 | | virtual bool GetDIBits(RetainPtr<CFX_DIBitmap> bitmap, int left, |
1584 | | int top) const override |
1585 | | { |
1586 | | return m_poParent->GetDIBits(std::move(bitmap), left, top); |
1587 | | } |
1588 | | |
1589 | | RetainPtr<const CFX_DIBitmap> GetBackDrop() const override |
1590 | | { |
1591 | | return m_poParent->GetBackDrop(); |
1592 | | } |
1593 | | |
1594 | | virtual bool SetDIBits(RetainPtr<const CFX_DIBBase> bitmap, uint32_t color, |
1595 | | const FX_RECT &src_rect, int dest_left, int dest_top, |
1596 | | BlendMode blend_type) override |
1597 | | { |
1598 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1599 | | return true; |
1600 | | return m_poParent->SetDIBits(std::move(bitmap), color, src_rect, |
1601 | | dest_left, dest_top, blend_type); |
1602 | | } |
1603 | | |
1604 | | virtual bool StretchDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1605 | | uint32_t color, int dest_left, int dest_top, |
1606 | | int dest_width, int dest_height, |
1607 | | const FX_RECT *pClipRect, |
1608 | | const FXDIB_ResampleOptions &options, |
1609 | | BlendMode blend_type) override |
1610 | | { |
1611 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1612 | | return true; |
1613 | | return m_poParent->StretchDIBits(std::move(bitmap), color, dest_left, |
1614 | | dest_top, dest_width, dest_height, |
1615 | | pClipRect, options, blend_type); |
1616 | | } |
1617 | | |
1618 | | virtual StartResult StartDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1619 | | float alpha, uint32_t color, |
1620 | | const CFX_Matrix &matrix, |
1621 | | const FXDIB_ResampleOptions &options, |
1622 | | BlendMode blend_type) override |
1623 | | { |
1624 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1625 | | return StartResult(Result::kSuccess, nullptr); |
1626 | | return m_poParent->StartDIBits(std::move(bitmap), alpha, color, matrix, |
1627 | | options, blend_type); |
1628 | | } |
1629 | | |
1630 | | virtual bool ContinueDIBits(CFX_AggImageRenderer *handle, |
1631 | | PauseIndicatorIface *pPause) override |
1632 | | { |
1633 | | return m_poParent->ContinueDIBits(handle, pPause); |
1634 | | } |
1635 | | |
1636 | | virtual bool DrawDeviceText(const pdfium::span<const TextCharPos> &pCharPos, |
1637 | | CFX_Font *pFont, |
1638 | | const CFX_Matrix &mtObject2Device, |
1639 | | float font_size, uint32_t color, |
1640 | | const CFX_TextRenderOptions &options) override |
1641 | | { |
1642 | | if (bEnableText) |
1643 | | { |
1644 | | // This is quite tricky. We call again the guy who called us |
1645 | | // (CFX_RenderDevice::DrawNormalText()) but we set a special flag to |
1646 | | // allow vector&raster operations so that the rendering will happen |
1647 | | // in the next phase |
1648 | | if (bTemporaryEnableVectorForTextStroking) |
1649 | | return FALSE; // this is the default behavior of the parent |
1650 | | bTemporaryEnableVectorForTextStroking = true; |
1651 | | bool bRet = device_->DrawNormalText( |
1652 | | pCharPos, pFont, font_size, mtObject2Device, color, options); |
1653 | | bTemporaryEnableVectorForTextStroking = FALSE; |
1654 | | return bRet; |
1655 | | } |
1656 | | else |
1657 | | return true; // pretend that we did the job |
1658 | | } |
1659 | | |
1660 | | int GetDriverType() const override |
1661 | | { |
1662 | | return m_poParent->GetDriverType(); |
1663 | | } |
1664 | | |
1665 | | #if defined(_SKIA_SUPPORT_) |
1666 | | virtual bool DrawShading(const CPDF_ShadingPattern &pattern, |
1667 | | const CFX_Matrix &matrix, const FX_RECT &clip_rect, |
1668 | | int alpha) override |
1669 | | { |
1670 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1671 | | return true; |
1672 | | return m_poParent->DrawShading(pattern, matrix, clip_rect, alpha); |
1673 | | } |
1674 | | #endif |
1675 | | |
1676 | | bool MultiplyAlpha(float alpha) override |
1677 | | { |
1678 | | return m_poParent->MultiplyAlpha(alpha); |
1679 | | } |
1680 | | |
1681 | | bool MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) override |
1682 | | { |
1683 | | return m_poParent->MultiplyAlphaMask(std::move(mask)); |
1684 | | } |
1685 | | |
1686 | | #if defined(_SKIA_SUPPORT_) |
1687 | | virtual bool SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap, |
1688 | | RetainPtr<const CFX_DIBBase> mask, int left, |
1689 | | int top, float alpha, |
1690 | | BlendMode blend_type) override |
1691 | | { |
1692 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1693 | | return true; |
1694 | | return m_poParent->SetBitsWithMask(std::move(bitmap), std::move(mask), |
1695 | | left, top, alpha, blend_type); |
1696 | | } |
1697 | | |
1698 | | void SetGroupKnockout(bool group_knockout) override |
1699 | | { |
1700 | | m_poParent->SetGroupKnockout(group_knockout); |
1701 | | } |
1702 | | #endif |
1703 | | #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ |
1704 | | void Flush() override |
1705 | | { |
1706 | | return m_poParent->Flush(); |
1707 | | } |
1708 | | #endif |
1709 | | }; |
1710 | | |
1711 | | /************************************************************************/ |
1712 | | /* PDFiumRenderPageBitmap() */ |
1713 | | /************************************************************************/ |
1714 | | |
1715 | | /* This method is a customization of RenderPageImpl() |
1716 | | from pdfium/fpdfsdk/cpdfsdk_renderpage.cpp to allow selection of which OGC/layer are |
1717 | | active. Thus it inherits the following license */ |
1718 | | // Copyright 2014-2020 PDFium Authors. All rights reserved. |
1719 | | // |
1720 | | // Redistribution and use in source and binary forms, with or without |
1721 | | // modification, are permitted provided that the following conditions are |
1722 | | // met: |
1723 | | // |
1724 | | // * Redistributions of source code must retain the above copyright |
1725 | | // notice, this list of conditions and the following disclaimer. |
1726 | | // * Redistributions in binary form must reproduce the above |
1727 | | // copyright notice, this list of conditions and the following disclaimer |
1728 | | // in the documentation and/or other materials provided with the |
1729 | | // distribution. |
1730 | | // * Neither the name of Google Inc. nor the names of its |
1731 | | // contributors may be used to endorse or promote products derived from |
1732 | | // this software without specific prior written permission. |
1733 | | // |
1734 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
1735 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
1736 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
1737 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
1738 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
1739 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
1740 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
1741 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
1742 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1743 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
1744 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1745 | | |
1746 | | static void myRenderPageImpl(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1747 | | CPDF_Page *pPage, const CFX_Matrix &matrix, |
1748 | | const FX_RECT &clipping_rect, int flags, |
1749 | | const FPDF_COLORSCHEME *color_scheme, |
1750 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1751 | | { |
1752 | | if (!pContext->options_) |
1753 | | pContext->options_ = std::make_unique<CPDF_RenderOptions>(); |
1754 | | |
1755 | | auto &options = pContext->options_->GetOptions(); |
1756 | | options.bClearType = !!(flags & FPDF_LCD_TEXT); |
1757 | | options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT); |
1758 | | options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE); |
1759 | | options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE); |
1760 | | options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT); |
1761 | | options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE); |
1762 | | options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH); |
1763 | | |
1764 | | // Grayscale output |
1765 | | if (flags & FPDF_GRAYSCALE) |
1766 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kGray); |
1767 | | |
1768 | | if (color_scheme) |
1769 | | { |
1770 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kForcedColor); |
1771 | | SetColorFromScheme(color_scheme, pContext->options_.get()); |
1772 | | options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE); |
1773 | | } |
1774 | | |
1775 | | const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING) |
1776 | | ? CPDF_OCContext::kPrint |
1777 | | : CPDF_OCContext::kView; |
1778 | | pContext->options_->SetOCContext(pdfium::MakeRetain<GDALPDFiumOCContext>( |
1779 | | poDS, pPage->GetDocument(), usage)); |
1780 | | |
1781 | | pContext->device_->SaveState(); |
1782 | | pContext->device_->SetBaseClip(clipping_rect); |
1783 | | pContext->device_->SetClip_Rect(clipping_rect); |
1784 | | pContext->context_ = std::make_unique<CPDF_RenderContext>( |
1785 | | pPage->GetDocument(), pPage->GetMutablePageResources(), |
1786 | | pPage->GetPageImageCache()); |
1787 | | |
1788 | | pContext->context_->AppendLayer(pPage, matrix); |
1789 | | |
1790 | | if (flags & FPDF_ANNOT) |
1791 | | { |
1792 | | auto pOwnedList = std::make_unique<CPDF_AnnotList>(pPage); |
1793 | | CPDF_AnnotList *pList = pOwnedList.get(); |
1794 | | pContext->annots_ = std::move(pOwnedList); |
1795 | | bool bPrinting = |
1796 | | pContext->device_->GetDeviceType() != DeviceType::kDisplay; |
1797 | | |
1798 | | // TODO(https://crbug.com/pdfium/993) - maybe pass true here. |
1799 | | const bool bShowWidget = false; |
1800 | | pList->DisplayAnnots(pContext->context_.get(), bPrinting, matrix, |
1801 | | bShowWidget); |
1802 | | } |
1803 | | |
1804 | | pContext->renderer_ = std::make_unique<CPDF_ProgressiveRenderer>( |
1805 | | pContext->context_.get(), pContext->device_.get(), |
1806 | | pContext->options_.get()); |
1807 | | pContext->renderer_->Start(pause); |
1808 | | if (bNeedToRestore) |
1809 | | pContext->device_->RestoreState(false); |
1810 | | } |
1811 | | |
1812 | | static void |
1813 | | myRenderPageWithContext(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1814 | | FPDF_PAGE page, int start_x, int start_y, int size_x, |
1815 | | int size_y, int rotate, int flags, |
1816 | | const FPDF_COLORSCHEME *color_scheme, |
1817 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1818 | | { |
1819 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1820 | | if (!pPage) |
1821 | | return; |
1822 | | |
1823 | | const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); |
1824 | | myRenderPageImpl(poDS, pContext, pPage, |
1825 | | pPage->GetDisplayMatrixForRect(rect, rotate), rect, flags, |
1826 | | color_scheme, bNeedToRestore, pause); |
1827 | | } |
1828 | | |
1829 | | class MyRenderDevice final : public CFX_RenderDevice |
1830 | | { |
1831 | | |
1832 | | public: |
1833 | | // Substitution for CFX_DefaultRenderDevice::Attach |
1834 | | bool Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, bool bRgbByteOrder, |
1835 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1836 | | bool bGroupKnockout, const char *pszRenderingOptions); |
1837 | | }; |
1838 | | |
1839 | | bool MyRenderDevice::Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, |
1840 | | bool bRgbByteOrder, |
1841 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1842 | | bool bGroupKnockout, |
1843 | | const char *pszRenderingOptions) |
1844 | | { |
1845 | | SetBitmap(pBitmap); |
1846 | | |
1847 | | std::unique_ptr<RenderDeviceDriverIface> driver = |
1848 | | std::make_unique<pdfium::CFX_AggDeviceDriver>( |
1849 | | pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout); |
1850 | | if (pszRenderingOptions != nullptr) |
1851 | | { |
1852 | | int bEnableVector = FALSE; |
1853 | | int bEnableText = FALSE; |
1854 | | int bEnableBitmap = FALSE; |
1855 | | |
1856 | | char **papszTokens = CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
1857 | | for (int i = 0; papszTokens[i] != nullptr; i++) |
1858 | | { |
1859 | | if (EQUAL(papszTokens[i], "VECTOR")) |
1860 | | bEnableVector = TRUE; |
1861 | | else if (EQUAL(papszTokens[i], "TEXT")) |
1862 | | bEnableText = TRUE; |
1863 | | else if (EQUAL(papszTokens[i], "RASTER") || |
1864 | | EQUAL(papszTokens[i], "BITMAP")) |
1865 | | bEnableBitmap = TRUE; |
1866 | | else |
1867 | | { |
1868 | | CPLError(CE_Warning, CPLE_NotSupported, |
1869 | | "Value %s is not a valid value for " |
1870 | | "GDAL_PDF_RENDERING_OPTIONS", |
1871 | | papszTokens[i]); |
1872 | | } |
1873 | | } |
1874 | | CSLDestroy(papszTokens); |
1875 | | |
1876 | | if (!bEnableVector || !bEnableText || !bEnableBitmap) |
1877 | | { |
1878 | | std::unique_ptr<GDALPDFiumRenderDeviceDriver> poGDALRDDriver = |
1879 | | std::make_unique<GDALPDFiumRenderDeviceDriver>( |
1880 | | std::move(driver), this); |
1881 | | poGDALRDDriver->SetEnableVector(bEnableVector); |
1882 | | poGDALRDDriver->SetEnableText(bEnableText); |
1883 | | poGDALRDDriver->SetEnableBitmap(bEnableBitmap); |
1884 | | driver = std::move(poGDALRDDriver); |
1885 | | } |
1886 | | } |
1887 | | |
1888 | | SetDeviceDriver(std::move(driver)); |
1889 | | return true; |
1890 | | } |
1891 | | |
1892 | | void PDFDataset::PDFiumRenderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, |
1893 | | int start_x, int start_y, int size_x, |
1894 | | int size_y, |
1895 | | const char *pszRenderingOptions) |
1896 | | { |
1897 | | const int rotate = 0; |
1898 | | const int flags = 0; |
1899 | | |
1900 | | if (!bitmap) |
1901 | | return; |
1902 | | |
1903 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1904 | | if (!pPage) |
1905 | | return; |
1906 | | |
1907 | | auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>(); |
1908 | | CPDF_PageRenderContext *pContext = pOwnedContext.get(); |
1909 | | CPDF_Page::RenderContextClearer clearer(pPage); |
1910 | | pPage->SetRenderContext(std::move(pOwnedContext)); |
1911 | | |
1912 | | auto pOwnedDevice = std::make_unique<MyRenderDevice>(); |
1913 | | auto pDevice = pOwnedDevice.get(); |
1914 | | pContext->device_ = std::move(pOwnedDevice); |
1915 | | |
1916 | | RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); |
1917 | | |
1918 | | pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, |
1919 | | false, pszRenderingOptions); |
1920 | | |
1921 | | myRenderPageWithContext(this, pContext, page, start_x, start_y, size_x, |
1922 | | size_y, rotate, flags, |
1923 | | /*color_scheme=*/nullptr, |
1924 | | /*need_to_restore=*/true, /*pause=*/nullptr); |
1925 | | |
1926 | | #ifdef _SKIA_SUPPORT_PATHS_ |
1927 | | pDevice->Flush(true); |
1928 | | pBitmap->UnPreMultiply(); |
1929 | | #endif |
1930 | | } |
1931 | | |
1932 | | #endif /* HAVE_PDFIUM */ |
1933 | | |
1934 | | /************************************************************************/ |
1935 | | /* ReadPixels() */ |
1936 | | /************************************************************************/ |
1937 | | |
1938 | | CPLErr PDFDataset::ReadPixels(int nReqXOff, int nReqYOff, int nReqXSize, |
1939 | | int nReqYSize, GSpacing nPixelSpace, |
1940 | | GSpacing nLineSpace, GSpacing nBandSpace, |
1941 | | GByte *pabyData) |
1942 | 7.32k | { |
1943 | 7.32k | CPLErr eErr = CE_None; |
1944 | 7.32k | const char *pszRenderingOptions = |
1945 | 7.32k | GetOption(papszOpenOptions, "RENDERING_OPTIONS", nullptr); |
1946 | | |
1947 | 7.32k | #ifdef HAVE_POPPLER |
1948 | 7.32k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
1949 | 7.32k | { |
1950 | 7.32k | SplashColor sColor; |
1951 | 7.32k | sColor[0] = 255; |
1952 | 7.32k | sColor[1] = 255; |
1953 | 7.32k | sColor[2] = 255; |
1954 | 7.32k | GDALPDFOutputDev *poSplashOut = new GDALPDFOutputDev( |
1955 | 7.32k | (nBands < 4) ? splashModeRGB8 : splashModeXBGR8, 4, false, |
1956 | 7.32k | (nBands < 4) ? sColor : nullptr); |
1957 | | |
1958 | 7.32k | if (pszRenderingOptions != nullptr) |
1959 | 7.32k | { |
1960 | 7.32k | poSplashOut->SetEnableVector(FALSE); |
1961 | 7.32k | poSplashOut->SetEnableText(FALSE); |
1962 | 7.32k | poSplashOut->SetEnableBitmap(FALSE); |
1963 | | |
1964 | 7.32k | char **papszTokens = |
1965 | 7.32k | CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
1966 | 21.9k | for (int i = 0; papszTokens[i] != nullptr; i++) |
1967 | 14.6k | { |
1968 | 14.6k | if (EQUAL(papszTokens[i], "VECTOR")) |
1969 | 7.32k | poSplashOut->SetEnableVector(TRUE); |
1970 | 7.32k | else if (EQUAL(papszTokens[i], "TEXT")) |
1971 | 0 | poSplashOut->SetEnableText(TRUE); |
1972 | 7.32k | else if (EQUAL(papszTokens[i], "RASTER") || |
1973 | 0 | EQUAL(papszTokens[i], "BITMAP")) |
1974 | 7.32k | poSplashOut->SetEnableBitmap(TRUE); |
1975 | 0 | else |
1976 | 0 | { |
1977 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1978 | 0 | "Value %s is not a valid value for " |
1979 | 0 | "GDAL_PDF_RENDERING_OPTIONS", |
1980 | 0 | papszTokens[i]); |
1981 | 0 | } |
1982 | 14.6k | } |
1983 | 7.32k | CSLDestroy(papszTokens); |
1984 | 7.32k | } |
1985 | | |
1986 | 7.32k | PDFDoc *poDoc = m_poDocPoppler; |
1987 | 7.32k | poSplashOut->startDoc(poDoc); |
1988 | | |
1989 | | // Note: Poppler 25.2 is certainly not the lowest version where we can |
1990 | | // avoid the hack. |
1991 | 7.32k | #if !(POPPLER_MAJOR_VERSION > 25 || \ |
1992 | 7.32k | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)) |
1993 | 7.32k | #define USE_OPTCONTENT_HACK |
1994 | 7.32k | #endif |
1995 | | |
1996 | 7.32k | #ifdef USE_OPTCONTENT_HACK |
1997 | | /* EVIL: we modify a private member... */ |
1998 | | /* poppler (at least 0.12 and 0.14 versions) don't render correctly */ |
1999 | | /* some PDFs and display an error message 'Could not find a OCG with |
2000 | | * Ref' */ |
2001 | | /* in those cases. This processing of optional content is an addition of |
2002 | | */ |
2003 | | /* poppler in comparison to original xpdf, which hasn't the issue. All |
2004 | | * in */ |
2005 | | /* all, nullifying optContent removes the error message and improves the |
2006 | | * rendering */ |
2007 | 7.32k | Catalog *poCatalog = poDoc->getCatalog(); |
2008 | 7.32k | OCGs *poOldOCGs = poCatalog->optContent; |
2009 | 7.32k | if (!m_bUseOCG) |
2010 | 7.32k | poCatalog->optContent = nullptr; |
2011 | 7.32k | #endif |
2012 | 7.32k | try |
2013 | 7.32k | { |
2014 | 7.32k | poDoc->displayPageSlice(poSplashOut, m_iPage, m_dfDPI, m_dfDPI, 0, |
2015 | 7.32k | TRUE, false, false, nReqXOff, nReqYOff, |
2016 | 7.32k | nReqXSize, nReqYSize); |
2017 | 7.32k | } |
2018 | 7.32k | catch (const std::exception &e) |
2019 | 7.32k | { |
2020 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2021 | 0 | "PDFDoc::displayPageSlice() failed with %s", e.what()); |
2022 | |
|
2023 | 0 | #ifdef USE_OPTCONTENT_HACK |
2024 | | /* Restore back */ |
2025 | 0 | poCatalog->optContent = poOldOCGs; |
2026 | 0 | #endif |
2027 | 0 | delete poSplashOut; |
2028 | 0 | return CE_Failure; |
2029 | 0 | } |
2030 | | |
2031 | 0 | #ifdef USE_OPTCONTENT_HACK |
2032 | | /* Restore back */ |
2033 | 7.32k | poCatalog->optContent = poOldOCGs; |
2034 | 7.32k | #endif |
2035 | | |
2036 | 7.32k | SplashBitmap *poBitmap = poSplashOut->getBitmap(); |
2037 | 7.32k | if (poBitmap->getWidth() != nReqXSize || |
2038 | 7.32k | poBitmap->getHeight() != nReqYSize) |
2039 | 0 | { |
2040 | 0 | CPLError( |
2041 | 0 | CE_Failure, CPLE_AppDefined, |
2042 | 0 | "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)", |
2043 | 0 | poBitmap->getWidth(), poBitmap->getHeight(), nReqXSize, |
2044 | 0 | nReqYSize); |
2045 | 0 | delete poSplashOut; |
2046 | 0 | return CE_Failure; |
2047 | 0 | } |
2048 | | |
2049 | 7.32k | GByte *pabyDataR = pabyData; |
2050 | 7.32k | GByte *pabyDataG = pabyData + nBandSpace; |
2051 | 7.32k | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2052 | 7.32k | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2053 | 7.32k | GByte *pabySrc = poBitmap->getDataPtr(); |
2054 | 7.32k | GByte *pabyAlphaSrc = |
2055 | 7.32k | reinterpret_cast<GByte *>(poBitmap->getAlphaPtr()); |
2056 | 7.32k | int i, j; |
2057 | 9.47M | for (j = 0; j < nReqYSize; j++) |
2058 | 9.46M | { |
2059 | 12.1G | for (i = 0; i < nReqXSize; i++) |
2060 | 12.1G | { |
2061 | 12.1G | if (nBands < 4) |
2062 | 12.1G | { |
2063 | 12.1G | pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0]; |
2064 | 12.1G | pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1]; |
2065 | 12.1G | pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2]; |
2066 | 12.1G | } |
2067 | 0 | else |
2068 | 0 | { |
2069 | 0 | pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2]; |
2070 | 0 | pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1]; |
2071 | 0 | pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0]; |
2072 | 0 | pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i]; |
2073 | 0 | } |
2074 | 12.1G | } |
2075 | 9.46M | pabyDataR += nLineSpace; |
2076 | 9.46M | pabyDataG += nLineSpace; |
2077 | 9.46M | pabyDataB += nLineSpace; |
2078 | 9.46M | pabyDataA += nLineSpace; |
2079 | 9.46M | pabyAlphaSrc += poBitmap->getAlphaRowSize(); |
2080 | 9.46M | pabySrc += poBitmap->getRowSize(); |
2081 | 9.46M | } |
2082 | 7.32k | delete poSplashOut; |
2083 | 7.32k | } |
2084 | 7.32k | #endif // HAVE_POPPLER |
2085 | | |
2086 | | #ifdef HAVE_PODOFO |
2087 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2088 | | { |
2089 | | if (m_bPdfToPpmFailed) |
2090 | | return CE_Failure; |
2091 | | |
2092 | | if (pszRenderingOptions != nullptr && |
2093 | | !EQUAL(pszRenderingOptions, "RASTER,VECTOR,TEXT")) |
2094 | | { |
2095 | | CPLError(CE_Warning, CPLE_NotSupported, |
2096 | | "GDAL_PDF_RENDERING_OPTIONS only supported " |
2097 | | "when PDF lib is Poppler."); |
2098 | | } |
2099 | | |
2100 | | CPLString osTmpFilename; |
2101 | | int nRet; |
2102 | | |
2103 | | #ifdef notdef |
2104 | | int bUseSpawn = |
2105 | | CPLTestBool(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES")); |
2106 | | if (!bUseSpawn) |
2107 | | { |
2108 | | CPLString osCmd = CPLSPrintf( |
2109 | | "pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"", |
2110 | | dfDPI, nReqXOff, nReqYOff, nReqXSize, nReqYSize, iPage, iPage, |
2111 | | osFilename.c_str()); |
2112 | | |
2113 | | if (!osUserPwd.empty()) |
2114 | | { |
2115 | | osCmd += " -upw \""; |
2116 | | osCmd += osUserPwd; |
2117 | | osCmd += "\""; |
2118 | | } |
2119 | | |
2120 | | CPLString osTmpFilenamePrefix = CPLGenerateTempFilenameSafe("pdf"); |
2121 | | osTmpFilename = |
2122 | | CPLSPrintf("%s-%d.ppm", osTmpFilenamePrefix.c_str(), iPage); |
2123 | | osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str()); |
2124 | | |
2125 | | CPLDebug("PDF", "Running '%s'", osCmd.c_str()); |
2126 | | nRet = CPLSystem(nullptr, osCmd.c_str()); |
2127 | | } |
2128 | | else |
2129 | | #endif // notdef |
2130 | | { |
2131 | | char **papszArgs = nullptr; |
2132 | | papszArgs = CSLAddString(papszArgs, "pdftoppm"); |
2133 | | papszArgs = CSLAddString(papszArgs, "-r"); |
2134 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", m_dfDPI)); |
2135 | | papszArgs = CSLAddString(papszArgs, "-x"); |
2136 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff)); |
2137 | | papszArgs = CSLAddString(papszArgs, "-y"); |
2138 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff)); |
2139 | | papszArgs = CSLAddString(papszArgs, "-W"); |
2140 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize)); |
2141 | | papszArgs = CSLAddString(papszArgs, "-H"); |
2142 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize)); |
2143 | | papszArgs = CSLAddString(papszArgs, "-f"); |
2144 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2145 | | papszArgs = CSLAddString(papszArgs, "-l"); |
2146 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2147 | | if (!m_osUserPwd.empty()) |
2148 | | { |
2149 | | papszArgs = CSLAddString(papszArgs, "-upw"); |
2150 | | papszArgs = CSLAddString(papszArgs, m_osUserPwd.c_str()); |
2151 | | } |
2152 | | papszArgs = CSLAddString(papszArgs, m_osFilename.c_str()); |
2153 | | |
2154 | | osTmpFilename = VSIMemGenerateHiddenFilename("pdf_temp.ppm"); |
2155 | | VSILFILE *fpOut = VSIFOpenL(osTmpFilename, "wb"); |
2156 | | if (fpOut != nullptr) |
2157 | | { |
2158 | | nRet = CPLSpawn(papszArgs, nullptr, fpOut, FALSE); |
2159 | | VSIFCloseL(fpOut); |
2160 | | } |
2161 | | else |
2162 | | nRet = -1; |
2163 | | |
2164 | | CSLDestroy(papszArgs); |
2165 | | } |
2166 | | |
2167 | | if (nRet == 0) |
2168 | | { |
2169 | | auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
2170 | | osTmpFilename, GDAL_OF_RASTER, nullptr, nullptr, nullptr)); |
2171 | | if (poDS) |
2172 | | { |
2173 | | if (poDS->GetRasterCount() == 3) |
2174 | | { |
2175 | | eErr = poDS->RasterIO(GF_Read, 0, 0, nReqXSize, nReqYSize, |
2176 | | pabyData, nReqXSize, nReqYSize, |
2177 | | GDT_UInt8, 3, nullptr, nPixelSpace, |
2178 | | nLineSpace, nBandSpace, nullptr); |
2179 | | } |
2180 | | } |
2181 | | } |
2182 | | else |
2183 | | { |
2184 | | CPLDebug("PDF", "Ret code = %d", nRet); |
2185 | | m_bPdfToPpmFailed = true; |
2186 | | eErr = CE_Failure; |
2187 | | } |
2188 | | VSIUnlink(osTmpFilename); |
2189 | | } |
2190 | | #endif // HAVE_PODOFO |
2191 | | #ifdef HAVE_PDFIUM |
2192 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2193 | | { |
2194 | | if (!m_poPagePdfium) |
2195 | | { |
2196 | | return CE_Failure; |
2197 | | } |
2198 | | |
2199 | | // Pdfium does not support multithreading |
2200 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, PDFIUM_MUTEX_TIMEOUT); |
2201 | | |
2202 | | CPLCreateOrAcquireMutex(&(m_poPagePdfium->readMutex), |
2203 | | PDFIUM_MUTEX_TIMEOUT); |
2204 | | |
2205 | | // Parsing content required before rastering |
2206 | | // can takes too long for PDF with large number of objects/layers |
2207 | | m_poPagePdfium->page->ParseContent(); |
2208 | | |
2209 | | FPDF_BITMAP bitmap = |
2210 | | FPDFBitmap_Create(nReqXSize, nReqYSize, nBands == 4 /*alpha*/); |
2211 | | // As coded now, FPDFBitmap_Create cannot allocate more than 1 GB |
2212 | | if (bitmap == nullptr) |
2213 | | { |
2214 | | // Release mutex - following code is thread-safe |
2215 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2216 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2217 | | |
2218 | | #ifdef notdef |
2219 | | // If the requested area is not too small, then try subdividing |
2220 | | if ((GIntBig)nReqXSize * nReqYSize * 4 > 1024 * 1024) |
2221 | | { |
2222 | | #ifdef DEBUG |
2223 | | CPLDebug( |
2224 | | "PDF", |
2225 | | "Subdividing PDFDataset::ReadPixels(%d, %d, %d, %d, " |
2226 | | "scaleFactor=%d)", |
2227 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2228 | | 1 << ((PDFRasterBand *)GetRasterBand(1))->nResolutionLevel); |
2229 | | #endif |
2230 | | if (nReqXSize >= nReqYSize) |
2231 | | { |
2232 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize / 2, |
2233 | | nReqYSize, nPixelSpace, nLineSpace, |
2234 | | nBandSpace, pabyData); |
2235 | | if (eErr == CE_None) |
2236 | | { |
2237 | | eErr = ReadPixels( |
2238 | | nReqXSize / 2, nReqYOff, nReqXSize - nReqXSize / 2, |
2239 | | nReqYSize, nPixelSpace, nLineSpace, nBandSpace, |
2240 | | pabyData + nPixelSpace * (nReqXSize / 2)); |
2241 | | } |
2242 | | } |
2243 | | else |
2244 | | { |
2245 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
2246 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2247 | | nLineSpace, nBandSpace, pabyData); |
2248 | | if (eErr == CE_None) |
2249 | | { |
2250 | | eErr = |
2251 | | ReadPixels(nReqXOff, nReqYSize / 2, nReqXSize, |
2252 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2253 | | nLineSpace, nBandSpace, |
2254 | | pabyData + nLineSpace * (nReqYSize / 2)); |
2255 | | } |
2256 | | } |
2257 | | return eErr; |
2258 | | } |
2259 | | #endif |
2260 | | |
2261 | | CPLError(CE_Failure, CPLE_AppDefined, |
2262 | | "FPDFBitmap_Create(%d,%d) failed", nReqXSize, nReqYSize); |
2263 | | |
2264 | | return CE_Failure; |
2265 | | } |
2266 | | // alpha is 0% which is transported to FF if not alpha |
2267 | | // Default background color is white |
2268 | | FPDF_DWORD color = 0x00FFFFFF; // A,R,G,B |
2269 | | FPDFBitmap_FillRect(bitmap, 0, 0, nReqXSize, nReqYSize, color); |
2270 | | |
2271 | | #ifdef DEBUG |
2272 | | // start_x, start_y, size_x, size_y, rotate, flags |
2273 | | CPLDebug("PDF", |
2274 | | "PDFDataset::ReadPixels(%d, %d, %d, %d, scaleFactor=%d)", |
2275 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2276 | | 1 << cpl::down_cast<PDFRasterBand *>(GetRasterBand(1)) |
2277 | | ->nResolutionLevel); |
2278 | | |
2279 | | CPLDebug("PDF", "FPDF_RenderPageBitmap(%d, %d, %d, %d)", -nReqXOff, |
2280 | | -nReqYOff, nRasterXSize, nRasterYSize); |
2281 | | #endif |
2282 | | |
2283 | | // Part of PDF is render with -x, -y, page_width, page_height |
2284 | | // (not requested size!) |
2285 | | PDFiumRenderPageBitmap( |
2286 | | bitmap, FPDFPageFromIPDFPage(m_poPagePdfium->page), -nReqXOff, |
2287 | | -nReqYOff, nRasterXSize, nRasterYSize, pszRenderingOptions); |
2288 | | |
2289 | | int stride = FPDFBitmap_GetStride(bitmap); |
2290 | | const GByte *buffer = |
2291 | | reinterpret_cast<const GByte *>(FPDFBitmap_GetBuffer(bitmap)); |
2292 | | |
2293 | | // Release mutex - following code is thread-safe |
2294 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2295 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2296 | | |
2297 | | // Source data is B, G, R, unused. |
2298 | | // Destination data is R, G, B (,A if is alpha) |
2299 | | GByte *pabyDataR = pabyData; |
2300 | | GByte *pabyDataG = pabyData + 1 * nBandSpace; |
2301 | | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2302 | | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2303 | | // Copied from Poppler |
2304 | | int i, j; |
2305 | | for (j = 0; j < nReqYSize; j++) |
2306 | | { |
2307 | | for (i = 0; i < nReqXSize; i++) |
2308 | | { |
2309 | | pabyDataR[i * nPixelSpace] = buffer[(i * 4) + 2]; |
2310 | | pabyDataG[i * nPixelSpace] = buffer[(i * 4) + 1]; |
2311 | | pabyDataB[i * nPixelSpace] = buffer[(i * 4) + 0]; |
2312 | | if (nBands == 4) |
2313 | | { |
2314 | | pabyDataA[i * nPixelSpace] = buffer[(i * 4) + 3]; |
2315 | | } |
2316 | | } |
2317 | | pabyDataR += nLineSpace; |
2318 | | pabyDataG += nLineSpace; |
2319 | | pabyDataB += nLineSpace; |
2320 | | pabyDataA += nLineSpace; |
2321 | | buffer += stride; |
2322 | | } |
2323 | | FPDFBitmap_Destroy(bitmap); |
2324 | | } |
2325 | | #endif // ~ HAVE_PDFIUM |
2326 | | |
2327 | 7.32k | return eErr; |
2328 | 7.32k | } |
2329 | | |
2330 | | /************************************************************************/ |
2331 | | /* ==================================================================== */ |
2332 | | /* PDFImageRasterBand */ |
2333 | | /* ==================================================================== */ |
2334 | | /************************************************************************/ |
2335 | | |
2336 | | class PDFImageRasterBand final : public PDFRasterBand |
2337 | | { |
2338 | | friend class PDFDataset; |
2339 | | |
2340 | | public: |
2341 | | PDFImageRasterBand(PDFDataset *, int); |
2342 | | |
2343 | | CPLErr IReadBlock(int, int, void *) override; |
2344 | | }; |
2345 | | |
2346 | | /************************************************************************/ |
2347 | | /* PDFImageRasterBand() */ |
2348 | | /************************************************************************/ |
2349 | | |
2350 | | PDFImageRasterBand::PDFImageRasterBand(PDFDataset *poDSIn, int nBandIn) |
2351 | 0 | : PDFRasterBand(poDSIn, nBandIn, 0) |
2352 | 0 | { |
2353 | 0 | } |
2354 | | |
2355 | | /************************************************************************/ |
2356 | | /* IReadBlock() */ |
2357 | | /************************************************************************/ |
2358 | | |
2359 | | CPLErr PDFImageRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
2360 | | void *pImage) |
2361 | 0 | { |
2362 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2363 | 0 | CPLAssert(poGDS->m_poImageObj != nullptr); |
2364 | |
|
2365 | 0 | if (!poGDS->m_bTried) |
2366 | 0 | { |
2367 | 0 | int nBands = (poGDS->nBands == 1) ? 1 : 3; |
2368 | 0 | poGDS->m_bTried = true; |
2369 | 0 | if (nBands == 3) |
2370 | 0 | { |
2371 | 0 | poGDS->m_pabyCachedData = static_cast<GByte *>( |
2372 | 0 | VSIMalloc3(nBands, nRasterXSize, nRasterYSize)); |
2373 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2374 | 0 | return CE_Failure; |
2375 | 0 | } |
2376 | | |
2377 | 0 | GDALPDFStream *poStream = poGDS->m_poImageObj->GetStream(); |
2378 | 0 | GByte *pabyStream = nullptr; |
2379 | |
|
2380 | 0 | if (poStream == nullptr || |
2381 | 0 | static_cast<size_t>(poStream->GetLength()) != |
2382 | 0 | static_cast<size_t>(nBands) * nRasterXSize * nRasterYSize || |
2383 | 0 | (pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes())) == |
2384 | 0 | nullptr) |
2385 | 0 | { |
2386 | 0 | VSIFree(poGDS->m_pabyCachedData); |
2387 | 0 | poGDS->m_pabyCachedData = nullptr; |
2388 | 0 | return CE_Failure; |
2389 | 0 | } |
2390 | | |
2391 | 0 | if (nBands == 3) |
2392 | 0 | { |
2393 | | /* pixel interleaved to band interleaved */ |
2394 | 0 | for (size_t i = 0; |
2395 | 0 | i < static_cast<size_t>(nRasterXSize) * nRasterYSize; i++) |
2396 | 0 | { |
2397 | 0 | poGDS->m_pabyCachedData[0 * static_cast<size_t>(nRasterXSize) * |
2398 | 0 | nRasterYSize + |
2399 | 0 | i] = pabyStream[3 * i + 0]; |
2400 | 0 | poGDS->m_pabyCachedData[1 * static_cast<size_t>(nRasterXSize) * |
2401 | 0 | nRasterYSize + |
2402 | 0 | i] = pabyStream[3 * i + 1]; |
2403 | 0 | poGDS->m_pabyCachedData[2 * static_cast<size_t>(nRasterXSize) * |
2404 | 0 | nRasterYSize + |
2405 | 0 | i] = pabyStream[3 * i + 2]; |
2406 | 0 | } |
2407 | 0 | VSIFree(pabyStream); |
2408 | 0 | } |
2409 | 0 | else |
2410 | 0 | poGDS->m_pabyCachedData = pabyStream; |
2411 | 0 | } |
2412 | | |
2413 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2414 | 0 | return CE_Failure; |
2415 | | |
2416 | 0 | if (nBand == 4) |
2417 | 0 | memset(pImage, 255, nRasterXSize); |
2418 | 0 | else |
2419 | 0 | memcpy(pImage, |
2420 | 0 | poGDS->m_pabyCachedData + |
2421 | 0 | static_cast<size_t>(nBand - 1) * nRasterXSize * |
2422 | 0 | nRasterYSize + |
2423 | 0 | static_cast<size_t>(nBlockYOff) * nRasterXSize, |
2424 | 0 | nRasterXSize); |
2425 | |
|
2426 | 0 | return CE_None; |
2427 | 0 | } |
2428 | | |
2429 | | /************************************************************************/ |
2430 | | /* PDFDataset() */ |
2431 | | /************************************************************************/ |
2432 | | |
2433 | | PDFDataset::PDFDataset(PDFDataset *poParentDSIn, int nXSize, int nYSize) |
2434 | 32.6k | : m_bIsOvrDS(poParentDSIn != nullptr), |
2435 | | #ifdef HAVE_PDFIUM |
2436 | | m_poDocPdfium(poParentDSIn ? poParentDSIn->m_poDocPdfium : nullptr), |
2437 | | m_poPagePdfium(poParentDSIn ? poParentDSIn->m_poPagePdfium : nullptr), |
2438 | | #endif |
2439 | 32.6k | m_bSetStyle(CPLTestBool(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES"))) |
2440 | 32.6k | { |
2441 | 32.6k | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
2442 | 32.6k | nRasterXSize = nXSize; |
2443 | 32.6k | nRasterYSize = nYSize; |
2444 | 32.6k | if (poParentDSIn) |
2445 | 0 | m_bUseLib = poParentDSIn->m_bUseLib; |
2446 | | |
2447 | 32.6k | InitMapOperators(); |
2448 | 32.6k | } |
2449 | | |
2450 | | /************************************************************************/ |
2451 | | /* IBuildOverviews() */ |
2452 | | /************************************************************************/ |
2453 | | |
2454 | | CPLErr PDFDataset::IBuildOverviews(const char *pszResampling, int nOverviews, |
2455 | | const int *panOverviewList, int nListBands, |
2456 | | const int *panBandList, |
2457 | | GDALProgressFunc pfnProgress, |
2458 | | void *pProgressData, |
2459 | | CSLConstList papszOptions) |
2460 | | |
2461 | 0 | { |
2462 | | /* -------------------------------------------------------------------- */ |
2463 | | /* In order for building external overviews to work properly we */ |
2464 | | /* discard any concept of internal overviews when the user */ |
2465 | | /* first requests to build external overviews. */ |
2466 | | /* -------------------------------------------------------------------- */ |
2467 | 0 | if (!m_apoOvrDS.empty()) |
2468 | 0 | { |
2469 | 0 | m_apoOvrDSBackup = std::move(m_apoOvrDS); |
2470 | 0 | m_apoOvrDS.clear(); |
2471 | 0 | } |
2472 | | |
2473 | | // Prevents InitOverviews() to run |
2474 | 0 | m_apoOvrDSBackup.emplace_back(nullptr); |
2475 | 0 | const CPLErr eErr = GDALPamDataset::IBuildOverviews( |
2476 | 0 | pszResampling, nOverviews, panOverviewList, nListBands, panBandList, |
2477 | 0 | pfnProgress, pProgressData, papszOptions); |
2478 | 0 | m_apoOvrDSBackup.pop_back(); |
2479 | 0 | return eErr; |
2480 | 0 | } |
2481 | | |
2482 | | /************************************************************************/ |
2483 | | /* PDFFreeDoc() */ |
2484 | | /************************************************************************/ |
2485 | | |
2486 | | #ifdef HAVE_POPPLER |
2487 | | static void PDFFreeDoc(PDFDoc *poDoc) |
2488 | 43.4k | { |
2489 | 43.4k | if (poDoc) |
2490 | 43.4k | { |
2491 | 43.4k | #if POPPLER_MAJOR_VERSION < 26 || \ |
2492 | 43.4k | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION < 2) |
2493 | | /* hack to avoid potential cross heap issues on Win32 */ |
2494 | | /* str is the VSIPDFFileStream object passed in the constructor of |
2495 | | * PDFDoc */ |
2496 | | // NOTE: This is potentially very dangerous. See comment in |
2497 | | // VSIPDFFileStream::FillBuffer() */ |
2498 | 43.4k | delete poDoc->str; |
2499 | 43.4k | poDoc->str = nullptr; |
2500 | 43.4k | #endif |
2501 | | |
2502 | 43.4k | delete poDoc; |
2503 | 43.4k | } |
2504 | 43.4k | } |
2505 | | #endif |
2506 | | |
2507 | | /************************************************************************/ |
2508 | | /* GetCatalog() */ |
2509 | | /************************************************************************/ |
2510 | | |
2511 | | GDALPDFObject *PDFDataset::GetCatalog() |
2512 | 78.6k | { |
2513 | 78.6k | if (m_poCatalogObject) |
2514 | 45.9k | return m_poCatalogObject; |
2515 | | |
2516 | 32.6k | #ifdef HAVE_POPPLER |
2517 | 32.6k | if (m_bUseLib.test(PDFLIB_POPPLER) && m_poDocPoppler) |
2518 | 32.6k | { |
2519 | 32.6k | m_poCatalogObjectPoppler = |
2520 | 32.6k | std::make_unique<Object>(m_poDocPoppler->getXRef()->getCatalog()); |
2521 | 32.6k | if (!m_poCatalogObjectPoppler->isNull()) |
2522 | 32.6k | m_poCatalogObject = |
2523 | 32.6k | new GDALPDFObjectPoppler(m_poCatalogObjectPoppler.get(), FALSE); |
2524 | 32.6k | } |
2525 | 32.6k | #endif |
2526 | | |
2527 | | #ifdef HAVE_PODOFO |
2528 | | if (m_bUseLib.test(PDFLIB_PODOFO) && m_poDocPodofo) |
2529 | | { |
2530 | | int nCatalogNum = 0; |
2531 | | int nCatalogGen = 0; |
2532 | | VSILFILE *fp = VSIFOpenL(m_osFilename.c_str(), "rb"); |
2533 | | if (fp != nullptr) |
2534 | | { |
2535 | | GDALPDFUpdateWriter oWriter(fp); |
2536 | | if (oWriter.ParseTrailerAndXRef()) |
2537 | | { |
2538 | | nCatalogNum = oWriter.GetCatalogNum().toInt(); |
2539 | | nCatalogGen = oWriter.GetCatalogGen(); |
2540 | | } |
2541 | | oWriter.Close(); |
2542 | | } |
2543 | | |
2544 | | PoDoFo::PdfObject *poCatalogPodofo = |
2545 | | m_poDocPodofo->GetObjects().GetObject( |
2546 | | PoDoFo::PdfReference(nCatalogNum, nCatalogGen)); |
2547 | | if (poCatalogPodofo) |
2548 | | m_poCatalogObject = new GDALPDFObjectPodofo( |
2549 | | poCatalogPodofo, m_poDocPodofo->GetObjects()); |
2550 | | } |
2551 | | #endif |
2552 | | |
2553 | | #ifdef HAVE_PDFIUM |
2554 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_poDocPdfium) |
2555 | | { |
2556 | | RetainPtr<CPDF_Dictionary> catalog = |
2557 | | m_poDocPdfium->doc->GetMutableRoot(); |
2558 | | if (catalog) |
2559 | | m_poCatalogObject = GDALPDFObjectPdfium::Build(catalog); |
2560 | | } |
2561 | | #endif // ~ HAVE_PDFIUM |
2562 | | |
2563 | 32.6k | return m_poCatalogObject; |
2564 | 78.6k | } |
2565 | | |
2566 | | /************************************************************************/ |
2567 | | /* ~PDFDataset() */ |
2568 | | /************************************************************************/ |
2569 | | |
2570 | | PDFDataset::~PDFDataset() |
2571 | 32.6k | { |
2572 | | #ifdef HAVE_PDFIUM |
2573 | | m_apoOvrDS.clear(); |
2574 | | m_apoOvrDSBackup.clear(); |
2575 | | #endif |
2576 | | |
2577 | 32.6k | CPLFree(m_pabyCachedData); |
2578 | 32.6k | m_pabyCachedData = nullptr; |
2579 | | |
2580 | 32.6k | delete m_poNeatLine; |
2581 | 32.6k | m_poNeatLine = nullptr; |
2582 | | |
2583 | | /* Collect data necessary to update */ |
2584 | 32.6k | int nNum = 0; |
2585 | 32.6k | int nGen = 0; |
2586 | 32.6k | GDALPDFDictionaryRW *poPageDictCopy = nullptr; |
2587 | 32.6k | GDALPDFDictionaryRW *poCatalogDictCopy = nullptr; |
2588 | 32.6k | if (m_poPageObj) |
2589 | 32.6k | { |
2590 | 32.6k | nNum = m_poPageObj->GetRefNum().toInt(); |
2591 | 32.6k | nGen = m_poPageObj->GetRefGen(); |
2592 | 32.6k | if (eAccess == GA_Update && |
2593 | 0 | (m_bProjDirty || m_bNeatLineDirty || m_bInfoDirty || m_bXMPDirty) && |
2594 | 0 | nNum != 0 && m_poPageObj != nullptr && |
2595 | 0 | m_poPageObj->GetType() == PDFObjectType_Dictionary) |
2596 | 0 | { |
2597 | 0 | poPageDictCopy = m_poPageObj->GetDictionary()->Clone(); |
2598 | |
|
2599 | 0 | if (m_bXMPDirty) |
2600 | 0 | { |
2601 | | /* We need the catalog because it points to the XMP Metadata |
2602 | | * object */ |
2603 | 0 | GetCatalog(); |
2604 | 0 | if (m_poCatalogObject && |
2605 | 0 | m_poCatalogObject->GetType() == PDFObjectType_Dictionary) |
2606 | 0 | poCatalogDictCopy = |
2607 | 0 | m_poCatalogObject->GetDictionary()->Clone(); |
2608 | 0 | } |
2609 | 0 | } |
2610 | 32.6k | } |
2611 | | |
2612 | | /* Close document (and file descriptor) to be able to open it */ |
2613 | | /* in read-write mode afterwards */ |
2614 | 32.6k | delete m_poPageObj; |
2615 | 32.6k | m_poPageObj = nullptr; |
2616 | 32.6k | delete m_poCatalogObject; |
2617 | 32.6k | m_poCatalogObject = nullptr; |
2618 | 32.6k | #ifdef HAVE_POPPLER |
2619 | 32.6k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
2620 | 32.6k | { |
2621 | 32.6k | m_poCatalogObjectPoppler.reset(); |
2622 | 32.6k | PDFFreeDoc(m_poDocPoppler); |
2623 | 32.6k | } |
2624 | 32.6k | m_poDocPoppler = nullptr; |
2625 | 32.6k | #endif |
2626 | | #ifdef HAVE_PODOFO |
2627 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2628 | | { |
2629 | | delete m_poDocPodofo; |
2630 | | } |
2631 | | m_poDocPodofo = nullptr; |
2632 | | #endif |
2633 | | #ifdef HAVE_PDFIUM |
2634 | | if (!m_bIsOvrDS) |
2635 | | { |
2636 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2637 | | { |
2638 | | UnloadPdfiumDocumentPage(&m_poDocPdfium, &m_poPagePdfium); |
2639 | | } |
2640 | | } |
2641 | | m_poDocPdfium = nullptr; |
2642 | | m_poPagePdfium = nullptr; |
2643 | | #endif // ~ HAVE_PDFIUM |
2644 | | |
2645 | 32.6k | m_bHasLoadedLayers = true; |
2646 | 32.6k | m_apoLayers.clear(); |
2647 | | |
2648 | | /* Now do the update */ |
2649 | 32.6k | if (poPageDictCopy) |
2650 | 0 | { |
2651 | 0 | VSILFILE *fp = VSIFOpenL(m_osFilename, "rb+"); |
2652 | 0 | if (fp != nullptr) |
2653 | 0 | { |
2654 | 0 | GDALPDFUpdateWriter oWriter(fp); |
2655 | 0 | if (oWriter.ParseTrailerAndXRef()) |
2656 | 0 | { |
2657 | 0 | if ((m_bProjDirty || m_bNeatLineDirty) && |
2658 | 0 | poPageDictCopy != nullptr) |
2659 | 0 | oWriter.UpdateProj(this, m_dfDPI, poPageDictCopy, |
2660 | 0 | GDALPDFObjectNum(nNum), nGen); |
2661 | |
|
2662 | 0 | if (m_bInfoDirty) |
2663 | 0 | oWriter.UpdateInfo(this); |
2664 | |
|
2665 | 0 | if (m_bXMPDirty && poCatalogDictCopy != nullptr) |
2666 | 0 | oWriter.UpdateXMP(this, poCatalogDictCopy); |
2667 | 0 | } |
2668 | 0 | oWriter.Close(); |
2669 | 0 | } |
2670 | 0 | else |
2671 | 0 | { |
2672 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2673 | 0 | "Cannot open %s in update mode", m_osFilename.c_str()); |
2674 | 0 | } |
2675 | 0 | } |
2676 | 32.6k | delete poPageDictCopy; |
2677 | 32.6k | poPageDictCopy = nullptr; |
2678 | 32.6k | delete poCatalogDictCopy; |
2679 | 32.6k | poCatalogDictCopy = nullptr; |
2680 | | |
2681 | 32.6k | if (m_nGCPCount > 0) |
2682 | 23 | { |
2683 | 23 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
2684 | 23 | CPLFree(m_pasGCPList); |
2685 | 23 | m_pasGCPList = nullptr; |
2686 | 23 | m_nGCPCount = 0; |
2687 | 23 | } |
2688 | | |
2689 | 32.6k | CleanupIntermediateResources(); |
2690 | | |
2691 | | // Do that only after having destroyed Poppler objects |
2692 | 32.6k | m_fp.reset(); |
2693 | 32.6k | } |
2694 | | |
2695 | | /************************************************************************/ |
2696 | | /* IRasterIO() */ |
2697 | | /************************************************************************/ |
2698 | | |
2699 | | CPLErr PDFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2700 | | int nXSize, int nYSize, void *pData, int nBufXSize, |
2701 | | int nBufYSize, GDALDataType eBufType, |
2702 | | int nBandCount, BANDMAP_TYPE panBandMap, |
2703 | | GSpacing nPixelSpace, GSpacing nLineSpace, |
2704 | | GSpacing nBandSpace, |
2705 | | GDALRasterIOExtraArg *psExtraArg) |
2706 | 0 | { |
2707 | | // Try to pass the request to the most appropriate overview dataset. |
2708 | 0 | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2709 | 0 | { |
2710 | 0 | int bTried = FALSE; |
2711 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2712 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2713 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, |
2714 | 0 | nBandSpace, psExtraArg, &bTried); |
2715 | 0 | if (bTried) |
2716 | 0 | return eErr; |
2717 | 0 | } |
2718 | | |
2719 | 0 | int nBandBlockXSize, nBandBlockYSize; |
2720 | 0 | int bReadPixels = FALSE; |
2721 | 0 | GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize); |
2722 | 0 | if (m_aiTiles.empty() && eRWFlag == GF_Read && nXSize == nBufXSize && |
2723 | 0 | nYSize == nBufYSize && |
2724 | 0 | (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) && |
2725 | 0 | eBufType == GDT_UInt8 && nBandCount == nBands && |
2726 | 0 | IsAllBands(nBandCount, panBandMap)) |
2727 | 0 | { |
2728 | 0 | bReadPixels = TRUE; |
2729 | | #ifdef HAVE_PODOFO |
2730 | | if (m_bUseLib.test(PDFLIB_PODOFO) && nBands == 4) |
2731 | | { |
2732 | | bReadPixels = FALSE; |
2733 | | } |
2734 | | #endif |
2735 | 0 | } |
2736 | |
|
2737 | 0 | if (bReadPixels) |
2738 | 0 | return ReadPixels(nXOff, nYOff, nXSize, nYSize, nPixelSpace, nLineSpace, |
2739 | 0 | nBandSpace, static_cast<GByte *>(pData)); |
2740 | | |
2741 | 0 | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2742 | 0 | { |
2743 | 0 | m_bCacheBlocksForOtherBands = true; |
2744 | 0 | } |
2745 | 0 | CPLErr eErr = GDALPamDataset::IRasterIO( |
2746 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2747 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, |
2748 | 0 | psExtraArg); |
2749 | 0 | m_bCacheBlocksForOtherBands = false; |
2750 | 0 | return eErr; |
2751 | 0 | } |
2752 | | |
2753 | | /************************************************************************/ |
2754 | | /* IRasterIO() */ |
2755 | | /************************************************************************/ |
2756 | | |
2757 | | CPLErr PDFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2758 | | int nXSize, int nYSize, void *pData, |
2759 | | int nBufXSize, int nBufYSize, |
2760 | | GDALDataType eBufType, GSpacing nPixelSpace, |
2761 | | GSpacing nLineSpace, |
2762 | | GDALRasterIOExtraArg *psExtraArg) |
2763 | 9.39M | { |
2764 | 9.39M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2765 | | |
2766 | | // Try to pass the request to the most appropriate overview dataset. |
2767 | 9.39M | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2768 | 0 | { |
2769 | 0 | int bTried = FALSE; |
2770 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2771 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2772 | 0 | eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried); |
2773 | 0 | if (bTried) |
2774 | 0 | return eErr; |
2775 | 0 | } |
2776 | | |
2777 | 9.39M | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2778 | 9.39M | { |
2779 | 9.39M | poGDS->m_bCacheBlocksForOtherBands = true; |
2780 | 9.39M | } |
2781 | 9.39M | CPLErr eErr = GDALPamRasterBand::IRasterIO( |
2782 | 9.39M | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2783 | 9.39M | eBufType, nPixelSpace, nLineSpace, psExtraArg); |
2784 | 9.39M | poGDS->m_bCacheBlocksForOtherBands = false; |
2785 | 9.39M | return eErr; |
2786 | 9.39M | } |
2787 | | |
2788 | | /************************************************************************/ |
2789 | | /* PDFDatasetErrorFunction() */ |
2790 | | /************************************************************************/ |
2791 | | |
2792 | | #ifdef HAVE_POPPLER |
2793 | | |
2794 | | static void PDFDatasetErrorFunctionCommon(const CPLString &osError) |
2795 | 14.1M | { |
2796 | 14.1M | if (strcmp(osError.c_str(), "Incorrect password") == 0) |
2797 | 67 | return; |
2798 | | /* Reported on newer USGS GeoPDF */ |
2799 | 14.1M | if (strcmp(osError.c_str(), |
2800 | 14.1M | "Couldn't find group for reference to set OFF") == 0) |
2801 | 503 | { |
2802 | 503 | CPLDebug("PDF", "%s", osError.c_str()); |
2803 | 503 | return; |
2804 | 503 | } |
2805 | | |
2806 | 14.1M | CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str()); |
2807 | 14.1M | } |
2808 | | |
2809 | | static int g_nPopplerErrors = 0; |
2810 | | constexpr int MAX_POPPLER_ERRORS = 1000; |
2811 | | |
2812 | | static void PDFDatasetErrorFunction(ErrorCategory /* eErrCategory */, |
2813 | | Goffset nPos, const char *pszMsg) |
2814 | 14.1M | { |
2815 | 14.1M | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
2816 | 9.19k | { |
2817 | | // If there are too many errors, then unregister ourselves and turn |
2818 | | // quiet error mode, as the error() function in poppler can spend |
2819 | | // significant time formatting an error message we won't emit... |
2820 | 9.19k | setErrorCallback(nullptr); |
2821 | 9.19k | globalParams->setErrQuiet(true); |
2822 | 9.19k | return; |
2823 | 9.19k | } |
2824 | | |
2825 | 14.1M | g_nPopplerErrors++; |
2826 | 14.1M | CPLString osError; |
2827 | | |
2828 | 14.1M | if (nPos >= 0) |
2829 | 13.4M | osError.Printf("Pos = " CPL_FRMT_GUIB ", ", |
2830 | 13.4M | static_cast<GUIntBig>(nPos)); |
2831 | 14.1M | osError += pszMsg; |
2832 | 14.1M | PDFDatasetErrorFunctionCommon(osError); |
2833 | 14.1M | } |
2834 | | #endif |
2835 | | |
2836 | | /************************************************************************/ |
2837 | | /* GDALPDFParseStreamContentOnlyDrawForm() */ |
2838 | | /************************************************************************/ |
2839 | | |
2840 | | static CPLString GDALPDFParseStreamContentOnlyDrawForm(const char *pszContent) |
2841 | 8.05k | { |
2842 | 8.05k | CPLString osToken; |
2843 | 8.05k | char ch; |
2844 | 8.05k | int nCurIdx = 0; |
2845 | 8.05k | CPLString osCurrentForm; |
2846 | | |
2847 | | // CPLDebug("PDF", "content = %s", pszContent); |
2848 | | |
2849 | 7.71M | while ((ch = *pszContent) != '\0') |
2850 | 7.71M | { |
2851 | 7.71M | if (ch == '%') |
2852 | 1.44k | { |
2853 | | /* Skip comments until end-of-line */ |
2854 | 45.8k | while ((ch = *pszContent) != '\0') |
2855 | 45.8k | { |
2856 | 45.8k | if (ch == '\r' || ch == '\n') |
2857 | 1.43k | break; |
2858 | 44.3k | pszContent++; |
2859 | 44.3k | } |
2860 | 1.44k | if (ch == 0) |
2861 | 10 | break; |
2862 | 1.44k | } |
2863 | 7.71M | else if (ch == ' ' || ch == '\r' || ch == '\n') |
2864 | 13.1k | { |
2865 | 13.1k | if (!osToken.empty()) |
2866 | 10.7k | { |
2867 | 10.7k | if (nCurIdx == 0 && osToken[0] == '/') |
2868 | 3.08k | { |
2869 | 3.08k | osCurrentForm = osToken.substr(1); |
2870 | 3.08k | nCurIdx++; |
2871 | 3.08k | } |
2872 | 7.69k | else if (nCurIdx == 1 && osToken == "Do") |
2873 | 79 | { |
2874 | 79 | nCurIdx++; |
2875 | 79 | } |
2876 | 7.61k | else |
2877 | 7.61k | { |
2878 | 7.61k | return ""; |
2879 | 7.61k | } |
2880 | 10.7k | } |
2881 | 5.54k | osToken = ""; |
2882 | 5.54k | } |
2883 | 7.70M | else |
2884 | 7.70M | osToken += ch; |
2885 | 7.70M | pszContent++; |
2886 | 7.70M | } |
2887 | | |
2888 | 441 | return osCurrentForm; |
2889 | 8.05k | } |
2890 | | |
2891 | | /************************************************************************/ |
2892 | | /* GDALPDFParseStreamContent() */ |
2893 | | /************************************************************************/ |
2894 | | |
2895 | | typedef enum |
2896 | | { |
2897 | | STATE_INIT, |
2898 | | STATE_AFTER_q, |
2899 | | STATE_AFTER_cm, |
2900 | | STATE_AFTER_Do |
2901 | | } PDFStreamState; |
2902 | | |
2903 | | /* This parser is reduced to understanding sequences that draw rasters, such as |
2904 | | : |
2905 | | q |
2906 | | scaleX 0 0 scaleY translateX translateY cm |
2907 | | /ImXXX Do |
2908 | | Q |
2909 | | |
2910 | | All other sequences will abort the parsing. |
2911 | | |
2912 | | Returns TRUE if the stream only contains images. |
2913 | | */ |
2914 | | |
2915 | | static int GDALPDFParseStreamContent(const char *pszContent, |
2916 | | GDALPDFDictionary *poXObjectDict, |
2917 | | double *pdfDPI, int *pbDPISet, |
2918 | | int *pnBands, |
2919 | | std::vector<GDALPDFTileDesc> &asTiles, |
2920 | | int bAcceptRotationTerms) |
2921 | 8.00k | { |
2922 | 8.00k | CPLString osToken; |
2923 | 8.00k | char ch; |
2924 | 8.00k | PDFStreamState nState = STATE_INIT; |
2925 | 8.00k | int nCurIdx = 0; |
2926 | 8.00k | double adfVals[6]; |
2927 | 8.00k | CPLString osCurrentImage; |
2928 | | |
2929 | 8.00k | double dfDPI = DEFAULT_DPI; |
2930 | 8.00k | *pbDPISet = FALSE; |
2931 | | |
2932 | 7.76M | while ((ch = *pszContent) != '\0') |
2933 | 7.75M | { |
2934 | 7.75M | if (ch == '%') |
2935 | 1.46k | { |
2936 | | /* Skip comments until end-of-line */ |
2937 | 45.8k | while ((ch = *pszContent) != '\0') |
2938 | 45.8k | { |
2939 | 45.8k | if (ch == '\r' || ch == '\n') |
2940 | 1.45k | break; |
2941 | 44.3k | pszContent++; |
2942 | 44.3k | } |
2943 | 1.46k | if (ch == 0) |
2944 | 12 | break; |
2945 | 1.46k | } |
2946 | 7.75M | else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') |
2947 | 39.7k | { |
2948 | 39.7k | if (!osToken.empty()) |
2949 | 31.4k | { |
2950 | 31.4k | if (nState == STATE_INIT) |
2951 | 8.25k | { |
2952 | 8.25k | if (osToken == "q") |
2953 | 2.87k | { |
2954 | 2.87k | nState = STATE_AFTER_q; |
2955 | 2.87k | nCurIdx = 0; |
2956 | 2.87k | } |
2957 | 5.37k | else if (osToken != "Q") |
2958 | 5.21k | return FALSE; |
2959 | 8.25k | } |
2960 | 23.2k | else if (nState == STATE_AFTER_q) |
2961 | 20.4k | { |
2962 | 20.4k | if (osToken == "q") |
2963 | 1.04k | { |
2964 | | // ignore |
2965 | 1.04k | } |
2966 | 19.4k | else if (nCurIdx < 6) |
2967 | 16.7k | { |
2968 | 16.7k | adfVals[nCurIdx++] = CPLAtof(osToken); |
2969 | 16.7k | } |
2970 | 2.67k | else if (nCurIdx == 6 && osToken == "cm") |
2971 | 1.26k | { |
2972 | 1.26k | nState = STATE_AFTER_cm; |
2973 | 1.26k | nCurIdx = 0; |
2974 | 1.26k | } |
2975 | 1.41k | else |
2976 | 1.41k | return FALSE; |
2977 | 20.4k | } |
2978 | 2.78k | else if (nState == STATE_AFTER_cm) |
2979 | 2.14k | { |
2980 | 2.14k | if (nCurIdx == 0 && osToken[0] == '/') |
2981 | 898 | { |
2982 | 898 | osCurrentImage = osToken.substr(1); |
2983 | 898 | } |
2984 | 1.25k | else if (osToken == "Do") |
2985 | 691 | { |
2986 | 691 | nState = STATE_AFTER_Do; |
2987 | 691 | } |
2988 | 559 | else |
2989 | 559 | return FALSE; |
2990 | 2.14k | } |
2991 | 635 | else if (nState == STATE_AFTER_Do) |
2992 | 635 | { |
2993 | 635 | if (osToken == "Q") |
2994 | 614 | { |
2995 | 614 | GDALPDFObject *poImage = |
2996 | 614 | poXObjectDict->Get(osCurrentImage); |
2997 | 614 | if (poImage != nullptr && |
2998 | 557 | poImage->GetType() == PDFObjectType_Dictionary) |
2999 | 548 | { |
3000 | 548 | GDALPDFTileDesc sTile; |
3001 | 548 | GDALPDFDictionary *poImageDict = |
3002 | 548 | poImage->GetDictionary(); |
3003 | 548 | GDALPDFObject *poWidth = poImageDict->Get("Width"); |
3004 | 548 | GDALPDFObject *poHeight = |
3005 | 548 | poImageDict->Get("Height"); |
3006 | 548 | GDALPDFObject *poColorSpace = |
3007 | 548 | poImageDict->Get("ColorSpace"); |
3008 | 548 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
3009 | 548 | if (poColorSpace && |
3010 | 527 | poColorSpace->GetType() == PDFObjectType_Name) |
3011 | 295 | { |
3012 | 295 | if (poColorSpace->GetName() == "DeviceRGB") |
3013 | 177 | { |
3014 | 177 | sTile.nBands = 3; |
3015 | 177 | if (*pnBands < 3) |
3016 | 161 | *pnBands = 3; |
3017 | 177 | } |
3018 | 118 | else if (poColorSpace->GetName() == |
3019 | 118 | "DeviceGray") |
3020 | 111 | { |
3021 | 111 | sTile.nBands = 1; |
3022 | 111 | if (*pnBands < 1) |
3023 | 91 | *pnBands = 1; |
3024 | 111 | } |
3025 | 7 | else |
3026 | 7 | sTile.nBands = 0; |
3027 | 295 | } |
3028 | 548 | if (poSMask != nullptr) |
3029 | 6 | *pnBands = 4; |
3030 | | |
3031 | 548 | if (poWidth && poHeight && |
3032 | 515 | ((bAcceptRotationTerms && |
3033 | 0 | adfVals[1] == -adfVals[2]) || |
3034 | 515 | (!bAcceptRotationTerms && adfVals[1] == 0.0 && |
3035 | 515 | adfVals[2] == 0.0))) |
3036 | 513 | { |
3037 | 513 | double dfWidth = Get(poWidth); |
3038 | 513 | double dfHeight = Get(poHeight); |
3039 | 513 | double dfScaleX = adfVals[0]; |
3040 | 513 | double dfScaleY = adfVals[3]; |
3041 | 513 | if (dfWidth > 0 && dfHeight > 0 && |
3042 | 511 | dfScaleX > 0 && dfScaleY > 0 && |
3043 | 510 | dfWidth / dfScaleX * DEFAULT_DPI < |
3044 | 510 | INT_MAX && |
3045 | 510 | dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX) |
3046 | 510 | { |
3047 | 510 | double dfDPI_X = ROUND_IF_CLOSE( |
3048 | 510 | dfWidth / dfScaleX * DEFAULT_DPI, 1e-3); |
3049 | 510 | double dfDPI_Y = ROUND_IF_CLOSE( |
3050 | 510 | dfHeight / dfScaleY * DEFAULT_DPI, |
3051 | 510 | 1e-3); |
3052 | | // CPLDebug("PDF", "Image %s, width = %.16g, |
3053 | | // height = %.16g, scaleX = %.16g, scaleY = |
3054 | | // %.16g --> DPI_X = %.16g, DPI_Y = %.16g", |
3055 | | // osCurrentImage.c_str(), |
3056 | | // dfWidth, dfHeight, |
3057 | | // dfScaleX, dfScaleY, |
3058 | | // dfDPI_X, dfDPI_Y); |
3059 | 510 | if (dfDPI_X > dfDPI) |
3060 | 233 | dfDPI = dfDPI_X; |
3061 | 510 | if (dfDPI_Y > dfDPI) |
3062 | 35 | dfDPI = dfDPI_Y; |
3063 | | |
3064 | 510 | memcpy(&(sTile.adfCM), adfVals, |
3065 | 510 | 6 * sizeof(double)); |
3066 | 510 | sTile.poImage = poImage; |
3067 | 510 | sTile.dfWidth = dfWidth; |
3068 | 510 | sTile.dfHeight = dfHeight; |
3069 | 510 | asTiles.push_back(sTile); |
3070 | | |
3071 | 510 | *pbDPISet = TRUE; |
3072 | 510 | *pdfDPI = dfDPI; |
3073 | 510 | } |
3074 | 513 | } |
3075 | 548 | } |
3076 | 614 | nState = STATE_INIT; |
3077 | 614 | } |
3078 | 21 | else |
3079 | 21 | return FALSE; |
3080 | 635 | } |
3081 | 31.4k | } |
3082 | 32.5k | osToken = ""; |
3083 | 32.5k | } |
3084 | 7.71M | else |
3085 | 7.71M | osToken += ch; |
3086 | 7.75M | pszContent++; |
3087 | 7.75M | } |
3088 | | |
3089 | 797 | return TRUE; |
3090 | 8.00k | } |
3091 | | |
3092 | | /************************************************************************/ |
3093 | | /* CheckTiledRaster() */ |
3094 | | /************************************************************************/ |
3095 | | |
3096 | | int PDFDataset::CheckTiledRaster() |
3097 | 205 | { |
3098 | 205 | size_t i; |
3099 | 205 | int l_nBlockXSize = 0; |
3100 | 205 | int l_nBlockYSize = 0; |
3101 | 205 | const double dfUserUnit = m_dfDPI * USER_UNIT_IN_INCH; |
3102 | | |
3103 | | /* First pass : check that all tiles have same DPI, */ |
3104 | | /* are contained entirely in the raster size, */ |
3105 | | /* and determine the block size */ |
3106 | 266 | for (i = 0; i < m_asTiles.size(); i++) |
3107 | 207 | { |
3108 | 207 | double dfDrawWidth = m_asTiles[i].adfCM[0] * dfUserUnit; |
3109 | 207 | double dfDrawHeight = m_asTiles[i].adfCM[3] * dfUserUnit; |
3110 | 207 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3111 | 207 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3112 | 207 | int nX = static_cast<int>(dfX + 0.1); |
3113 | 207 | int nY = static_cast<int>(dfY + 0.1); |
3114 | 207 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3115 | 207 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3116 | | |
3117 | 207 | GDALPDFDictionary *poImageDict = m_asTiles[i].poImage->GetDictionary(); |
3118 | 207 | GDALPDFObject *poBitsPerComponent = |
3119 | 207 | poImageDict->Get("BitsPerComponent"); |
3120 | 207 | GDALPDFObject *poColorSpace = poImageDict->Get("ColorSpace"); |
3121 | 207 | GDALPDFObject *poFilter = poImageDict->Get("Filter"); |
3122 | | |
3123 | | /* Podofo cannot uncompress JPEG2000 streams */ |
3124 | 207 | if (m_bUseLib.test(PDFLIB_PODOFO) && poFilter != nullptr && |
3125 | 0 | poFilter->GetType() == PDFObjectType_Name && |
3126 | 0 | poFilter->GetName() == "JPXDecode") |
3127 | 0 | { |
3128 | 0 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3129 | 0 | static_cast<int>(i)); |
3130 | 0 | return FALSE; |
3131 | 0 | } |
3132 | | |
3133 | 207 | if (poBitsPerComponent == nullptr || Get(poBitsPerComponent) != 8 || |
3134 | 148 | poColorSpace == nullptr || |
3135 | 148 | poColorSpace->GetType() != PDFObjectType_Name || |
3136 | 107 | (poColorSpace->GetName() != "DeviceRGB" && |
3137 | 39 | poColorSpace->GetName() != "DeviceGray")) |
3138 | 103 | { |
3139 | 103 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3140 | 103 | static_cast<int>(i)); |
3141 | 103 | return FALSE; |
3142 | 103 | } |
3143 | | |
3144 | 104 | if (fabs(dfDrawWidth - m_asTiles[i].dfWidth) > 1e-2 || |
3145 | 84 | fabs(dfDrawHeight - m_asTiles[i].dfHeight) > 1e-2 || |
3146 | 80 | fabs(nWidth - m_asTiles[i].dfWidth) > 1e-8 || |
3147 | 80 | fabs(nHeight - m_asTiles[i].dfHeight) > 1e-8 || |
3148 | 80 | fabs(nX - dfX) > 1e-1 || fabs(nY - dfY) > 1e-1 || nX < 0 || |
3149 | 62 | nY < 0 || nX + nWidth > nRasterXSize || nY >= nRasterYSize) |
3150 | 43 | { |
3151 | 43 | CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", static_cast<int>(i), |
3152 | 43 | dfX, dfY, dfDrawWidth, dfDrawHeight, m_asTiles[i].dfWidth, |
3153 | 43 | m_asTiles[i].dfHeight); |
3154 | 43 | return FALSE; |
3155 | 43 | } |
3156 | 61 | if (l_nBlockXSize == 0 && l_nBlockYSize == 0 && nX == 0 && nY != 0) |
3157 | 0 | { |
3158 | 0 | l_nBlockXSize = nWidth; |
3159 | 0 | l_nBlockYSize = nHeight; |
3160 | 0 | } |
3161 | 61 | } |
3162 | 59 | if (l_nBlockXSize <= 0 || l_nBlockYSize <= 0 || l_nBlockXSize > 2048 || |
3163 | 0 | l_nBlockYSize > 2048) |
3164 | 59 | return FALSE; |
3165 | | |
3166 | 0 | int nXBlocks = DIV_ROUND_UP(nRasterXSize, l_nBlockXSize); |
3167 | 0 | int nYBlocks = DIV_ROUND_UP(nRasterYSize, l_nBlockYSize); |
3168 | | |
3169 | | /* Second pass to determine that all tiles are properly aligned on block |
3170 | | * size */ |
3171 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3172 | 0 | { |
3173 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3174 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3175 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3176 | 0 | int nY = static_cast<int>(dfY + 0.1); |
3177 | 0 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3178 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3179 | 0 | int bOK = TRUE; |
3180 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3181 | 0 | if ((nX % l_nBlockXSize) != 0) |
3182 | 0 | bOK = FALSE; |
3183 | 0 | if (nBlockXOff < nXBlocks - 1 && nWidth != l_nBlockXSize) |
3184 | 0 | bOK = FALSE; |
3185 | 0 | if (nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize) |
3186 | 0 | bOK = FALSE; |
3187 | |
|
3188 | 0 | if (nY > 0 && nHeight != l_nBlockYSize) |
3189 | 0 | bOK = FALSE; |
3190 | 0 | if (nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * l_nBlockYSize) |
3191 | 0 | bOK = FALSE; |
3192 | |
|
3193 | 0 | if (!bOK) |
3194 | 0 | { |
3195 | 0 | CPLDebug("PDF", "Tile %d : %d %d %d %d", static_cast<int>(i), nX, |
3196 | 0 | nY, nWidth, nHeight); |
3197 | 0 | return FALSE; |
3198 | 0 | } |
3199 | 0 | } |
3200 | | |
3201 | | /* Third pass to set the aiTiles array */ |
3202 | 0 | m_aiTiles.resize(static_cast<size_t>(nXBlocks) * nYBlocks, -1); |
3203 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3204 | 0 | { |
3205 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3206 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3207 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3208 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3209 | 0 | int nY = nRasterYSize - (static_cast<int>(dfY + 0.1) + nHeight); |
3210 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3211 | 0 | int nBlockYOff = nY / l_nBlockYSize; |
3212 | 0 | m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff] = static_cast<int>(i); |
3213 | 0 | } |
3214 | |
|
3215 | 0 | this->m_nBlockXSize = l_nBlockXSize; |
3216 | 0 | this->m_nBlockYSize = l_nBlockYSize; |
3217 | |
|
3218 | 0 | return TRUE; |
3219 | 0 | } |
3220 | | |
3221 | | /************************************************************************/ |
3222 | | /* GuessDPIAndBandCount() */ |
3223 | | /************************************************************************/ |
3224 | | |
3225 | | void PDFDataset::GuessDPIAndBandCount(GDALPDFDictionary *poPageDict, |
3226 | | double &dfDPI, int &nBandsGuessed) |
3227 | 32.6k | { |
3228 | | /* Try to get a better value from the images that are drawn */ |
3229 | | /* Very simplistic logic. Will only work for raster only PDF */ |
3230 | | |
3231 | 32.6k | GDALPDFObject *poContents = poPageDict->Get("Contents"); |
3232 | 32.6k | if (poContents != nullptr && poContents->GetType() == PDFObjectType_Array) |
3233 | 1.90k | { |
3234 | 1.90k | GDALPDFArray *poContentsArray = poContents->GetArray(); |
3235 | 1.90k | if (poContentsArray->GetLength() == 1) |
3236 | 317 | { |
3237 | 317 | poContents = poContentsArray->Get(0); |
3238 | 317 | } |
3239 | 1.90k | } |
3240 | | |
3241 | 32.6k | GDALPDFObject *poXObject = poPageDict->LookupObject("Resources.XObject"); |
3242 | 32.6k | if (poContents != nullptr && |
3243 | 25.8k | poContents->GetType() == PDFObjectType_Dictionary && |
3244 | 24.1k | poXObject != nullptr && |
3245 | 8.55k | poXObject->GetType() == PDFObjectType_Dictionary) |
3246 | 8.44k | { |
3247 | 8.44k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
3248 | 8.44k | GDALPDFDictionary *poContentDict = poXObjectDict; |
3249 | 8.44k | GDALPDFStream *poPageStream = poContents->GetStream(); |
3250 | 8.44k | if (poPageStream != nullptr) |
3251 | 8.13k | { |
3252 | 8.13k | char *pszContent = nullptr; |
3253 | 8.13k | constexpr int64_t MAX_LENGTH = 10 * 1000 * 1000; |
3254 | 8.13k | const int64_t nLength = poPageStream->GetLength(MAX_LENGTH); |
3255 | 8.13k | int bResetTiles = FALSE; |
3256 | 8.13k | double dfScaleDPI = 1.0; |
3257 | | |
3258 | 8.13k | if (nLength < MAX_LENGTH) |
3259 | 8.13k | { |
3260 | 8.13k | CPLString osForm; |
3261 | 8.13k | pszContent = poPageStream->GetBytes(); |
3262 | 8.13k | if (pszContent != nullptr) |
3263 | 8.05k | { |
3264 | | #ifdef DEBUG |
3265 | | const char *pszDumpStream = |
3266 | | CPLGetConfigOption("PDF_DUMP_STREAM", nullptr); |
3267 | | if (pszDumpStream != nullptr) |
3268 | | { |
3269 | | VSILFILE *fpDump = VSIFOpenL(pszDumpStream, "wb"); |
3270 | | if (fpDump) |
3271 | | { |
3272 | | VSIFWriteL(pszContent, 1, static_cast<int>(nLength), |
3273 | | fpDump); |
3274 | | VSIFCloseL(fpDump); |
3275 | | } |
3276 | | } |
3277 | | #endif // DEBUG |
3278 | 8.05k | osForm = GDALPDFParseStreamContentOnlyDrawForm(pszContent); |
3279 | 8.05k | if (osForm.empty()) |
3280 | 7.91k | { |
3281 | | /* Special case for USGS Topo PDF, like |
3282 | | * CA_Hollywood_20090811_OM_geo.pdf */ |
3283 | 7.91k | const char *pszOGCDo = strstr(pszContent, " /XO1 Do"); |
3284 | 7.91k | if (pszOGCDo) |
3285 | 13 | { |
3286 | 13 | const char *pszcm = strstr(pszContent, " cm "); |
3287 | 13 | if (pszcm != nullptr && pszcm < pszOGCDo) |
3288 | 0 | { |
3289 | 0 | const char *pszNextcm = strstr(pszcm + 2, "cm"); |
3290 | 0 | if (pszNextcm == nullptr || |
3291 | 0 | pszNextcm > pszOGCDo) |
3292 | 0 | { |
3293 | 0 | const char *pszIter = pszcm; |
3294 | 0 | while (pszIter > pszContent) |
3295 | 0 | { |
3296 | 0 | if ((*pszIter >= '0' && |
3297 | 0 | *pszIter <= '9') || |
3298 | 0 | *pszIter == '-' || |
3299 | 0 | *pszIter == '.' || *pszIter == ' ') |
3300 | 0 | pszIter--; |
3301 | 0 | else |
3302 | 0 | { |
3303 | 0 | pszIter++; |
3304 | 0 | break; |
3305 | 0 | } |
3306 | 0 | } |
3307 | 0 | CPLString oscm(pszIter); |
3308 | 0 | oscm.resize(pszcm - pszIter); |
3309 | 0 | char **papszTokens = |
3310 | 0 | CSLTokenizeString(oscm); |
3311 | 0 | double dfScaleX = -1.0; |
3312 | 0 | double dfScaleY = -2.0; |
3313 | 0 | if (CSLCount(papszTokens) == 6) |
3314 | 0 | { |
3315 | 0 | dfScaleX = CPLAtof(papszTokens[0]); |
3316 | 0 | dfScaleY = CPLAtof(papszTokens[3]); |
3317 | 0 | } |
3318 | 0 | CSLDestroy(papszTokens); |
3319 | 0 | if (dfScaleX == dfScaleY && dfScaleX > 0.0) |
3320 | 0 | { |
3321 | 0 | osForm = "XO1"; |
3322 | 0 | bResetTiles = TRUE; |
3323 | 0 | dfScaleDPI = 1.0 / dfScaleX; |
3324 | 0 | } |
3325 | 0 | } |
3326 | 0 | } |
3327 | 13 | else |
3328 | 13 | { |
3329 | 13 | osForm = "XO1"; |
3330 | 13 | bResetTiles = TRUE; |
3331 | 13 | } |
3332 | 13 | } |
3333 | | /* Special case for USGS Topo PDF, like |
3334 | | * CA_Sacramento_East_20120308_TM_geo.pdf */ |
3335 | 7.90k | else |
3336 | 7.90k | { |
3337 | 7.90k | CPLString osOCG = |
3338 | 7.90k | FindLayerOCG(poPageDict, "Orthoimage"); |
3339 | 7.90k | if (!osOCG.empty()) |
3340 | 0 | { |
3341 | 0 | const char *pszBDCLookup = |
3342 | 0 | CPLSPrintf("/OC /%s BDC", osOCG.c_str()); |
3343 | 0 | const char *pszBDC = |
3344 | 0 | strstr(pszContent, pszBDCLookup); |
3345 | 0 | if (pszBDC != nullptr) |
3346 | 0 | { |
3347 | 0 | const char *pszIter = |
3348 | 0 | pszBDC + strlen(pszBDCLookup); |
3349 | 0 | while (*pszIter != '\0') |
3350 | 0 | { |
3351 | 0 | if (*pszIter == 13 || *pszIter == 10 || |
3352 | 0 | *pszIter == ' ' || *pszIter == 'q') |
3353 | 0 | pszIter++; |
3354 | 0 | else |
3355 | 0 | break; |
3356 | 0 | } |
3357 | 0 | if (STARTS_WITH(pszIter, |
3358 | 0 | "1 0 0 1 0 0 cm\n")) |
3359 | 0 | pszIter += strlen("1 0 0 1 0 0 cm\n"); |
3360 | 0 | if (*pszIter == '/') |
3361 | 0 | { |
3362 | 0 | pszIter++; |
3363 | 0 | const char *pszDo = |
3364 | 0 | strstr(pszIter, " Do"); |
3365 | 0 | if (pszDo != nullptr) |
3366 | 0 | { |
3367 | 0 | osForm = pszIter; |
3368 | 0 | osForm.resize(pszDo - pszIter); |
3369 | 0 | bResetTiles = TRUE; |
3370 | 0 | } |
3371 | 0 | } |
3372 | 0 | } |
3373 | 0 | } |
3374 | 7.90k | } |
3375 | 7.91k | } |
3376 | 8.05k | } |
3377 | | |
3378 | 8.13k | if (!osForm.empty()) |
3379 | 152 | { |
3380 | 152 | CPLFree(pszContent); |
3381 | 152 | pszContent = nullptr; |
3382 | | |
3383 | 152 | GDALPDFObject *poObjForm = poXObjectDict->Get(osForm); |
3384 | 152 | if (poObjForm != nullptr && |
3385 | 104 | poObjForm->GetType() == PDFObjectType_Dictionary && |
3386 | 104 | (poPageStream = poObjForm->GetStream()) != nullptr) |
3387 | 104 | { |
3388 | 104 | GDALPDFDictionary *poObjFormDict = |
3389 | 104 | poObjForm->GetDictionary(); |
3390 | 104 | GDALPDFObject *poSubtype = |
3391 | 104 | poObjFormDict->Get("Subtype"); |
3392 | 104 | if (poSubtype != nullptr && |
3393 | 104 | poSubtype->GetType() == PDFObjectType_Name && |
3394 | 104 | poSubtype->GetName() == "Form") |
3395 | 103 | { |
3396 | 103 | if (poPageStream->GetLength(MAX_LENGTH) < |
3397 | 103 | MAX_LENGTH) |
3398 | 103 | { |
3399 | 103 | pszContent = poPageStream->GetBytes(); |
3400 | | |
3401 | 103 | GDALPDFObject *poXObject2 = |
3402 | 103 | poObjFormDict->LookupObject( |
3403 | 103 | "Resources.XObject"); |
3404 | 103 | if (poXObject2 != nullptr && |
3405 | 3 | poXObject2->GetType() == |
3406 | 3 | PDFObjectType_Dictionary) |
3407 | 3 | poContentDict = poXObject2->GetDictionary(); |
3408 | 103 | } |
3409 | 103 | } |
3410 | 104 | } |
3411 | 152 | } |
3412 | 8.13k | } |
3413 | | |
3414 | 8.13k | if (pszContent != nullptr) |
3415 | 8.00k | { |
3416 | 8.00k | int bDPISet = FALSE; |
3417 | | |
3418 | 8.00k | const char *pszContentToParse = pszContent; |
3419 | 8.00k | if (bResetTiles) |
3420 | 0 | { |
3421 | 0 | while (*pszContentToParse != '\0') |
3422 | 0 | { |
3423 | 0 | if (*pszContentToParse == 13 || |
3424 | 0 | *pszContentToParse == 10 || |
3425 | 0 | *pszContentToParse == ' ' || |
3426 | 0 | (*pszContentToParse >= '0' && |
3427 | 0 | *pszContentToParse <= '9') || |
3428 | 0 | *pszContentToParse == '.' || |
3429 | 0 | *pszContentToParse == '-' || |
3430 | 0 | *pszContentToParse == 'l' || |
3431 | 0 | *pszContentToParse == 'm' || |
3432 | 0 | *pszContentToParse == 'n' || |
3433 | 0 | *pszContentToParse == 'W') |
3434 | 0 | pszContentToParse++; |
3435 | 0 | else |
3436 | 0 | break; |
3437 | 0 | } |
3438 | 0 | } |
3439 | | |
3440 | 8.00k | GDALPDFParseStreamContent(pszContentToParse, poContentDict, |
3441 | 8.00k | &dfDPI, &bDPISet, &nBandsGuessed, |
3442 | 8.00k | m_asTiles, bResetTiles); |
3443 | 8.00k | CPLFree(pszContent); |
3444 | 8.00k | if (bDPISet) |
3445 | 470 | { |
3446 | 470 | dfDPI *= dfScaleDPI; |
3447 | | |
3448 | 470 | CPLDebug("PDF", "DPI guessed from contents stream = %.16g", |
3449 | 470 | dfDPI); |
3450 | 470 | SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI)); |
3451 | 470 | if (bResetTiles) |
3452 | 0 | m_asTiles.resize(0); |
3453 | 470 | } |
3454 | 7.53k | else |
3455 | 7.53k | m_asTiles.resize(0); |
3456 | 8.00k | } |
3457 | 8.13k | } |
3458 | 8.44k | } |
3459 | | |
3460 | 32.6k | GDALPDFObject *poUserUnit = nullptr; |
3461 | 32.6k | if ((poUserUnit = poPageDict->Get("UserUnit")) != nullptr && |
3462 | 3.93k | (poUserUnit->GetType() == PDFObjectType_Int || |
3463 | 41 | poUserUnit->GetType() == PDFObjectType_Real)) |
3464 | 3.90k | { |
3465 | 3.90k | dfDPI = ROUND_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI, 1e-5); |
3466 | 3.90k | CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", dfDPI); |
3467 | 3.90k | } |
3468 | 32.6k | } |
3469 | | |
3470 | | /************************************************************************/ |
3471 | | /* FindXMP() */ |
3472 | | /************************************************************************/ |
3473 | | |
3474 | | void PDFDataset::FindXMP(GDALPDFObject *poObj) |
3475 | 0 | { |
3476 | 0 | if (poObj->GetType() != PDFObjectType_Dictionary) |
3477 | 0 | return; |
3478 | | |
3479 | 0 | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3480 | 0 | GDALPDFObject *poType = poDict->Get("Type"); |
3481 | 0 | GDALPDFObject *poSubtype = poDict->Get("Subtype"); |
3482 | 0 | if (poType == nullptr || poType->GetType() != PDFObjectType_Name || |
3483 | 0 | poType->GetName() != "Metadata" || poSubtype == nullptr || |
3484 | 0 | poSubtype->GetType() != PDFObjectType_Name || |
3485 | 0 | poSubtype->GetName() != "XML") |
3486 | 0 | { |
3487 | 0 | return; |
3488 | 0 | } |
3489 | | |
3490 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
3491 | 0 | if (poStream == nullptr) |
3492 | 0 | return; |
3493 | | |
3494 | 0 | char *pszContent = poStream->GetBytes(); |
3495 | 0 | const auto nLength = poStream->GetLength(); |
3496 | 0 | if (pszContent != nullptr && nLength > 15 && |
3497 | 0 | STARTS_WITH(pszContent, "<?xpacket begin=")) |
3498 | 0 | { |
3499 | 0 | char *apszMDList[2]; |
3500 | 0 | apszMDList[0] = pszContent; |
3501 | 0 | apszMDList[1] = nullptr; |
3502 | 0 | SetMetadata(apszMDList, "xml:XMP"); |
3503 | 0 | } |
3504 | 0 | CPLFree(pszContent); |
3505 | 0 | } |
3506 | | |
3507 | | /************************************************************************/ |
3508 | | /* ParseInfo() */ |
3509 | | /************************************************************************/ |
3510 | | |
3511 | | void PDFDataset::ParseInfo(GDALPDFObject *poInfoObj) |
3512 | 32.6k | { |
3513 | 32.6k | if (poInfoObj->GetType() != PDFObjectType_Dictionary) |
3514 | 23.9k | return; |
3515 | | |
3516 | 8.69k | GDALPDFDictionary *poInfoObjDict = poInfoObj->GetDictionary(); |
3517 | 8.69k | GDALPDFObject *poItem = nullptr; |
3518 | 8.69k | int bOneMDISet = FALSE; |
3519 | 8.69k | if ((poItem = poInfoObjDict->Get("Author")) != nullptr && |
3520 | 2.36k | poItem->GetType() == PDFObjectType_String) |
3521 | 2.36k | { |
3522 | 2.36k | SetMetadataItem("AUTHOR", poItem->GetString().c_str()); |
3523 | 2.36k | bOneMDISet = TRUE; |
3524 | 2.36k | } |
3525 | 8.69k | if ((poItem = poInfoObjDict->Get("Creator")) != nullptr && |
3526 | 5.95k | poItem->GetType() == PDFObjectType_String) |
3527 | 5.94k | { |
3528 | 5.94k | SetMetadataItem("CREATOR", poItem->GetString().c_str()); |
3529 | 5.94k | bOneMDISet = TRUE; |
3530 | 5.94k | } |
3531 | 8.69k | if ((poItem = poInfoObjDict->Get("Keywords")) != nullptr && |
3532 | 1.01k | poItem->GetType() == PDFObjectType_String) |
3533 | 1.01k | { |
3534 | 1.01k | SetMetadataItem("KEYWORDS", poItem->GetString().c_str()); |
3535 | 1.01k | bOneMDISet = TRUE; |
3536 | 1.01k | } |
3537 | 8.69k | if ((poItem = poInfoObjDict->Get("Subject")) != nullptr && |
3538 | 1.50k | poItem->GetType() == PDFObjectType_String) |
3539 | 1.50k | { |
3540 | 1.50k | SetMetadataItem("SUBJECT", poItem->GetString().c_str()); |
3541 | 1.50k | bOneMDISet = TRUE; |
3542 | 1.50k | } |
3543 | 8.69k | if ((poItem = poInfoObjDict->Get("Title")) != nullptr && |
3544 | 2.97k | poItem->GetType() == PDFObjectType_String) |
3545 | 2.92k | { |
3546 | 2.92k | SetMetadataItem("TITLE", poItem->GetString().c_str()); |
3547 | 2.92k | bOneMDISet = TRUE; |
3548 | 2.92k | } |
3549 | 8.69k | if ((poItem = poInfoObjDict->Get("Producer")) != nullptr && |
3550 | 7.26k | poItem->GetType() == PDFObjectType_String) |
3551 | 6.65k | { |
3552 | 6.65k | if (bOneMDISet || |
3553 | 1.10k | poItem->GetString() != "PoDoFo - http://podofo.sf.net") |
3554 | 6.65k | { |
3555 | 6.65k | SetMetadataItem("PRODUCER", poItem->GetString().c_str()); |
3556 | 6.65k | bOneMDISet = TRUE; |
3557 | 6.65k | } |
3558 | 6.65k | } |
3559 | 8.69k | if ((poItem = poInfoObjDict->Get("CreationDate")) != nullptr && |
3560 | 7.17k | poItem->GetType() == PDFObjectType_String) |
3561 | 7.14k | { |
3562 | 7.14k | if (bOneMDISet) |
3563 | 6.41k | SetMetadataItem("CREATION_DATE", poItem->GetString().c_str()); |
3564 | 7.14k | } |
3565 | 8.69k | } |
3566 | | |
3567 | | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3568 | | |
3569 | | /************************************************************************/ |
3570 | | /* AddLayer() */ |
3571 | | /************************************************************************/ |
3572 | | |
3573 | | void PDFDataset::AddLayer(const std::string &osName, int iPage) |
3574 | 285k | { |
3575 | 285k | LayerStruct layerStruct; |
3576 | 285k | layerStruct.osName = osName; |
3577 | 285k | layerStruct.nInsertIdx = static_cast<int>(m_oLayerNameSet.size()); |
3578 | 285k | layerStruct.iPage = iPage; |
3579 | 285k | m_oLayerNameSet.emplace_back(std::move(layerStruct)); |
3580 | 285k | } |
3581 | | |
3582 | | /************************************************************************/ |
3583 | | /* SortLayerList() */ |
3584 | | /************************************************************************/ |
3585 | | |
3586 | | void PDFDataset::SortLayerList() |
3587 | 6.98k | { |
3588 | 6.98k | if (!m_oLayerNameSet.empty()) |
3589 | 4.18k | { |
3590 | | // Sort layers by prioritizing page number and then insertion index |
3591 | 4.18k | std::sort(m_oLayerNameSet.begin(), m_oLayerNameSet.end(), |
3592 | 4.18k | [](const LayerStruct &a, const LayerStruct &b) |
3593 | 552k | { |
3594 | 552k | if (a.iPage < b.iPage) |
3595 | 1.27k | return true; |
3596 | 550k | if (a.iPage > b.iPage) |
3597 | 11 | return false; |
3598 | 550k | return a.nInsertIdx < b.nInsertIdx; |
3599 | 550k | }); |
3600 | 4.18k | } |
3601 | 6.98k | } |
3602 | | |
3603 | | /************************************************************************/ |
3604 | | /* CreateLayerList() */ |
3605 | | /************************************************************************/ |
3606 | | |
3607 | | void PDFDataset::CreateLayerList() |
3608 | 6.98k | { |
3609 | 6.98k | SortLayerList(); |
3610 | | |
3611 | 6.98k | if (m_oLayerNameSet.size() >= 100) |
3612 | 108 | { |
3613 | 108 | for (const auto &oLayerStruct : m_oLayerNameSet) |
3614 | 256k | { |
3615 | 256k | m_aosLayerNames.AddNameValue( |
3616 | 256k | CPLSPrintf("LAYER_%03d_NAME", m_aosLayerNames.size()), |
3617 | 256k | oLayerStruct.osName.c_str()); |
3618 | 256k | } |
3619 | 108 | } |
3620 | 6.87k | else |
3621 | 6.87k | { |
3622 | 6.87k | for (const auto &oLayerStruct : m_oLayerNameSet) |
3623 | 29.1k | { |
3624 | 29.1k | m_aosLayerNames.AddNameValue( |
3625 | 29.1k | CPLSPrintf("LAYER_%02d_NAME", m_aosLayerNames.size()), |
3626 | 29.1k | oLayerStruct.osName.c_str()); |
3627 | 29.1k | } |
3628 | 6.87k | } |
3629 | 6.98k | } |
3630 | | |
3631 | | /************************************************************************/ |
3632 | | /* BuildPostfixedLayerNameAndAddLayer() */ |
3633 | | /************************************************************************/ |
3634 | | |
3635 | | /** Append a suffix with the page number(s) to the provided layer name, if |
3636 | | * it makes sense (that is if it is a multiple page PDF and we haven't selected |
3637 | | * a specific name). And also call AddLayer() on it if successful. |
3638 | | * If may return an empty string if the layer isn't used by the page of interest |
3639 | | */ |
3640 | | std::string PDFDataset::BuildPostfixedLayerNameAndAddLayer( |
3641 | | const std::string &osName, const std::pair<int, int> &oOCGRef, |
3642 | | int iPageOfInterest, int nPageCount) |
3643 | 282k | { |
3644 | 282k | std::string osPostfixedName = osName; |
3645 | 282k | int iLayerPage = 0; |
3646 | 282k | if (nPageCount > 1 && !m_oMapOCGNumGenToPages.empty()) |
3647 | 357 | { |
3648 | 357 | const auto oIterToPages = m_oMapOCGNumGenToPages.find(oOCGRef); |
3649 | 357 | if (oIterToPages != m_oMapOCGNumGenToPages.end()) |
3650 | 294 | { |
3651 | 294 | const auto &anPages = oIterToPages->second; |
3652 | 294 | if (iPageOfInterest > 0) |
3653 | 0 | { |
3654 | 0 | if (std::find(anPages.begin(), anPages.end(), |
3655 | 0 | iPageOfInterest) == anPages.end()) |
3656 | 0 | { |
3657 | 0 | return std::string(); |
3658 | 0 | } |
3659 | 0 | } |
3660 | 294 | else if (anPages.size() == 1) |
3661 | 291 | { |
3662 | 291 | iLayerPage = anPages.front(); |
3663 | 291 | osPostfixedName += CPLSPrintf(" (page %d)", anPages.front()); |
3664 | 291 | } |
3665 | 3 | else |
3666 | 3 | { |
3667 | 3 | osPostfixedName += " (pages "; |
3668 | 12 | for (size_t j = 0; j < anPages.size(); ++j) |
3669 | 9 | { |
3670 | 9 | if (j > 0) |
3671 | 6 | osPostfixedName += ", "; |
3672 | 9 | osPostfixedName += CPLSPrintf("%d", anPages[j]); |
3673 | 9 | } |
3674 | 3 | osPostfixedName += ')'; |
3675 | 3 | } |
3676 | 294 | } |
3677 | 357 | } |
3678 | | |
3679 | 282k | AddLayer(osPostfixedName, iLayerPage); |
3680 | | |
3681 | 282k | return osPostfixedName; |
3682 | 282k | } |
3683 | | |
3684 | | #endif // defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3685 | | |
3686 | | #ifdef HAVE_POPPLER |
3687 | | |
3688 | | /************************************************************************/ |
3689 | | /* ExploreLayersPoppler() */ |
3690 | | /************************************************************************/ |
3691 | | |
3692 | | void PDFDataset::ExploreLayersPoppler(GDALPDFArray *poArray, |
3693 | | int iPageOfInterest, int nPageCount, |
3694 | | CPLString osTopLayer, int nRecLevel, |
3695 | | int &nVisited, bool &bStop) |
3696 | 31.7k | { |
3697 | 31.7k | if (nRecLevel == 16 || nVisited == 1000) |
3698 | 107 | { |
3699 | 107 | CPLError( |
3700 | 107 | CE_Failure, CPLE_AppDefined, |
3701 | 107 | "ExploreLayersPoppler(): too deep exploration or too many items"); |
3702 | 107 | bStop = true; |
3703 | 107 | return; |
3704 | 107 | } |
3705 | 31.6k | if (bStop) |
3706 | 0 | return; |
3707 | | |
3708 | 31.6k | int nLength = poArray->GetLength(); |
3709 | 31.6k | CPLString osCurLayer; |
3710 | 1.27M | for (int i = 0; i < nLength; i++) |
3711 | 1.24M | { |
3712 | 1.24M | nVisited++; |
3713 | 1.24M | GDALPDFObject *poObj = poArray->Get(i); |
3714 | 1.24M | if (poObj == nullptr) |
3715 | 48.7k | continue; |
3716 | 1.19M | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
3717 | 1.37k | { |
3718 | 1.37k | std::string osName = |
3719 | 1.37k | PDFSanitizeLayerName(poObj->GetString().c_str()); |
3720 | 1.37k | if (!osTopLayer.empty()) |
3721 | 855 | { |
3722 | 855 | osTopLayer += '.'; |
3723 | 855 | osTopLayer += osName; |
3724 | 855 | } |
3725 | 523 | else |
3726 | 523 | osTopLayer = std::move(osName); |
3727 | 1.37k | AddLayer(osTopLayer, 0); |
3728 | 1.37k | m_oLayerOCGListPoppler.push_back(std::pair(osTopLayer, nullptr)); |
3729 | 1.37k | } |
3730 | 1.19M | else if (poObj->GetType() == PDFObjectType_Array) |
3731 | 25.7k | { |
3732 | 25.7k | ExploreLayersPoppler(poObj->GetArray(), iPageOfInterest, nPageCount, |
3733 | 25.7k | osCurLayer, nRecLevel + 1, nVisited, bStop); |
3734 | 25.7k | if (bStop) |
3735 | 1.60k | return; |
3736 | 24.1k | osCurLayer = ""; |
3737 | 24.1k | } |
3738 | 1.17M | else if (poObj->GetType() == PDFObjectType_Dictionary) |
3739 | 458k | { |
3740 | 458k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3741 | 458k | GDALPDFObject *poName = poDict->Get("Name"); |
3742 | 458k | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
3743 | 398k | { |
3744 | 398k | std::string osName = |
3745 | 398k | PDFSanitizeLayerName(poName->GetString().c_str()); |
3746 | | /* coverity[copy_paste_error] */ |
3747 | 398k | if (!osTopLayer.empty()) |
3748 | 336k | { |
3749 | 336k | osCurLayer = osTopLayer; |
3750 | 336k | osCurLayer += '.'; |
3751 | 336k | osCurLayer += osName; |
3752 | 336k | } |
3753 | 61.3k | else |
3754 | 61.3k | osCurLayer = std::move(osName); |
3755 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
3756 | | |
3757 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3758 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3759 | | const |
3760 | | #endif |
3761 | 398k | OCGs *optContentConfig = |
3762 | 398k | m_poDocPoppler->getOptContentConfig(); |
3763 | 398k | struct Ref r; |
3764 | 398k | r.num = poObj->GetRefNum().toInt(); |
3765 | 398k | r.gen = poObj->GetRefGen(); |
3766 | 398k | OptionalContentGroup *ocg = optContentConfig->findOcgByRef(r); |
3767 | 398k | if (ocg) |
3768 | 282k | { |
3769 | 282k | const auto oRefPair = std::pair(poObj->GetRefNum().toInt(), |
3770 | 282k | poObj->GetRefGen()); |
3771 | 282k | const std::string osPostfixedName = |
3772 | 282k | BuildPostfixedLayerNameAndAddLayer( |
3773 | 282k | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
3774 | 282k | if (osPostfixedName.empty()) |
3775 | 0 | continue; |
3776 | | |
3777 | 282k | m_oLayerOCGListPoppler.push_back( |
3778 | 282k | std::make_pair(osPostfixedName, ocg)); |
3779 | 282k | m_aoLayerWithRef.emplace_back(osPostfixedName.c_str(), |
3780 | 282k | poObj->GetRefNum(), r.gen); |
3781 | 282k | } |
3782 | 398k | } |
3783 | 458k | } |
3784 | 1.19M | } |
3785 | 31.6k | } |
3786 | | |
3787 | | /************************************************************************/ |
3788 | | /* FindLayersPoppler() */ |
3789 | | /************************************************************************/ |
3790 | | |
3791 | | void PDFDataset::FindLayersPoppler(int iPageOfInterest) |
3792 | 32.6k | { |
3793 | 32.6k | int nPageCount = 0; |
3794 | 32.6k | const auto poPages = GetPagesKids(); |
3795 | 32.6k | if (poPages) |
3796 | 31.5k | nPageCount = poPages->GetLength(); |
3797 | | |
3798 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3799 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3800 | | const |
3801 | | #endif |
3802 | 32.6k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3803 | 32.6k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3804 | 25.6k | return; |
3805 | | |
3806 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3807 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3808 | | const |
3809 | | #endif |
3810 | 6.98k | Array *array = optContentConfig->getOrderArray(); |
3811 | 6.98k | if (array) |
3812 | 6.04k | { |
3813 | 6.04k | GDALPDFArray *poArray = GDALPDFCreateArray(array); |
3814 | 6.04k | int nVisited = 0; |
3815 | 6.04k | bool bStop = false; |
3816 | 6.04k | ExploreLayersPoppler(poArray, iPageOfInterest, nPageCount, CPLString(), |
3817 | 6.04k | 0, nVisited, bStop); |
3818 | 6.04k | delete poArray; |
3819 | 6.04k | } |
3820 | 944 | else |
3821 | 944 | { |
3822 | 944 | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3823 | 1.45k | { |
3824 | 1.45k | auto ocg = refOCGPair.second.get(); |
3825 | 1.45k | if (ocg != nullptr && ocg->getName() != nullptr) |
3826 | 1.35k | { |
3827 | 1.35k | const char *pszLayerName = |
3828 | 1.35k | reinterpret_cast<const char *>(ocg->getName()->c_str()); |
3829 | 1.35k | AddLayer(pszLayerName, 0); |
3830 | 1.35k | m_oLayerOCGListPoppler.push_back( |
3831 | 1.35k | std::make_pair(CPLString(pszLayerName), ocg)); |
3832 | 1.35k | } |
3833 | 1.45k | } |
3834 | 944 | } |
3835 | | |
3836 | 6.98k | CreateLayerList(); |
3837 | 6.98k | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
3838 | 6.98k | } |
3839 | | |
3840 | | /************************************************************************/ |
3841 | | /* TurnLayersOnOffPoppler() */ |
3842 | | /************************************************************************/ |
3843 | | |
3844 | | void PDFDataset::TurnLayersOnOffPoppler() |
3845 | 32.6k | { |
3846 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3847 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3848 | | const |
3849 | | #endif |
3850 | 32.6k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3851 | 32.6k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3852 | 25.6k | return; |
3853 | | |
3854 | | // Which layers to turn ON ? |
3855 | 6.98k | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
3856 | 6.98k | if (pszLayers) |
3857 | 0 | { |
3858 | 0 | int i; |
3859 | 0 | int bAll = EQUAL(pszLayers, "ALL"); |
3860 | 0 | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3861 | 0 | { |
3862 | 0 | auto ocg = refOCGPair.second.get(); |
3863 | 0 | ocg->setState((bAll) ? OptionalContentGroup::On |
3864 | 0 | : OptionalContentGroup::Off); |
3865 | 0 | } |
3866 | |
|
3867 | 0 | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
3868 | 0 | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
3869 | 0 | { |
3870 | 0 | bool isFound = false; |
3871 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
3872 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
3873 | 0 | { |
3874 | 0 | if (oIter2->first != papszLayers[i]) |
3875 | 0 | continue; |
3876 | | |
3877 | 0 | isFound = true; |
3878 | 0 | auto oIter = oIter2; |
3879 | 0 | if (oIter->second) |
3880 | 0 | { |
3881 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
3882 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3883 | 0 | } |
3884 | | |
3885 | | // Turn child layers on, unless there's one of them explicitly |
3886 | | // listed in the list. |
3887 | 0 | size_t nLen = strlen(papszLayers[i]); |
3888 | 0 | int bFoundChildLayer = FALSE; |
3889 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3890 | 0 | for (; |
3891 | 0 | oIter != m_oLayerOCGListPoppler.end() && !bFoundChildLayer; |
3892 | 0 | ++oIter) |
3893 | 0 | { |
3894 | 0 | if (oIter->first.size() > nLen && |
3895 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
3896 | 0 | 0 && |
3897 | 0 | oIter->first[nLen] == '.') |
3898 | 0 | { |
3899 | 0 | for (int j = 0; papszLayers[j] != nullptr; j++) |
3900 | 0 | { |
3901 | 0 | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
3902 | 0 | 0) |
3903 | 0 | { |
3904 | 0 | bFoundChildLayer = TRUE; |
3905 | 0 | break; |
3906 | 0 | } |
3907 | 0 | } |
3908 | 0 | } |
3909 | 0 | } |
3910 | |
|
3911 | 0 | if (!bFoundChildLayer) |
3912 | 0 | { |
3913 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3914 | 0 | for (; oIter != m_oLayerOCGListPoppler.end() && |
3915 | 0 | !bFoundChildLayer; |
3916 | 0 | ++oIter) |
3917 | 0 | { |
3918 | 0 | if (oIter->first.size() > nLen && |
3919 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], |
3920 | 0 | nLen) == 0 && |
3921 | 0 | oIter->first[nLen] == '.') |
3922 | 0 | { |
3923 | 0 | if (oIter->second) |
3924 | 0 | { |
3925 | | // CPLDebug("PDF", "Turn '%s' on too", |
3926 | | // oIter->first.c_str()); |
3927 | 0 | oIter->second->setState( |
3928 | 0 | OptionalContentGroup::On); |
3929 | 0 | } |
3930 | 0 | } |
3931 | 0 | } |
3932 | 0 | } |
3933 | | |
3934 | | // Turn parent layers on too |
3935 | 0 | std::string layer(papszLayers[i]); |
3936 | 0 | std::string::size_type j; |
3937 | 0 | while ((j = layer.find_last_of('.')) != std::string::npos) |
3938 | 0 | { |
3939 | 0 | layer.resize(j); |
3940 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3941 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
3942 | 0 | { |
3943 | 0 | if (oIter->first == layer && oIter->second) |
3944 | 0 | { |
3945 | | // CPLDebug("PDF", "Turn '%s' on too", |
3946 | | // layer.c_str()); |
3947 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3948 | 0 | } |
3949 | 0 | } |
3950 | 0 | } |
3951 | 0 | } |
3952 | 0 | if (!isFound) |
3953 | 0 | { |
3954 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
3955 | 0 | papszLayers[i]); |
3956 | 0 | } |
3957 | 0 | } |
3958 | 0 | CSLDestroy(papszLayers); |
3959 | |
|
3960 | 0 | m_bUseOCG = true; |
3961 | 0 | } |
3962 | | |
3963 | | // Which layers to turn OFF ? |
3964 | 6.98k | const char *pszLayersOFF = |
3965 | 6.98k | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
3966 | 6.98k | if (pszLayersOFF) |
3967 | 0 | { |
3968 | 0 | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
3969 | 0 | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
3970 | 0 | { |
3971 | 0 | bool isFound = false; |
3972 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
3973 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
3974 | 0 | { |
3975 | 0 | if (oIter2->first != papszLayersOFF[i]) |
3976 | 0 | continue; |
3977 | | |
3978 | 0 | isFound = true; |
3979 | 0 | auto oIter = oIter2; |
3980 | 0 | if (oIter->second) |
3981 | 0 | { |
3982 | | // CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]); |
3983 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
3984 | 0 | } |
3985 | | |
3986 | | // Turn child layers off too |
3987 | 0 | size_t nLen = strlen(papszLayersOFF[i]); |
3988 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3989 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
3990 | 0 | { |
3991 | 0 | if (oIter->first.size() > nLen && |
3992 | 0 | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
3993 | 0 | nLen) == 0 && |
3994 | 0 | oIter->first[nLen] == '.') |
3995 | 0 | { |
3996 | 0 | if (oIter->second) |
3997 | 0 | { |
3998 | | // CPLDebug("PDF", "Turn '%s' off too", |
3999 | | // oIter->first.c_str()); |
4000 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
4001 | 0 | } |
4002 | 0 | } |
4003 | 0 | } |
4004 | 0 | } |
4005 | 0 | if (!isFound) |
4006 | 0 | { |
4007 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4008 | 0 | papszLayersOFF[i]); |
4009 | 0 | } |
4010 | 0 | } |
4011 | 0 | CSLDestroy(papszLayersOFF); |
4012 | |
|
4013 | 0 | m_bUseOCG = true; |
4014 | 0 | } |
4015 | 6.98k | } |
4016 | | |
4017 | | #endif |
4018 | | |
4019 | | #ifdef HAVE_PDFIUM |
4020 | | |
4021 | | /************************************************************************/ |
4022 | | /* ExploreLayersPdfium() */ |
4023 | | /************************************************************************/ |
4024 | | |
4025 | | void PDFDataset::ExploreLayersPdfium(GDALPDFArray *poArray, int iPageOfInterest, |
4026 | | int nPageCount, int nRecLevel, |
4027 | | CPLString osTopLayer) |
4028 | | { |
4029 | | if (nRecLevel == 16) |
4030 | | return; |
4031 | | |
4032 | | const int nLength = poArray->GetLength(); |
4033 | | std::string osCurLayer; |
4034 | | for (int i = 0; i < nLength; i++) |
4035 | | { |
4036 | | GDALPDFObject *poObj = poArray->Get(i); |
4037 | | if (poObj == nullptr) |
4038 | | continue; |
4039 | | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
4040 | | { |
4041 | | const std::string osName = |
4042 | | PDFSanitizeLayerName(poObj->GetString().c_str()); |
4043 | | if (!osTopLayer.empty()) |
4044 | | osTopLayer = std::string(osTopLayer).append(".").append(osName); |
4045 | | else |
4046 | | osTopLayer = osName; |
4047 | | AddLayer(osTopLayer, 0); |
4048 | | m_oMapLayerNameToOCGNumGenPdfium[osTopLayer] = std::pair(-1, -1); |
4049 | | } |
4050 | | else if (poObj->GetType() == PDFObjectType_Array) |
4051 | | { |
4052 | | ExploreLayersPdfium(poObj->GetArray(), iPageOfInterest, nPageCount, |
4053 | | nRecLevel + 1, osCurLayer); |
4054 | | osCurLayer.clear(); |
4055 | | } |
4056 | | else if (poObj->GetType() == PDFObjectType_Dictionary) |
4057 | | { |
4058 | | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
4059 | | GDALPDFObject *poName = poDict->Get("Name"); |
4060 | | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
4061 | | { |
4062 | | std::string osName = |
4063 | | PDFSanitizeLayerName(poName->GetString().c_str()); |
4064 | | // coverity[copy_paste_error] |
4065 | | if (!osTopLayer.empty()) |
4066 | | { |
4067 | | osCurLayer = |
4068 | | std::string(osTopLayer).append(".").append(osName); |
4069 | | } |
4070 | | else |
4071 | | osCurLayer = std::move(osName); |
4072 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
4073 | | |
4074 | | const auto oRefPair = |
4075 | | std::pair(poObj->GetRefNum().toInt(), poObj->GetRefGen()); |
4076 | | const std::string osPostfixedName = |
4077 | | BuildPostfixedLayerNameAndAddLayer( |
4078 | | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
4079 | | if (osPostfixedName.empty()) |
4080 | | continue; |
4081 | | |
4082 | | m_aoLayerWithRef.emplace_back( |
4083 | | osPostfixedName, poObj->GetRefNum(), poObj->GetRefGen()); |
4084 | | m_oMapLayerNameToOCGNumGenPdfium[osPostfixedName] = oRefPair; |
4085 | | } |
4086 | | } |
4087 | | } |
4088 | | } |
4089 | | |
4090 | | /************************************************************************/ |
4091 | | /* FindLayersPdfium() */ |
4092 | | /************************************************************************/ |
4093 | | |
4094 | | void PDFDataset::FindLayersPdfium(int iPageOfInterest) |
4095 | | { |
4096 | | int nPageCount = 0; |
4097 | | const auto poPages = GetPagesKids(); |
4098 | | if (poPages) |
4099 | | nPageCount = poPages->GetLength(); |
4100 | | |
4101 | | GDALPDFObject *poCatalog = GetCatalog(); |
4102 | | if (poCatalog == nullptr || |
4103 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4104 | | return; |
4105 | | GDALPDFObject *poOrder = poCatalog->LookupObject("OCProperties.D.Order"); |
4106 | | if (poOrder != nullptr && poOrder->GetType() == PDFObjectType_Array) |
4107 | | { |
4108 | | ExploreLayersPdfium(poOrder->GetArray(), iPageOfInterest, nPageCount, |
4109 | | 0); |
4110 | | } |
4111 | | #if 0 |
4112 | | else |
4113 | | { |
4114 | | GDALPDFObject* poOCGs = poD->GetDictionary()->Get("OCGs"); |
4115 | | if( poOCGs != nullptr && poOCGs->GetType() == PDFObjectType_Array ) |
4116 | | { |
4117 | | GDALPDFArray* poArray = poOCGs->GetArray(); |
4118 | | int nLength = poArray->GetLength(); |
4119 | | for(int i=0;i<nLength;i++) |
4120 | | { |
4121 | | GDALPDFObject* poObj = poArray->Get(i); |
4122 | | if( poObj != nullptr ) |
4123 | | { |
4124 | | // TODO ? |
4125 | | } |
4126 | | } |
4127 | | } |
4128 | | } |
4129 | | #endif |
4130 | | |
4131 | | CreateLayerList(); |
4132 | | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
4133 | | } |
4134 | | |
4135 | | /************************************************************************/ |
4136 | | /* TurnLayersOnOffPdfium() */ |
4137 | | /************************************************************************/ |
4138 | | |
4139 | | void PDFDataset::TurnLayersOnOffPdfium() |
4140 | | { |
4141 | | GDALPDFObject *poCatalog = GetCatalog(); |
4142 | | if (poCatalog == nullptr || |
4143 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4144 | | return; |
4145 | | GDALPDFObject *poOCGs = poCatalog->LookupObject("OCProperties.OCGs"); |
4146 | | if (poOCGs == nullptr || poOCGs->GetType() != PDFObjectType_Array) |
4147 | | return; |
4148 | | |
4149 | | // Which layers to turn ON ? |
4150 | | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
4151 | | if (pszLayers) |
4152 | | { |
4153 | | int i; |
4154 | | int bAll = EQUAL(pszLayers, "ALL"); |
4155 | | |
4156 | | GDALPDFArray *poOCGsArray = poOCGs->GetArray(); |
4157 | | int nLength = poOCGsArray->GetLength(); |
4158 | | for (i = 0; i < nLength; i++) |
4159 | | { |
4160 | | GDALPDFObject *poOCG = poOCGsArray->Get(i); |
4161 | | m_oMapOCGNumGenToVisibilityStatePdfium[std::pair( |
4162 | | poOCG->GetRefNum().toInt(), poOCG->GetRefGen())] = |
4163 | | (bAll) ? VISIBILITY_ON : VISIBILITY_OFF; |
4164 | | } |
4165 | | |
4166 | | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
4167 | | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
4168 | | { |
4169 | | auto oIter = m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4170 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4171 | | { |
4172 | | if (oIter->second.first >= 0) |
4173 | | { |
4174 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
4175 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4176 | | VISIBILITY_ON; |
4177 | | } |
4178 | | |
4179 | | // Turn child layers on, unless there's one of them explicitly |
4180 | | // listed in the list. |
4181 | | size_t nLen = strlen(papszLayers[i]); |
4182 | | int bFoundChildLayer = FALSE; |
4183 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4184 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4185 | | !bFoundChildLayer; |
4186 | | oIter++) |
4187 | | { |
4188 | | if (oIter->first.size() > nLen && |
4189 | | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
4190 | | 0 && |
4191 | | oIter->first[nLen] == '.') |
4192 | | { |
4193 | | for (int j = 0; papszLayers[j] != nullptr; j++) |
4194 | | { |
4195 | | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
4196 | | 0) |
4197 | | bFoundChildLayer = TRUE; |
4198 | | } |
4199 | | } |
4200 | | } |
4201 | | |
4202 | | if (!bFoundChildLayer) |
4203 | | { |
4204 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4205 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4206 | | !bFoundChildLayer; |
4207 | | oIter++) |
4208 | | { |
4209 | | if (oIter->first.size() > nLen && |
4210 | | strncmp(oIter->first.c_str(), papszLayers[i], |
4211 | | nLen) == 0 && |
4212 | | oIter->first[nLen] == '.') |
4213 | | { |
4214 | | if (oIter->second.first >= 0) |
4215 | | { |
4216 | | // CPLDebug("PDF", "Turn '%s' on too", |
4217 | | // oIter->first.c_str()); |
4218 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4219 | | [oIter->second] = VISIBILITY_ON; |
4220 | | } |
4221 | | } |
4222 | | } |
4223 | | } |
4224 | | |
4225 | | // Turn parent layers on too |
4226 | | char *pszLastDot = nullptr; |
4227 | | while ((pszLastDot = strrchr(papszLayers[i], '.')) != nullptr) |
4228 | | { |
4229 | | *pszLastDot = '\0'; |
4230 | | oIter = |
4231 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4232 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4233 | | { |
4234 | | if (oIter->second.first >= 0) |
4235 | | { |
4236 | | // CPLDebug("PDF", "Turn '%s' on too", |
4237 | | // papszLayers[i]); |
4238 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4239 | | [oIter->second] = VISIBILITY_ON; |
4240 | | } |
4241 | | } |
4242 | | } |
4243 | | } |
4244 | | else |
4245 | | { |
4246 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4247 | | papszLayers[i]); |
4248 | | } |
4249 | | } |
4250 | | CSLDestroy(papszLayers); |
4251 | | |
4252 | | m_bUseOCG = true; |
4253 | | } |
4254 | | |
4255 | | // Which layers to turn OFF ? |
4256 | | const char *pszLayersOFF = |
4257 | | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
4258 | | if (pszLayersOFF) |
4259 | | { |
4260 | | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
4261 | | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
4262 | | { |
4263 | | auto oIter = |
4264 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayersOFF[i]); |
4265 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4266 | | { |
4267 | | if (oIter->second.first >= 0) |
4268 | | { |
4269 | | // CPLDebug("PDF", "Turn '%s' (%d,%d) off", |
4270 | | // papszLayersOFF[i], oIter->second.first, |
4271 | | // oIter->second.second); |
4272 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4273 | | VISIBILITY_OFF; |
4274 | | } |
4275 | | |
4276 | | // Turn child layers off too |
4277 | | size_t nLen = strlen(papszLayersOFF[i]); |
4278 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4279 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end(); oIter++) |
4280 | | { |
4281 | | if (oIter->first.size() > nLen && |
4282 | | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
4283 | | nLen) == 0 && |
4284 | | oIter->first[nLen] == '.') |
4285 | | { |
4286 | | if (oIter->second.first >= 0) |
4287 | | { |
4288 | | // CPLDebug("PDF", "Turn '%s' off too", |
4289 | | // oIter->first.c_str()); |
4290 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4291 | | [oIter->second] = VISIBILITY_OFF; |
4292 | | } |
4293 | | } |
4294 | | } |
4295 | | } |
4296 | | else |
4297 | | { |
4298 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4299 | | papszLayersOFF[i]); |
4300 | | } |
4301 | | } |
4302 | | CSLDestroy(papszLayersOFF); |
4303 | | |
4304 | | m_bUseOCG = true; |
4305 | | } |
4306 | | } |
4307 | | |
4308 | | /************************************************************************/ |
4309 | | /* GetVisibilityStateForOGCPdfium() */ |
4310 | | /************************************************************************/ |
4311 | | |
4312 | | PDFDataset::VisibilityState PDFDataset::GetVisibilityStateForOGCPdfium(int nNum, |
4313 | | int nGen) |
4314 | | { |
4315 | | auto oIter = |
4316 | | m_oMapOCGNumGenToVisibilityStatePdfium.find(std::pair(nNum, nGen)); |
4317 | | if (oIter == m_oMapOCGNumGenToVisibilityStatePdfium.end()) |
4318 | | return VISIBILITY_DEFAULT; |
4319 | | return oIter->second; |
4320 | | } |
4321 | | |
4322 | | #endif /* HAVE_PDFIUM */ |
4323 | | |
4324 | | /************************************************************************/ |
4325 | | /* GetPagesKids() */ |
4326 | | /************************************************************************/ |
4327 | | |
4328 | | GDALPDFArray *PDFDataset::GetPagesKids() |
4329 | 65.3k | { |
4330 | 65.3k | const auto poCatalog = GetCatalog(); |
4331 | 65.3k | if (!poCatalog || poCatalog->GetType() != PDFObjectType_Dictionary) |
4332 | 0 | { |
4333 | 0 | return nullptr; |
4334 | 0 | } |
4335 | 65.3k | const auto poKids = poCatalog->LookupObject("Pages.Kids"); |
4336 | 65.3k | if (!poKids || poKids->GetType() != PDFObjectType_Array) |
4337 | 2.16k | { |
4338 | 2.16k | return nullptr; |
4339 | 2.16k | } |
4340 | 63.1k | return poKids->GetArray(); |
4341 | 65.3k | } |
4342 | | |
4343 | | /************************************************************************/ |
4344 | | /* MapOCGsToPages() */ |
4345 | | /************************************************************************/ |
4346 | | |
4347 | | void PDFDataset::MapOCGsToPages() |
4348 | 32.6k | { |
4349 | 32.6k | const auto poKidsArray = GetPagesKids(); |
4350 | 32.6k | if (!poKidsArray) |
4351 | 1.08k | { |
4352 | 1.08k | return; |
4353 | 1.08k | } |
4354 | 31.5k | const int nKidsArrayLength = poKidsArray->GetLength(); |
4355 | 89.6k | for (int iPage = 0; iPage < nKidsArrayLength; ++iPage) |
4356 | 58.0k | { |
4357 | 58.0k | const auto poPage = poKidsArray->Get(iPage); |
4358 | 58.0k | if (poPage && poPage->GetType() == PDFObjectType_Dictionary) |
4359 | 39.3k | { |
4360 | 39.3k | const auto poXObject = poPage->LookupObject("Resources.XObject"); |
4361 | 39.3k | if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary) |
4362 | 11.8k | { |
4363 | 11.8k | for (const auto &oNameObjectPair : |
4364 | 11.8k | poXObject->GetDictionary()->GetValues()) |
4365 | 79.7k | { |
4366 | 79.7k | const auto poProperties = |
4367 | 79.7k | oNameObjectPair.second->LookupObject( |
4368 | 79.7k | "Resources.Properties"); |
4369 | 79.7k | if (poProperties && |
4370 | 228 | poProperties->GetType() == PDFObjectType_Dictionary) |
4371 | 226 | { |
4372 | 226 | const auto &oMap = |
4373 | 226 | poProperties->GetDictionary()->GetValues(); |
4374 | 226 | for (const auto &[osKey, poObj] : oMap) |
4375 | 444 | { |
4376 | 444 | if (poObj->GetRefNum().toBool() && |
4377 | 402 | poObj->GetType() == PDFObjectType_Dictionary) |
4378 | 394 | { |
4379 | 394 | GDALPDFObject *poType = |
4380 | 394 | poObj->GetDictionary()->Get("Type"); |
4381 | 394 | GDALPDFObject *poName = |
4382 | 394 | poObj->GetDictionary()->Get("Name"); |
4383 | 394 | if (poType && |
4384 | 362 | poType->GetType() == PDFObjectType_Name && |
4385 | 362 | poType->GetName() == "OCG" && poName && |
4386 | 351 | poName->GetType() == PDFObjectType_String) |
4387 | 351 | { |
4388 | 351 | m_oMapOCGNumGenToPages |
4389 | 351 | [std::pair(poObj->GetRefNum().toInt(), |
4390 | 351 | poObj->GetRefGen())] |
4391 | 351 | .push_back(iPage + 1); |
4392 | 351 | } |
4393 | 394 | } |
4394 | 444 | } |
4395 | 226 | } |
4396 | 79.7k | } |
4397 | 11.8k | } |
4398 | 39.3k | } |
4399 | 58.0k | } |
4400 | 31.5k | } |
4401 | | |
4402 | | /************************************************************************/ |
4403 | | /* FindLayerOCG() */ |
4404 | | /************************************************************************/ |
4405 | | |
4406 | | CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary *poPageDict, |
4407 | | const char *pszLayerName) |
4408 | 7.90k | { |
4409 | 7.90k | GDALPDFObject *poProperties = |
4410 | 7.90k | poPageDict->LookupObject("Resources.Properties"); |
4411 | 7.90k | if (poProperties != nullptr && |
4412 | 2.68k | poProperties->GetType() == PDFObjectType_Dictionary) |
4413 | 2.67k | { |
4414 | 2.67k | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4415 | 2.67k | for (const auto &[osKey, poObj] : oMap) |
4416 | 14.7k | { |
4417 | 14.7k | if (poObj->GetRefNum().toBool() && |
4418 | 14.5k | poObj->GetType() == PDFObjectType_Dictionary) |
4419 | 14.4k | { |
4420 | 14.4k | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4421 | 14.4k | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4422 | 14.4k | if (poType != nullptr && |
4423 | 14.1k | poType->GetType() == PDFObjectType_Name && |
4424 | 14.1k | poType->GetName() == "OCG" && poName != nullptr && |
4425 | 13.9k | poName->GetType() == PDFObjectType_String) |
4426 | 13.9k | { |
4427 | 13.9k | if (poName->GetString() == pszLayerName) |
4428 | 0 | return osKey; |
4429 | 13.9k | } |
4430 | 14.4k | } |
4431 | 14.7k | } |
4432 | 2.67k | } |
4433 | 7.90k | return ""; |
4434 | 7.90k | } |
4435 | | |
4436 | | /************************************************************************/ |
4437 | | /* FindLayersGeneric() */ |
4438 | | /************************************************************************/ |
4439 | | |
4440 | | void PDFDataset::FindLayersGeneric(GDALPDFDictionary *poPageDict) |
4441 | 0 | { |
4442 | 0 | GDALPDFObject *poProperties = |
4443 | 0 | poPageDict->LookupObject("Resources.Properties"); |
4444 | 0 | if (poProperties != nullptr && |
4445 | 0 | poProperties->GetType() == PDFObjectType_Dictionary) |
4446 | 0 | { |
4447 | 0 | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4448 | 0 | for (const auto &[osKey, poObj] : oMap) |
4449 | 0 | { |
4450 | 0 | if (poObj->GetRefNum().toBool() && |
4451 | 0 | poObj->GetType() == PDFObjectType_Dictionary) |
4452 | 0 | { |
4453 | 0 | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4454 | 0 | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4455 | 0 | if (poType != nullptr && |
4456 | 0 | poType->GetType() == PDFObjectType_Name && |
4457 | 0 | poType->GetName() == "OCG" && poName != nullptr && |
4458 | 0 | poName->GetType() == PDFObjectType_String) |
4459 | 0 | { |
4460 | 0 | m_aoLayerWithRef.emplace_back( |
4461 | 0 | PDFSanitizeLayerName(poName->GetString().c_str()) |
4462 | 0 | .c_str(), |
4463 | 0 | poObj->GetRefNum(), poObj->GetRefGen()); |
4464 | 0 | } |
4465 | 0 | } |
4466 | 0 | } |
4467 | 0 | } |
4468 | 0 | } |
4469 | | |
4470 | | /************************************************************************/ |
4471 | | /* Open() */ |
4472 | | /************************************************************************/ |
4473 | | |
4474 | | PDFDataset *PDFDataset::Open(GDALOpenInfo *poOpenInfo) |
4475 | | |
4476 | 43.4k | { |
4477 | 43.4k | if (!PDFDatasetIdentify(poOpenInfo)) |
4478 | 0 | return nullptr; |
4479 | | |
4480 | 43.4k | const char *pszUserPwd = |
4481 | 43.4k | GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", nullptr); |
4482 | | |
4483 | 43.4k | const bool bOpenSubdataset = STARTS_WITH(poOpenInfo->pszFilename, "PDF:"); |
4484 | 43.4k | const bool bOpenSubdatasetImage = |
4485 | 43.4k | STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:"); |
4486 | 43.4k | int iPage = -1; |
4487 | 43.4k | int nImageNum = -1; |
4488 | 43.4k | std::string osSubdatasetName; |
4489 | 43.4k | const char *pszFilename = poOpenInfo->pszFilename; |
4490 | | |
4491 | 43.4k | if (bOpenSubdataset) |
4492 | 0 | { |
4493 | 0 | iPage = atoi(pszFilename + 4); |
4494 | 0 | if (iPage <= 0) |
4495 | 0 | return nullptr; |
4496 | 0 | pszFilename = strchr(pszFilename + 4, ':'); |
4497 | 0 | if (pszFilename == nullptr) |
4498 | 0 | return nullptr; |
4499 | 0 | pszFilename++; |
4500 | 0 | osSubdatasetName = CPLSPrintf("Page %d", iPage); |
4501 | 0 | } |
4502 | 43.4k | else if (bOpenSubdatasetImage) |
4503 | 0 | { |
4504 | 0 | iPage = atoi(pszFilename + 10); |
4505 | 0 | if (iPage <= 0) |
4506 | 0 | return nullptr; |
4507 | 0 | const char *pszNext = strchr(pszFilename + 10, ':'); |
4508 | 0 | if (pszNext == nullptr) |
4509 | 0 | return nullptr; |
4510 | 0 | nImageNum = atoi(pszNext + 1); |
4511 | 0 | if (nImageNum <= 0) |
4512 | 0 | return nullptr; |
4513 | 0 | pszFilename = strchr(pszNext + 1, ':'); |
4514 | 0 | if (pszFilename == nullptr) |
4515 | 0 | return nullptr; |
4516 | 0 | pszFilename++; |
4517 | 0 | osSubdatasetName = CPLSPrintf("Image %d", nImageNum); |
4518 | 0 | } |
4519 | 43.4k | else |
4520 | 43.4k | iPage = 1; |
4521 | | |
4522 | 43.4k | std::bitset<PDFLIB_COUNT> bHasLib; |
4523 | 43.4k | bHasLib.reset(); |
4524 | | // Each library set their flag |
4525 | 43.4k | #if defined(HAVE_POPPLER) |
4526 | 43.4k | bHasLib.set(PDFLIB_POPPLER); |
4527 | 43.4k | #endif // HAVE_POPPLER |
4528 | | #if defined(HAVE_PODOFO) |
4529 | | bHasLib.set(PDFLIB_PODOFO); |
4530 | | #endif // HAVE_PODOFO |
4531 | | #if defined(HAVE_PDFIUM) |
4532 | | bHasLib.set(PDFLIB_PDFIUM); |
4533 | | #endif // HAVE_PDFIUM |
4534 | | |
4535 | 43.4k | std::bitset<PDFLIB_COUNT> bUseLib; |
4536 | | |
4537 | | // More than one library available |
4538 | | // Detect which one |
4539 | 43.4k | if (bHasLib.count() != 1) |
4540 | 0 | { |
4541 | 0 | const char *pszDefaultLib = bHasLib.test(PDFLIB_PDFIUM) ? "PDFIUM" |
4542 | 0 | : bHasLib.test(PDFLIB_POPPLER) ? "POPPLER" |
4543 | 0 | : "PODOFO"; |
4544 | 0 | const char *pszPDFLib = |
4545 | 0 | GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", pszDefaultLib); |
4546 | 0 | while (true) |
4547 | 0 | { |
4548 | 0 | if (EQUAL(pszPDFLib, "POPPLER")) |
4549 | 0 | bUseLib.set(PDFLIB_POPPLER); |
4550 | 0 | else if (EQUAL(pszPDFLib, "PODOFO")) |
4551 | 0 | bUseLib.set(PDFLIB_PODOFO); |
4552 | 0 | else if (EQUAL(pszPDFLib, "PDFIUM")) |
4553 | 0 | bUseLib.set(PDFLIB_PDFIUM); |
4554 | |
|
4555 | 0 | if (bUseLib.count() != 1 || (bHasLib & bUseLib) == 0) |
4556 | 0 | { |
4557 | 0 | CPLDebug("PDF", |
4558 | 0 | "Invalid value for GDAL_PDF_LIB config option: %s. " |
4559 | 0 | "Fallback to %s", |
4560 | 0 | pszPDFLib, pszDefaultLib); |
4561 | 0 | pszPDFLib = pszDefaultLib; |
4562 | 0 | bUseLib.reset(); |
4563 | 0 | } |
4564 | 0 | else |
4565 | 0 | break; |
4566 | 0 | } |
4567 | 0 | } |
4568 | 43.4k | else |
4569 | 43.4k | bUseLib = bHasLib; |
4570 | | |
4571 | 43.4k | GDALPDFObject *poPageObj = nullptr; |
4572 | 43.4k | #ifdef HAVE_POPPLER |
4573 | 43.4k | PDFDoc *poDocPoppler = nullptr; |
4574 | 43.4k | Page *poPagePoppler = nullptr; |
4575 | 43.4k | Catalog *poCatalogPoppler = nullptr; |
4576 | 43.4k | #endif |
4577 | | #ifdef HAVE_PODOFO |
4578 | | std::unique_ptr<PoDoFo::PdfMemDocument> poDocPodofo; |
4579 | | PoDoFo::PdfPage *poPagePodofo = nullptr; |
4580 | | #endif |
4581 | | #ifdef HAVE_PDFIUM |
4582 | | TPdfiumDocumentStruct *poDocPdfium = nullptr; |
4583 | | TPdfiumPageStruct *poPagePdfium = nullptr; |
4584 | | #endif |
4585 | 43.4k | int nPages = 0; |
4586 | 43.4k | VSIVirtualHandleUniquePtr fp; |
4587 | | |
4588 | 43.4k | #ifdef HAVE_POPPLER |
4589 | 43.4k | if (bUseLib.test(PDFLIB_POPPLER)) |
4590 | 43.4k | { |
4591 | 43.4k | static bool globalParamsCreatedByGDAL = false; |
4592 | 43.4k | { |
4593 | 43.4k | CPLMutexHolderD(&hGlobalParamsMutex); |
4594 | | /* poppler global variable */ |
4595 | 43.4k | if (globalParams == nullptr) |
4596 | 11 | { |
4597 | 11 | globalParamsCreatedByGDAL = true; |
4598 | 11 | globalParams.reset(new GlobalParams()); |
4599 | 11 | } |
4600 | | |
4601 | 43.4k | globalParams->setPrintCommands(CPLTestBool( |
4602 | 43.4k | CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE"))); |
4603 | 43.4k | } |
4604 | | |
4605 | 43.4k | const auto registerErrorCallback = []() |
4606 | 86.8k | { |
4607 | | /* Set custom error handler for poppler errors */ |
4608 | 86.8k | setErrorCallback(PDFDatasetErrorFunction); |
4609 | 86.8k | assert(globalParams); // avoid CSA false positive |
4610 | 86.8k | globalParams->setErrQuiet(false); |
4611 | 86.8k | }; |
4612 | | |
4613 | 43.4k | fp.reset(VSIFOpenL(pszFilename, "rb")); |
4614 | 43.4k | if (!fp) |
4615 | 0 | return nullptr; |
4616 | | |
4617 | 43.4k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
4618 | 43.4k | { |
4619 | | // Workaround for ossfuzz only due to |
4620 | | // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37584 |
4621 | | // https://gitlab.freedesktop.org/poppler/poppler/-/issues/1137 |
4622 | 43.4k | GByte *pabyRet = nullptr; |
4623 | 43.4k | vsi_l_offset nSize = 0; |
4624 | 43.4k | if (VSIIngestFile(fp.get(), pszFilename, &pabyRet, &nSize, |
4625 | 43.4k | 10 * 1024 * 1024)) |
4626 | 43.4k | { |
4627 | | // Replace nul byte by something else so that strstr() works |
4628 | 1.53G | for (size_t i = 0; i < nSize; i++) |
4629 | 1.53G | { |
4630 | 1.53G | if (pabyRet[i] == 0) |
4631 | 65.9M | pabyRet[i] = ' '; |
4632 | 1.53G | } |
4633 | 43.4k | if (strstr(reinterpret_cast<const char *>(pabyRet), |
4634 | 43.4k | "/JBIG2Decode")) |
4635 | 16 | { |
4636 | 16 | CPLError(CE_Failure, CPLE_AppDefined, |
4637 | 16 | "/JBIG2Decode found. Giving up due to potential " |
4638 | 16 | "very long processing time."); |
4639 | 16 | CPLFree(pabyRet); |
4640 | 16 | return nullptr; |
4641 | 16 | } |
4642 | 43.4k | } |
4643 | 43.4k | CPLFree(pabyRet); |
4644 | 43.4k | } |
4645 | 0 | #endif |
4646 | | |
4647 | 0 | fp.reset(VSICreateBufferedReaderHandle(fp.release())); |
4648 | 43.4k | while (true) |
4649 | 43.4k | { |
4650 | 43.4k | fp->Seek(0, SEEK_SET); |
4651 | 43.4k | g_nPopplerErrors = 0; |
4652 | 43.4k | if (globalParamsCreatedByGDAL) |
4653 | 43.4k | registerErrorCallback(); |
4654 | 43.4k | Object oObj; |
4655 | 43.4k | auto poStream = std::make_unique<VSIPDFFileStream>( |
4656 | 43.4k | fp.get(), pszFilename, std::move(oObj)); |
4657 | 43.4k | const bool bFoundLinearizedHint = poStream->FoundLinearizedHint(); |
4658 | 43.4k | #if POPPLER_MAJOR_VERSION > 22 || \ |
4659 | 43.4k | (POPPLER_MAJOR_VERSION == 22 && POPPLER_MINOR_VERSION > 2) |
4660 | 43.4k | std::optional<GooString> osUserPwd; |
4661 | 43.4k | if (pszUserPwd) |
4662 | 0 | osUserPwd = std::optional<GooString>(pszUserPwd); |
4663 | 43.4k | try |
4664 | 43.4k | { |
4665 | | #if POPPLER_MAJOR_VERSION > 26 || \ |
4666 | | (POPPLER_MAJOR_VERSION == 26 && POPPLER_MINOR_VERSION >= 2) |
4667 | | poDocPoppler = new PDFDoc( |
4668 | | std::move(poStream), std::optional<GooString>(), osUserPwd); |
4669 | | #else |
4670 | 43.4k | poDocPoppler = new PDFDoc( |
4671 | 43.4k | poStream.release(), std::optional<GooString>(), osUserPwd); |
4672 | 43.4k | #endif |
4673 | 43.4k | } |
4674 | 43.4k | catch (const std::exception &e) |
4675 | 43.4k | { |
4676 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4677 | 0 | "PDFDoc::PDFDoc() failed with %s", e.what()); |
4678 | 0 | return nullptr; |
4679 | 0 | } |
4680 | | #else |
4681 | | GooString *poUserPwd = nullptr; |
4682 | | if (pszUserPwd) |
4683 | | poUserPwd = new GooString(pszUserPwd); |
4684 | | poDocPoppler = new PDFDoc(poStream.release(), nullptr, poUserPwd); |
4685 | | delete poUserPwd; |
4686 | | #endif |
4687 | 43.4k | if (globalParamsCreatedByGDAL) |
4688 | 43.4k | registerErrorCallback(); |
4689 | 43.4k | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
4690 | 4.60k | { |
4691 | 4.60k | PDFFreeDoc(poDocPoppler); |
4692 | 4.60k | return nullptr; |
4693 | 4.60k | } |
4694 | | |
4695 | 38.8k | if (!poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0) |
4696 | 5.95k | { |
4697 | 5.95k | if (poDocPoppler->getErrorCode() == errEncrypted) |
4698 | 74 | { |
4699 | 74 | if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
4700 | 0 | { |
4701 | 0 | pszUserPwd = |
4702 | 0 | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4703 | 0 | PDFFreeDoc(poDocPoppler); |
4704 | | |
4705 | | /* Reset errors that could have been issued during |
4706 | | * opening and that */ |
4707 | | /* did not result in an invalid document */ |
4708 | 0 | CPLErrorReset(); |
4709 | |
|
4710 | 0 | continue; |
4711 | 0 | } |
4712 | 74 | else if (pszUserPwd == nullptr) |
4713 | 74 | { |
4714 | 74 | CPLError(CE_Failure, CPLE_AppDefined, |
4715 | 74 | "A password is needed. You can specify it " |
4716 | 74 | "through the PDF_USER_PWD " |
4717 | 74 | "configuration option / USER_PWD open option " |
4718 | 74 | "(that can be set to ASK_INTERACTIVE)"); |
4719 | 74 | } |
4720 | 0 | else |
4721 | 0 | { |
4722 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4723 | 0 | "Invalid password"); |
4724 | 0 | } |
4725 | 74 | } |
4726 | 5.87k | else |
4727 | 5.87k | { |
4728 | 5.87k | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4729 | 5.87k | } |
4730 | | |
4731 | 5.95k | PDFFreeDoc(poDocPoppler); |
4732 | 5.95k | return nullptr; |
4733 | 5.95k | } |
4734 | 32.8k | else if (poDocPoppler->isLinearized() && !bFoundLinearizedHint) |
4735 | 1 | { |
4736 | | // This is a likely defect of poppler Linearization.cc file that |
4737 | | // recognizes a file as linearized if the /Linearized hint is |
4738 | | // missing, but the content of this dictionary are present. But |
4739 | | // given the hacks of PDFFreeDoc() and |
4740 | | // VSIPDFFileStream::FillBuffer() opening such a file will |
4741 | | // result in a null-ptr deref at closing if we try to access a |
4742 | | // page and build the page cache, so just exit now |
4743 | 1 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4744 | | |
4745 | 1 | PDFFreeDoc(poDocPoppler); |
4746 | 1 | return nullptr; |
4747 | 1 | } |
4748 | 32.8k | else |
4749 | 32.8k | { |
4750 | 32.8k | break; |
4751 | 32.8k | } |
4752 | 38.8k | } |
4753 | | |
4754 | 32.8k | poCatalogPoppler = poDocPoppler->getCatalog(); |
4755 | 32.8k | if (poCatalogPoppler == nullptr || !poCatalogPoppler->isOk()) |
4756 | 0 | { |
4757 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4758 | 0 | "Invalid PDF : invalid catalog"); |
4759 | 0 | PDFFreeDoc(poDocPoppler); |
4760 | 0 | return nullptr; |
4761 | 0 | } |
4762 | | |
4763 | 32.8k | nPages = poDocPoppler->getNumPages(); |
4764 | | |
4765 | 32.8k | if (iPage == 1 && nPages > 10000 && |
4766 | 0 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_LIMIT_PAGE_COUNT", "YES"))) |
4767 | 0 | { |
4768 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
4769 | 0 | "This PDF document reports %d pages. " |
4770 | 0 | "Limiting count to 10000 for performance reasons. " |
4771 | 0 | "You may remove this limit by setting the " |
4772 | 0 | "GDAL_PDF_LIMIT_PAGE_COUNT configuration option to NO", |
4773 | 0 | nPages); |
4774 | 0 | nPages = 10000; |
4775 | 0 | } |
4776 | | |
4777 | 32.8k | if (iPage < 1 || iPage > nPages) |
4778 | 0 | { |
4779 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4780 | 0 | iPage, nPages); |
4781 | 0 | PDFFreeDoc(poDocPoppler); |
4782 | 0 | return nullptr; |
4783 | 0 | } |
4784 | | |
4785 | | /* Sanity check to validate page count */ |
4786 | 32.8k | if (iPage > 1 && nPages <= 10000 && iPage != nPages) |
4787 | 0 | { |
4788 | 0 | poPagePoppler = poCatalogPoppler->getPage(nPages); |
4789 | 0 | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4790 | 0 | { |
4791 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4792 | 0 | "Invalid PDF : invalid page count"); |
4793 | 0 | PDFFreeDoc(poDocPoppler); |
4794 | 0 | return nullptr; |
4795 | 0 | } |
4796 | 0 | } |
4797 | | |
4798 | 32.8k | poPagePoppler = poCatalogPoppler->getPage(iPage); |
4799 | 32.8k | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4800 | 161 | { |
4801 | 161 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
4802 | 161 | PDFFreeDoc(poDocPoppler); |
4803 | 161 | return nullptr; |
4804 | 161 | } |
4805 | | |
4806 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
4807 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 3) |
4808 | | const Object &oPageObj = poPagePoppler->getPageObj(); |
4809 | | #else |
4810 | | /* Here's the dirty part: this is a private member */ |
4811 | | /* so we had to #define private public to get it ! */ |
4812 | 32.6k | const Object &oPageObj = poPagePoppler->pageObj; |
4813 | 32.6k | #endif |
4814 | 32.6k | if (!oPageObj.isDict()) |
4815 | 0 | { |
4816 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4817 | 0 | "Invalid PDF : !oPageObj.isDict()"); |
4818 | 0 | PDFFreeDoc(poDocPoppler); |
4819 | 0 | return nullptr; |
4820 | 0 | } |
4821 | | |
4822 | 32.6k | poPageObj = new GDALPDFObjectPoppler(&oPageObj); |
4823 | 32.6k | Ref *poPageRef = poCatalogPoppler->getPageRef(iPage); |
4824 | 32.6k | if (poPageRef != nullptr) |
4825 | 32.6k | { |
4826 | 32.6k | cpl::down_cast<GDALPDFObjectPoppler *>(poPageObj)->SetRefNumAndGen( |
4827 | 32.6k | GDALPDFObjectNum(poPageRef->num), poPageRef->gen); |
4828 | 32.6k | } |
4829 | 32.6k | } |
4830 | 32.6k | #endif // ~ HAVE_POPPLER |
4831 | | |
4832 | | #ifdef HAVE_PODOFO |
4833 | | if (bUseLib.test(PDFLIB_PODOFO) && poPageObj == nullptr) |
4834 | | { |
4835 | | #if !(PODOFO_VERSION_MAJOR > 0 || \ |
4836 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)) |
4837 | | PoDoFo::PdfError::EnableDebug(false); |
4838 | | PoDoFo::PdfError::EnableLogging(false); |
4839 | | #endif |
4840 | | |
4841 | | poDocPodofo = std::make_unique<PoDoFo::PdfMemDocument>(); |
4842 | | try |
4843 | | { |
4844 | | poDocPodofo->Load(pszFilename); |
4845 | | } |
4846 | | catch (PoDoFo::PdfError &oError) |
4847 | | { |
4848 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4849 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4850 | | if (oError.GetCode() == PoDoFo::PdfErrorCode::InvalidPassword) |
4851 | | #else |
4852 | | if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword) |
4853 | | #endif |
4854 | | { |
4855 | | if (pszUserPwd) |
4856 | | { |
4857 | | pszUserPwd = |
4858 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4859 | | |
4860 | | try |
4861 | | { |
4862 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4863 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4864 | | poDocPodofo = |
4865 | | std::make_unique<PoDoFo::PdfMemDocument>(); |
4866 | | poDocPodofo->Load(pszFilename, pszUserPwd); |
4867 | | #else |
4868 | | poDocPodofo->SetPassword(pszUserPwd); |
4869 | | #endif |
4870 | | } |
4871 | | catch (PoDoFo::PdfError &oError2) |
4872 | | { |
4873 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4874 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4875 | | if (oError2.GetCode() == |
4876 | | PoDoFo::PdfErrorCode::InvalidPassword) |
4877 | | #else |
4878 | | if (oError2.GetError() == |
4879 | | PoDoFo::ePdfError_InvalidPassword) |
4880 | | #endif |
4881 | | { |
4882 | | CPLError(CE_Failure, CPLE_AppDefined, |
4883 | | "Invalid password"); |
4884 | | } |
4885 | | else |
4886 | | { |
4887 | | CPLError(CE_Failure, CPLE_AppDefined, |
4888 | | "Invalid PDF : %s", oError2.what()); |
4889 | | } |
4890 | | return nullptr; |
4891 | | } |
4892 | | catch (...) |
4893 | | { |
4894 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4895 | | return nullptr; |
4896 | | } |
4897 | | } |
4898 | | else |
4899 | | { |
4900 | | CPLError(CE_Failure, CPLE_AppDefined, |
4901 | | "A password is needed. You can specify it through " |
4902 | | "the PDF_USER_PWD " |
4903 | | "configuration option / USER_PWD open option " |
4904 | | "(that can be set to ASK_INTERACTIVE)"); |
4905 | | return nullptr; |
4906 | | } |
4907 | | } |
4908 | | else |
4909 | | { |
4910 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4911 | | oError.what()); |
4912 | | return nullptr; |
4913 | | } |
4914 | | } |
4915 | | catch (...) |
4916 | | { |
4917 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4918 | | return nullptr; |
4919 | | } |
4920 | | |
4921 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4922 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4923 | | auto &oPageCollections = poDocPodofo->GetPages(); |
4924 | | nPages = static_cast<int>(oPageCollections.GetCount()); |
4925 | | #else |
4926 | | nPages = poDocPodofo->GetPageCount(); |
4927 | | #endif |
4928 | | if (iPage < 1 || iPage > nPages) |
4929 | | { |
4930 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4931 | | iPage, nPages); |
4932 | | return nullptr; |
4933 | | } |
4934 | | |
4935 | | try |
4936 | | { |
4937 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4938 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4939 | | /* Sanity check to validate page count */ |
4940 | | if (iPage != nPages) |
4941 | | CPL_IGNORE_RET_VAL(oPageCollections.GetPageAt(nPages - 1)); |
4942 | | |
4943 | | poPagePodofo = &oPageCollections.GetPageAt(iPage - 1); |
4944 | | #else |
4945 | | /* Sanity check to validate page count */ |
4946 | | if (iPage != nPages) |
4947 | | CPL_IGNORE_RET_VAL(poDocPodofo->GetPage(nPages - 1)); |
4948 | | |
4949 | | poPagePodofo = poDocPodofo->GetPage(iPage - 1); |
4950 | | #endif |
4951 | | } |
4952 | | catch (PoDoFo::PdfError &oError) |
4953 | | { |
4954 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4955 | | oError.what()); |
4956 | | return nullptr; |
4957 | | } |
4958 | | catch (...) |
4959 | | { |
4960 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4961 | | return nullptr; |
4962 | | } |
4963 | | |
4964 | | if (poPagePodofo == nullptr) |
4965 | | { |
4966 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
4967 | | return nullptr; |
4968 | | } |
4969 | | |
4970 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4971 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4972 | | const PoDoFo::PdfObject *pObj = &poPagePodofo->GetObject(); |
4973 | | #else |
4974 | | const PoDoFo::PdfObject *pObj = poPagePodofo->GetObject(); |
4975 | | #endif |
4976 | | poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects()); |
4977 | | } |
4978 | | #endif // ~ HAVE_PODOFO |
4979 | | |
4980 | | #ifdef HAVE_PDFIUM |
4981 | | if (bUseLib.test(PDFLIB_PDFIUM) && poPageObj == nullptr) |
4982 | | { |
4983 | | if (!LoadPdfiumDocumentPage(pszFilename, pszUserPwd, iPage, |
4984 | | &poDocPdfium, &poPagePdfium, &nPages)) |
4985 | | { |
4986 | | // CPLError is called inside function |
4987 | | return nullptr; |
4988 | | } |
4989 | | |
4990 | | const auto pageObj = poPagePdfium->page->GetDict(); |
4991 | | if (pageObj == nullptr) |
4992 | | { |
4993 | | CPLError(CE_Failure, CPLE_AppDefined, |
4994 | | "Invalid PDF : invalid page object"); |
4995 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
4996 | | return nullptr; |
4997 | | } |
4998 | | poPageObj = GDALPDFObjectPdfium::Build(pageObj); |
4999 | | } |
5000 | | #endif // ~ HAVE_PDFIUM |
5001 | | |
5002 | 32.6k | if (poPageObj == nullptr) |
5003 | 0 | return nullptr; |
5004 | 32.6k | GDALPDFDictionary *poPageDict = poPageObj->GetDictionary(); |
5005 | 32.6k | if (poPageDict == nullptr) |
5006 | 0 | { |
5007 | 0 | delete poPageObj; |
5008 | |
|
5009 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5010 | 0 | "Invalid PDF : poPageDict == nullptr"); |
5011 | 0 | #ifdef HAVE_POPPLER |
5012 | 0 | if (bUseLib.test(PDFLIB_POPPLER)) |
5013 | 0 | PDFFreeDoc(poDocPoppler); |
5014 | 0 | #endif |
5015 | | #ifdef HAVE_PDFIUM |
5016 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5017 | | { |
5018 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
5019 | | } |
5020 | | #endif |
5021 | 0 | return nullptr; |
5022 | 0 | } |
5023 | | |
5024 | 32.6k | const char *pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", nullptr); |
5025 | 32.6k | if (pszDumpObject != nullptr) |
5026 | 0 | { |
5027 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpObject); |
5028 | 0 | oDumper.Dump(poPageObj); |
5029 | 0 | } |
5030 | | |
5031 | 32.6k | PDFDataset *poDS = new PDFDataset(); |
5032 | 32.6k | poDS->m_fp = std::move(fp); |
5033 | 32.6k | poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions); |
5034 | 32.6k | poDS->m_bUseLib = bUseLib; |
5035 | 32.6k | poDS->m_osFilename = pszFilename; |
5036 | 32.6k | poDS->eAccess = poOpenInfo->eAccess; |
5037 | | |
5038 | 32.6k | if (nPages > 1 && !bOpenSubdataset) |
5039 | 3.56k | { |
5040 | 3.56k | int i; |
5041 | 3.56k | CPLStringList aosList; |
5042 | 86.3k | for (i = 0; i < nPages; i++) |
5043 | 82.7k | { |
5044 | 82.7k | char szKey[32]; |
5045 | 82.7k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_NAME", i + 1); |
5046 | 82.7k | aosList.AddNameValue( |
5047 | 82.7k | szKey, CPLSPrintf("PDF:%d:%s", i + 1, poOpenInfo->pszFilename)); |
5048 | 82.7k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_DESC", i + 1); |
5049 | 82.7k | aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i + 1, |
5050 | 82.7k | poOpenInfo->pszFilename)); |
5051 | 82.7k | } |
5052 | 3.56k | poDS->SetMetadata(aosList.List(), "SUBDATASETS"); |
5053 | 3.56k | } |
5054 | | |
5055 | 32.6k | #ifdef HAVE_POPPLER |
5056 | 32.6k | poDS->m_poDocPoppler = poDocPoppler; |
5057 | 32.6k | #endif |
5058 | | #ifdef HAVE_PODOFO |
5059 | | poDS->m_poDocPodofo = poDocPodofo.release(); |
5060 | | #endif |
5061 | | #ifdef HAVE_PDFIUM |
5062 | | poDS->m_poDocPdfium = poDocPdfium; |
5063 | | poDS->m_poPagePdfium = poPagePdfium; |
5064 | | #endif |
5065 | 32.6k | poDS->m_poPageObj = poPageObj; |
5066 | 32.6k | poDS->m_osUserPwd = pszUserPwd ? pszUserPwd : ""; |
5067 | 32.6k | poDS->m_iPage = iPage; |
5068 | | |
5069 | 32.6k | const char *pszDumpCatalog = |
5070 | 32.6k | CPLGetConfigOption("PDF_DUMP_CATALOG", nullptr); |
5071 | 32.6k | if (pszDumpCatalog != nullptr) |
5072 | 0 | { |
5073 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpCatalog); |
5074 | 0 | auto poCatalog = poDS->GetCatalog(); |
5075 | 0 | if (poCatalog) |
5076 | 0 | oDumper.Dump(poCatalog); |
5077 | 0 | } |
5078 | | |
5079 | 32.6k | int nBandsGuessed = 0; |
5080 | 32.6k | if (nImageNum < 0) |
5081 | 32.6k | { |
5082 | 32.6k | double dfDPI = std::numeric_limits<double>::quiet_NaN(); |
5083 | 32.6k | poDS->GuessDPIAndBandCount(poPageDict, dfDPI, nBandsGuessed); |
5084 | 32.6k | if (!std::isnan(dfDPI)) |
5085 | 4.28k | poDS->m_dfDPI = dfDPI; |
5086 | 32.6k | if (nBandsGuessed < 4) |
5087 | 32.6k | nBandsGuessed = 0; |
5088 | 32.6k | } |
5089 | | |
5090 | 32.6k | int nTargetBands = 3; |
5091 | | #ifdef HAVE_PDFIUM |
5092 | | // Use Alpha channel for PDFIUM as default format RGBA |
5093 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5094 | | nTargetBands = 4; |
5095 | | #endif |
5096 | 32.6k | if (nBandsGuessed) |
5097 | 6 | nTargetBands = nBandsGuessed; |
5098 | 32.6k | const char *pszPDFBands = |
5099 | 32.6k | GetOption(poOpenInfo->papszOpenOptions, "BANDS", nullptr); |
5100 | 32.6k | if (pszPDFBands) |
5101 | 18.8k | { |
5102 | 18.8k | nTargetBands = atoi(pszPDFBands); |
5103 | 18.8k | if (nTargetBands != 3 && nTargetBands != 4) |
5104 | 0 | { |
5105 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
5106 | 0 | "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback"); |
5107 | 0 | nTargetBands = 3; |
5108 | 0 | } |
5109 | 18.8k | } |
5110 | | #ifdef HAVE_PODOFO |
5111 | | if (bUseLib.test(PDFLIB_PODOFO) && nTargetBands == 4 && |
5112 | | poDS->m_aiTiles.empty()) |
5113 | | { |
5114 | | CPLError(CE_Warning, CPLE_NotSupported, |
5115 | | "GDAL_PDF_BANDS=4 not supported when PDF driver is compiled " |
5116 | | "against Podofo. " |
5117 | | "Using 3 as a fallback"); |
5118 | | nTargetBands = 3; |
5119 | | } |
5120 | | #endif |
5121 | | |
5122 | | // Create bands. We must do that before initializing PAM. But at that point |
5123 | | // we don't know yet the dataset dimension, since we need to know the DPI, |
5124 | | // that we can fully know only after loading PAM... So we will have to patch |
5125 | | // later the band dimension. |
5126 | 130k | for (int iBand = 1; iBand <= nTargetBands; iBand++) |
5127 | 98.0k | { |
5128 | 98.0k | if (poDS->m_poImageObj != nullptr) |
5129 | 0 | poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand)); |
5130 | 98.0k | else |
5131 | 98.0k | poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand, 0)); |
5132 | 98.0k | } |
5133 | | |
5134 | | /* -------------------------------------------------------------------- */ |
5135 | | /* Initialize any PAM information. */ |
5136 | | /* -------------------------------------------------------------------- */ |
5137 | 32.6k | if (bOpenSubdataset || bOpenSubdatasetImage) |
5138 | 0 | { |
5139 | 0 | poDS->SetPhysicalFilename(pszFilename); |
5140 | 0 | poDS->SetSubdatasetName(osSubdatasetName.c_str()); |
5141 | 0 | } |
5142 | 32.6k | else |
5143 | 32.6k | { |
5144 | 32.6k | poDS->SetDescription(poOpenInfo->pszFilename); |
5145 | 32.6k | } |
5146 | | |
5147 | 32.6k | poDS->TryLoadXML(); |
5148 | | |
5149 | | // Establish DPI |
5150 | 32.6k | const char *pszDPI = |
5151 | 32.6k | GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr); |
5152 | 32.6k | if (pszDPI == nullptr) |
5153 | 13.8k | pszDPI = poDS->GDALPamDataset::GetMetadataItem("DPI"); |
5154 | 32.6k | if (pszDPI != nullptr) |
5155 | 18.8k | { |
5156 | 18.8k | poDS->m_dfDPI = CPLAtof(pszDPI); |
5157 | | |
5158 | 18.8k | if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, |
5159 | 18.8k | "SAVE_DPI_TO_PAM", "FALSE"))) |
5160 | 0 | { |
5161 | 0 | const std::string osDPI(pszDPI); |
5162 | 0 | poDS->GDALPamDataset::SetMetadataItem("DPI", osDPI.c_str()); |
5163 | 0 | } |
5164 | 18.8k | } |
5165 | | |
5166 | 32.6k | if (poDS->m_dfDPI < 1e-2 || poDS->m_dfDPI > 7200) |
5167 | 22 | { |
5168 | 22 | CPLError(CE_Warning, CPLE_AppDefined, |
5169 | 22 | "Invalid value for GDAL_PDF_DPI. Using default value instead"); |
5170 | 22 | poDS->m_dfDPI = GDAL_DEFAULT_DPI; |
5171 | 22 | } |
5172 | 32.6k | poDS->SetMetadataItem("DPI", CPLSPrintf("%.16g", poDS->m_dfDPI)); |
5173 | | |
5174 | 32.6k | double dfX1 = 0.0; |
5175 | 32.6k | double dfY1 = 0.0; |
5176 | 32.6k | double dfX2 = 0.0; |
5177 | 32.6k | double dfY2 = 0.0; |
5178 | | |
5179 | 32.6k | #ifdef HAVE_POPPLER |
5180 | 32.6k | if (bUseLib.test(PDFLIB_POPPLER)) |
5181 | 32.6k | { |
5182 | 32.6k | const auto *psMediaBox = poPagePoppler->getMediaBox(); |
5183 | 32.6k | dfX1 = psMediaBox->x1; |
5184 | 32.6k | dfY1 = psMediaBox->y1; |
5185 | 32.6k | dfX2 = psMediaBox->x2; |
5186 | 32.6k | dfY2 = psMediaBox->y2; |
5187 | 32.6k | } |
5188 | 32.6k | #endif |
5189 | | |
5190 | | #ifdef HAVE_PODOFO |
5191 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5192 | | { |
5193 | | CPLAssert(poPagePodofo); |
5194 | | auto oMediaBox = poPagePodofo->GetMediaBox(); |
5195 | | dfX1 = oMediaBox.GetLeft(); |
5196 | | dfY1 = oMediaBox.GetBottom(); |
5197 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5198 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5199 | | dfX2 = dfX1 + oMediaBox.Width; |
5200 | | dfY2 = dfY1 + oMediaBox.Height; |
5201 | | #else |
5202 | | dfX2 = dfX1 + oMediaBox.GetWidth(); |
5203 | | dfY2 = dfY1 + oMediaBox.GetHeight(); |
5204 | | #endif |
5205 | | } |
5206 | | #endif |
5207 | | |
5208 | | #ifdef HAVE_PDFIUM |
5209 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5210 | | { |
5211 | | CPLAssert(poPagePdfium); |
5212 | | CFX_FloatRect rect = poPagePdfium->page->GetBBox(); |
5213 | | dfX1 = rect.left; |
5214 | | dfX2 = rect.right; |
5215 | | dfY1 = rect.bottom; |
5216 | | dfY2 = rect.top; |
5217 | | } |
5218 | | #endif // ~ HAVE_PDFIUM |
5219 | | |
5220 | 32.6k | double dfUserUnit = poDS->m_dfDPI * USER_UNIT_IN_INCH; |
5221 | 32.6k | poDS->m_dfPageWidth = dfX2 - dfX1; |
5222 | 32.6k | poDS->m_dfPageHeight = dfY2 - dfY1; |
5223 | | // CPLDebug("PDF", "left=%f right=%f bottom=%f top=%f", dfX1, dfX2, dfY1, |
5224 | | // dfY2); |
5225 | 32.6k | const double dfXSize = floor((dfX2 - dfX1) * dfUserUnit + 0.5); |
5226 | 32.6k | const double dfYSize = floor((dfY2 - dfY1) * dfUserUnit + 0.5); |
5227 | 32.6k | if (!(dfXSize >= 0 && dfXSize <= INT_MAX && dfYSize >= 0 && |
5228 | 32.6k | dfYSize <= INT_MAX)) |
5229 | 7 | { |
5230 | 7 | delete poDS; |
5231 | 7 | return nullptr; |
5232 | 7 | } |
5233 | 32.6k | poDS->nRasterXSize = static_cast<int>(dfXSize); |
5234 | 32.6k | poDS->nRasterYSize = static_cast<int>(dfYSize); |
5235 | | |
5236 | 32.6k | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) |
5237 | 20 | { |
5238 | 20 | delete poDS; |
5239 | 20 | return nullptr; |
5240 | 20 | } |
5241 | | |
5242 | 32.6k | double dfRotation = 0; |
5243 | 32.6k | #ifdef HAVE_POPPLER |
5244 | 32.6k | if (bUseLib.test(PDFLIB_POPPLER)) |
5245 | 32.6k | dfRotation = poDocPoppler->getPageRotate(iPage); |
5246 | 32.6k | #endif |
5247 | | |
5248 | | #ifdef HAVE_PODOFO |
5249 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5250 | | { |
5251 | | CPLAssert(poPagePodofo); |
5252 | | #if PODOFO_VERSION_MAJOR >= 1 |
5253 | | poPagePodofo->TryGetRotationRaw(dfRotation); |
5254 | | #elif (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5255 | | dfRotation = poPagePodofo->GetRotationRaw(); |
5256 | | #else |
5257 | | dfRotation = poPagePodofo->GetRotation(); |
5258 | | #endif |
5259 | | } |
5260 | | #endif |
5261 | | |
5262 | | #ifdef HAVE_PDFIUM |
5263 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5264 | | { |
5265 | | CPLAssert(poPagePdfium); |
5266 | | dfRotation = poPagePdfium->page->GetPageRotation() * 90; |
5267 | | } |
5268 | | #endif |
5269 | | |
5270 | 32.6k | if (dfRotation == 90 || dfRotation == -90 || dfRotation == 270) |
5271 | 173 | { |
5272 | | /* FIXME: the podofo case should be implemented. This needs to rotate */ |
5273 | | /* the output of pdftoppm */ |
5274 | 173 | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
5275 | 173 | if (bUseLib.test(PDFLIB_POPPLER) || bUseLib.test(PDFLIB_PDFIUM)) |
5276 | 173 | { |
5277 | 173 | int nTmp = poDS->nRasterXSize; |
5278 | 173 | poDS->nRasterXSize = poDS->nRasterYSize; |
5279 | 173 | poDS->nRasterYSize = nTmp; |
5280 | 173 | } |
5281 | 173 | #endif |
5282 | 173 | } |
5283 | | |
5284 | 32.6k | if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5285 | 18.7k | { |
5286 | 18.7k | poDS->m_nBlockXSize = 512; |
5287 | 18.7k | poDS->m_nBlockYSize = 512; |
5288 | 18.7k | } |
5289 | | /* Check if the PDF is only made of regularly tiled images */ |
5290 | | /* (like some USGS GeoPDF production) */ |
5291 | 13.8k | else if (dfRotation == 0.0 && !poDS->m_asTiles.empty() && |
5292 | 205 | EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"), |
5293 | 13.8k | "ALL")) |
5294 | 205 | { |
5295 | 205 | poDS->CheckTiledRaster(); |
5296 | 205 | if (!poDS->m_aiTiles.empty()) |
5297 | 0 | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
5298 | 205 | } |
5299 | | |
5300 | 32.6k | GDALPDFObject *poLGIDict = nullptr; |
5301 | 32.6k | GDALPDFObject *poVP = nullptr; |
5302 | 32.6k | int bIsOGCBP = FALSE; |
5303 | 32.6k | if ((poLGIDict = poPageDict->Get("LGIDict")) != nullptr && nImageNum < 0) |
5304 | 537 | { |
5305 | | /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */ |
5306 | 537 | CPLDebug("PDF", "OGC Encoding Best Practice style detected"); |
5307 | 537 | if (poDS->ParseLGIDictObject(poLGIDict)) |
5308 | 169 | { |
5309 | 169 | if (poDS->m_bHasCTM) |
5310 | 148 | { |
5311 | 148 | if (dfRotation == 90) |
5312 | 0 | { |
5313 | 0 | poDS->m_gt.xorig = poDS->m_adfCTM[4]; |
5314 | 0 | poDS->m_gt.xscale = poDS->m_adfCTM[2] / dfUserUnit; |
5315 | 0 | poDS->m_gt.xrot = poDS->m_adfCTM[0] / dfUserUnit; |
5316 | 0 | poDS->m_gt.yorig = poDS->m_adfCTM[5]; |
5317 | 0 | poDS->m_gt.yrot = poDS->m_adfCTM[3] / dfUserUnit; |
5318 | 0 | poDS->m_gt.yscale = poDS->m_adfCTM[1] / dfUserUnit; |
5319 | 0 | } |
5320 | 148 | else if (dfRotation == -90 || dfRotation == 270) |
5321 | 0 | { |
5322 | 0 | poDS->m_gt.xorig = |
5323 | 0 | poDS->m_adfCTM[4] + |
5324 | 0 | poDS->m_adfCTM[2] * poDS->m_dfPageHeight + |
5325 | 0 | poDS->m_adfCTM[0] * poDS->m_dfPageWidth; |
5326 | 0 | poDS->m_gt.xscale = -poDS->m_adfCTM[2] / dfUserUnit; |
5327 | 0 | poDS->m_gt.xrot = -poDS->m_adfCTM[0] / dfUserUnit; |
5328 | 0 | poDS->m_gt.yorig = |
5329 | 0 | poDS->m_adfCTM[5] + |
5330 | 0 | poDS->m_adfCTM[3] * poDS->m_dfPageHeight + |
5331 | 0 | poDS->m_adfCTM[1] * poDS->m_dfPageWidth; |
5332 | 0 | poDS->m_gt.yrot = -poDS->m_adfCTM[3] / dfUserUnit; |
5333 | 0 | poDS->m_gt.yscale = -poDS->m_adfCTM[1] / dfUserUnit; |
5334 | 0 | } |
5335 | 148 | else |
5336 | 148 | { |
5337 | 148 | poDS->m_gt.xorig = poDS->m_adfCTM[4] + |
5338 | 148 | poDS->m_adfCTM[2] * dfY2 + |
5339 | 148 | poDS->m_adfCTM[0] * dfX1; |
5340 | 148 | poDS->m_gt.xscale = poDS->m_adfCTM[0] / dfUserUnit; |
5341 | 148 | poDS->m_gt.xrot = -poDS->m_adfCTM[2] / dfUserUnit; |
5342 | 148 | poDS->m_gt.yorig = poDS->m_adfCTM[5] + |
5343 | 148 | poDS->m_adfCTM[3] * dfY2 + |
5344 | 148 | poDS->m_adfCTM[1] * dfX1; |
5345 | 148 | poDS->m_gt.yrot = poDS->m_adfCTM[1] / dfUserUnit; |
5346 | 148 | poDS->m_gt.yscale = -poDS->m_adfCTM[3] / dfUserUnit; |
5347 | 148 | } |
5348 | | |
5349 | 148 | poDS->m_bGeoTransformValid = true; |
5350 | 148 | } |
5351 | | |
5352 | 169 | bIsOGCBP = TRUE; |
5353 | | |
5354 | 169 | int i; |
5355 | 251 | for (i = 0; i < poDS->m_nGCPCount; i++) |
5356 | 82 | { |
5357 | 82 | if (dfRotation == 90) |
5358 | 0 | { |
5359 | 0 | double dfPixel = |
5360 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5361 | 0 | double dfLine = |
5362 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5363 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = dfLine; |
5364 | 0 | poDS->m_pasGCPList[i].dfGCPLine = dfPixel; |
5365 | 0 | } |
5366 | 82 | else if (dfRotation == -90 || dfRotation == 270) |
5367 | 0 | { |
5368 | 0 | double dfPixel = |
5369 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5370 | 0 | double dfLine = |
5371 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5372 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = |
5373 | 0 | poDS->nRasterXSize - dfLine; |
5374 | 0 | poDS->m_pasGCPList[i].dfGCPLine = |
5375 | 0 | poDS->nRasterYSize - dfPixel; |
5376 | 0 | } |
5377 | 82 | else |
5378 | 82 | { |
5379 | 82 | poDS->m_pasGCPList[i].dfGCPPixel = |
5380 | 82 | (-dfX1 + poDS->m_pasGCPList[i].dfGCPPixel) * dfUserUnit; |
5381 | 82 | poDS->m_pasGCPList[i].dfGCPLine = |
5382 | 82 | (dfY2 - poDS->m_pasGCPList[i].dfGCPLine) * dfUserUnit; |
5383 | 82 | } |
5384 | 82 | } |
5385 | 169 | } |
5386 | 537 | } |
5387 | 32.1k | else if ((poVP = poPageDict->Get("VP")) != nullptr && nImageNum < 0) |
5388 | 5.80k | { |
5389 | | /* Cf adobe_supplement_iso32000.pdf */ |
5390 | 5.80k | CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?"); |
5391 | 5.80k | if (dfX1 != 0 || dfY1 != 0) |
5392 | 112 | { |
5393 | 112 | CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case..."); |
5394 | 112 | } |
5395 | 5.80k | poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1); |
5396 | 5.80k | } |
5397 | 26.3k | else |
5398 | 26.3k | { |
5399 | 26.3k | GDALPDFObject *poXObject = |
5400 | 26.3k | poPageDict->LookupObject("Resources.XObject"); |
5401 | | |
5402 | 26.3k | if (poXObject != nullptr && |
5403 | 7.82k | poXObject->GetType() == PDFObjectType_Dictionary) |
5404 | 7.78k | { |
5405 | 7.78k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
5406 | 7.78k | const auto &oMap = poXObjectDict->GetValues(); |
5407 | 7.78k | int nSubDataset = 0; |
5408 | 7.78k | for (const auto &[osKey, poObj] : oMap) |
5409 | 52.2k | { |
5410 | 52.2k | if (poObj->GetType() == PDFObjectType_Dictionary) |
5411 | 48.5k | { |
5412 | 48.5k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
5413 | 48.5k | GDALPDFObject *poSubtype = nullptr; |
5414 | 48.5k | GDALPDFObject *poMeasure = nullptr; |
5415 | 48.5k | GDALPDFObject *poWidth = nullptr; |
5416 | 48.5k | GDALPDFObject *poHeight = nullptr; |
5417 | 48.5k | int nW = 0; |
5418 | 48.5k | int nH = 0; |
5419 | 48.5k | if ((poSubtype = poDict->Get("Subtype")) != nullptr && |
5420 | 47.3k | poSubtype->GetType() == PDFObjectType_Name && |
5421 | 47.3k | poSubtype->GetName() == "Image" && |
5422 | 11.5k | (poMeasure = poDict->Get("Measure")) != nullptr && |
5423 | 1 | poMeasure->GetType() == PDFObjectType_Dictionary && |
5424 | 0 | (poWidth = poDict->Get("Width")) != nullptr && |
5425 | 0 | poWidth->GetType() == PDFObjectType_Int && |
5426 | 0 | (nW = poWidth->GetInt()) > 0 && |
5427 | 0 | (poHeight = poDict->Get("Height")) != nullptr && |
5428 | 0 | poHeight->GetType() == PDFObjectType_Int && |
5429 | 0 | (nH = poHeight->GetInt()) > 0) |
5430 | 0 | { |
5431 | 0 | if (nImageNum < 0) |
5432 | 0 | CPLDebug("PDF", |
5433 | 0 | "Measure found on Image object (%d)", |
5434 | 0 | poObj->GetRefNum().toInt()); |
5435 | |
|
5436 | 0 | GDALPDFObject *poColorSpace = poDict->Get("ColorSpace"); |
5437 | 0 | GDALPDFObject *poBitsPerComponent = |
5438 | 0 | poDict->Get("BitsPerComponent"); |
5439 | 0 | if (poObj->GetRefNum().toBool() && |
5440 | 0 | poObj->GetRefGen() == 0 && |
5441 | 0 | poColorSpace != nullptr && |
5442 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
5443 | 0 | (poColorSpace->GetName() == "DeviceGray" || |
5444 | 0 | poColorSpace->GetName() == "DeviceRGB") && |
5445 | 0 | (poBitsPerComponent == nullptr || |
5446 | 0 | (poBitsPerComponent->GetType() == |
5447 | 0 | PDFObjectType_Int && |
5448 | 0 | poBitsPerComponent->GetInt() == 8))) |
5449 | 0 | { |
5450 | 0 | if (nImageNum < 0) |
5451 | 0 | { |
5452 | 0 | nSubDataset++; |
5453 | 0 | poDS->SetMetadataItem( |
5454 | 0 | CPLSPrintf("SUBDATASET_%d_NAME", |
5455 | 0 | nSubDataset), |
5456 | 0 | CPLSPrintf("PDF_IMAGE:%d:%d:%s", iPage, |
5457 | 0 | poObj->GetRefNum().toInt(), |
5458 | 0 | pszFilename), |
5459 | 0 | "SUBDATASETS"); |
5460 | 0 | poDS->SetMetadataItem( |
5461 | 0 | CPLSPrintf("SUBDATASET_%d_DESC", |
5462 | 0 | nSubDataset), |
5463 | 0 | CPLSPrintf("Georeferenced image of size " |
5464 | 0 | "%dx%d of page %d of %s", |
5465 | 0 | nW, nH, iPage, pszFilename), |
5466 | 0 | "SUBDATASETS"); |
5467 | 0 | } |
5468 | 0 | else if (poObj->GetRefNum().toInt() == nImageNum) |
5469 | 0 | { |
5470 | 0 | poDS->nRasterXSize = nW; |
5471 | 0 | poDS->nRasterYSize = nH; |
5472 | 0 | poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW, |
5473 | 0 | 0); |
5474 | 0 | poDS->m_poImageObj = poObj; |
5475 | 0 | if (poColorSpace->GetName() == "DeviceGray") |
5476 | 0 | { |
5477 | 0 | for (int i = 1; i < poDS->nBands; ++i) |
5478 | 0 | delete poDS->papoBands[i]; |
5479 | 0 | poDS->nBands = 1; |
5480 | 0 | } |
5481 | 0 | break; |
5482 | 0 | } |
5483 | 0 | } |
5484 | 0 | } |
5485 | 48.5k | } |
5486 | 52.2k | } |
5487 | 7.78k | } |
5488 | | |
5489 | 26.3k | if (nImageNum >= 0 && poDS->m_poImageObj == nullptr) |
5490 | 0 | { |
5491 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d", |
5492 | 0 | nImageNum); |
5493 | 0 | delete poDS; |
5494 | 0 | return nullptr; |
5495 | 0 | } |
5496 | | |
5497 | | /* Not a geospatial PDF doc */ |
5498 | 26.3k | } |
5499 | | |
5500 | | /* If pixel size or top left coordinates are very close to an int, round |
5501 | | * them to the int */ |
5502 | 32.6k | double dfEps = |
5503 | 32.6k | (fabs(poDS->m_gt.xorig) > 1e5 && fabs(poDS->m_gt.yorig) > 1e5) ? 1e-5 |
5504 | 32.6k | : 1e-8; |
5505 | 32.6k | poDS->m_gt.xorig = ROUND_IF_CLOSE(poDS->m_gt.xorig, dfEps); |
5506 | 32.6k | poDS->m_gt.xscale = ROUND_IF_CLOSE(poDS->m_gt.xscale); |
5507 | 32.6k | poDS->m_gt.yorig = ROUND_IF_CLOSE(poDS->m_gt.yorig, dfEps); |
5508 | 32.6k | poDS->m_gt.yscale = ROUND_IF_CLOSE(poDS->m_gt.yscale); |
5509 | | |
5510 | 32.6k | if (bUseLib.test(PDFLIB_PDFIUM)) |
5511 | 0 | { |
5512 | | // Attempt to "fix" the loss of precision due to the use of float32 for |
5513 | | // numbers by pdfium |
5514 | 0 | if ((fabs(poDS->m_gt.xorig) > 1e5 || fabs(poDS->m_gt.yorig) > 1e5) && |
5515 | 0 | fabs(poDS->m_gt.xorig - std::round(poDS->m_gt.xorig)) < |
5516 | 0 | 1e-6 * fabs(poDS->m_gt.xorig) && |
5517 | 0 | fabs(poDS->m_gt.xscale - std::round(poDS->m_gt.xscale)) < |
5518 | 0 | 1e-3 * fabs(poDS->m_gt.xscale) && |
5519 | 0 | fabs(poDS->m_gt.yorig - std::round(poDS->m_gt.yorig)) < |
5520 | 0 | 1e-6 * fabs(poDS->m_gt.yorig) && |
5521 | 0 | fabs(poDS->m_gt.yscale - std::round(poDS->m_gt.yscale)) < |
5522 | 0 | 1e-3 * fabs(poDS->m_gt.yscale)) |
5523 | 0 | { |
5524 | 0 | for (int i = 0; i < 6; i++) |
5525 | 0 | { |
5526 | 0 | poDS->m_gt[i] = std::round(poDS->m_gt[i]); |
5527 | 0 | } |
5528 | 0 | } |
5529 | 0 | } |
5530 | | |
5531 | 32.6k | if (poDS->m_poNeatLine) |
5532 | 2.53k | { |
5533 | 2.53k | char *pszNeatLineWkt = nullptr; |
5534 | 2.53k | OGRLinearRing *poRing = poDS->m_poNeatLine->getExteriorRing(); |
5535 | | /* Adobe style is already in target SRS units */ |
5536 | 2.53k | if (bIsOGCBP) |
5537 | 168 | { |
5538 | 168 | int nPoints = poRing->getNumPoints(); |
5539 | 168 | int i; |
5540 | | |
5541 | 76.8k | for (i = 0; i < nPoints; i++) |
5542 | 76.7k | { |
5543 | 76.7k | double x, y; |
5544 | 76.7k | if (dfRotation == 90.0) |
5545 | 0 | { |
5546 | 0 | x = poRing->getY(i) * dfUserUnit; |
5547 | 0 | y = poRing->getX(i) * dfUserUnit; |
5548 | 0 | } |
5549 | 76.7k | else if (dfRotation == -90.0 || dfRotation == 270.0) |
5550 | 0 | { |
5551 | 0 | x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit; |
5552 | 0 | y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit; |
5553 | 0 | } |
5554 | 76.7k | else |
5555 | 76.7k | { |
5556 | 76.7k | x = (-dfX1 + poRing->getX(i)) * dfUserUnit; |
5557 | 76.7k | y = (dfY2 - poRing->getY(i)) * dfUserUnit; |
5558 | 76.7k | } |
5559 | 76.7k | double X = poDS->m_gt.xorig + x * poDS->m_gt.xscale + |
5560 | 76.7k | y * poDS->m_gt.xrot; |
5561 | 76.7k | double Y = poDS->m_gt.yorig + x * poDS->m_gt.yrot + |
5562 | 76.7k | y * poDS->m_gt.yscale; |
5563 | 76.7k | poRing->setPoint(i, X, Y); |
5564 | 76.7k | } |
5565 | 168 | } |
5566 | 2.53k | poRing->closeRings(); |
5567 | | |
5568 | 2.53k | poDS->m_poNeatLine->exportToWkt(&pszNeatLineWkt); |
5569 | 2.53k | if (nImageNum < 0) |
5570 | 2.53k | poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt); |
5571 | 2.53k | CPLFree(pszNeatLineWkt); |
5572 | 2.53k | } |
5573 | | |
5574 | 32.6k | poDS->MapOCGsToPages(); |
5575 | | |
5576 | 32.6k | #ifdef HAVE_POPPLER |
5577 | 32.6k | if (bUseLib.test(PDFLIB_POPPLER)) |
5578 | 32.6k | { |
5579 | 32.6k | auto poMetadata = poCatalogPoppler->readMetadata(); |
5580 | 32.6k | if (poMetadata) |
5581 | 5.42k | { |
5582 | 5.42k | const char *pszContent = poMetadata->c_str(); |
5583 | 5.42k | if (pszContent != nullptr && |
5584 | 5.42k | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5585 | 5.03k | { |
5586 | 5.03k | const char *const apszMDList[2] = {pszContent, nullptr}; |
5587 | 5.03k | poDS->SetMetadata(const_cast<char **>(apszMDList), "xml:XMP"); |
5588 | 5.03k | } |
5589 | | #if (POPPLER_MAJOR_VERSION < 21 || \ |
5590 | | (POPPLER_MAJOR_VERSION == 21 && POPPLER_MINOR_VERSION < 10)) |
5591 | | delete poMetadata; |
5592 | | #endif |
5593 | 5.42k | } |
5594 | | |
5595 | | /* Read Info object */ |
5596 | | /* The test is necessary since with some corrupted PDFs |
5597 | | * poDocPoppler->getDocInfo() */ |
5598 | | /* might abort() */ |
5599 | 32.6k | if (poDocPoppler->getXRef()->isOk()) |
5600 | 32.6k | { |
5601 | 32.6k | Object oInfo = poDocPoppler->getDocInfo(); |
5602 | 32.6k | GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE); |
5603 | 32.6k | poDS->ParseInfo(&oInfoObjPoppler); |
5604 | 32.6k | } |
5605 | | |
5606 | | /* Find layers */ |
5607 | 32.6k | poDS->FindLayersPoppler( |
5608 | 32.6k | (bOpenSubdataset || bOpenSubdatasetImage) ? iPage : 0); |
5609 | | |
5610 | | /* Turn user specified layers on or off */ |
5611 | 32.6k | poDS->TurnLayersOnOffPoppler(); |
5612 | 32.6k | } |
5613 | 32.6k | #endif |
5614 | | |
5615 | | #ifdef HAVE_PODOFO |
5616 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5617 | | { |
5618 | | for (const auto &obj : poDS->m_poDocPodofo->GetObjects()) |
5619 | | { |
5620 | | GDALPDFObjectPodofo oObjPodofo(obj, |
5621 | | poDS->m_poDocPodofo->GetObjects()); |
5622 | | poDS->FindXMP(&oObjPodofo); |
5623 | | } |
5624 | | |
5625 | | /* Find layers */ |
5626 | | poDS->FindLayersGeneric(poPageDict); |
5627 | | |
5628 | | /* Read Info object */ |
5629 | | const PoDoFo::PdfInfo *poInfo = poDS->m_poDocPodofo->GetInfo(); |
5630 | | if (poInfo != nullptr) |
5631 | | { |
5632 | | GDALPDFObjectPodofo oInfoObjPodofo( |
5633 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5634 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5635 | | &(poInfo->GetObject()), |
5636 | | #else |
5637 | | poInfo->GetObject(), |
5638 | | #endif |
5639 | | poDS->m_poDocPodofo->GetObjects()); |
5640 | | poDS->ParseInfo(&oInfoObjPodofo); |
5641 | | } |
5642 | | } |
5643 | | #endif |
5644 | | #ifdef HAVE_PDFIUM |
5645 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5646 | | { |
5647 | | // coverity is confused by WrapRetain(), believing that multiple |
5648 | | // smart pointers manage the same raw pointer. Which is actually |
5649 | | // true, but a RetainPtr holds a reference counted object. It is |
5650 | | // thus safe to have several RetainPtr holding it. |
5651 | | // coverity[multiple_init_smart_ptr] |
5652 | | GDALPDFObjectPdfium *poRoot = GDALPDFObjectPdfium::Build( |
5653 | | pdfium::WrapRetain(poDocPdfium->doc->GetRoot())); |
5654 | | if (poRoot->GetType() == PDFObjectType_Dictionary) |
5655 | | { |
5656 | | GDALPDFDictionary *poDict = poRoot->GetDictionary(); |
5657 | | GDALPDFObject *poMetadata(poDict->Get("Metadata")); |
5658 | | if (poMetadata != nullptr) |
5659 | | { |
5660 | | GDALPDFStream *poStream = poMetadata->GetStream(); |
5661 | | if (poStream != nullptr) |
5662 | | { |
5663 | | char *pszContent = poStream->GetBytes(); |
5664 | | const auto nLength = poStream->GetLength(); |
5665 | | if (pszContent != nullptr && nLength > 15 && |
5666 | | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5667 | | { |
5668 | | char *apszMDList[2]; |
5669 | | apszMDList[0] = pszContent; |
5670 | | apszMDList[1] = nullptr; |
5671 | | poDS->SetMetadata(apszMDList, "xml:XMP"); |
5672 | | } |
5673 | | CPLFree(pszContent); |
5674 | | } |
5675 | | } |
5676 | | } |
5677 | | delete poRoot; |
5678 | | |
5679 | | /* Find layers */ |
5680 | | poDS->FindLayersPdfium((bOpenSubdataset || bOpenSubdatasetImage) ? iPage |
5681 | | : 0); |
5682 | | |
5683 | | /* Turn user specified layers on or off */ |
5684 | | poDS->TurnLayersOnOffPdfium(); |
5685 | | |
5686 | | GDALPDFObjectPdfium *poInfo = |
5687 | | GDALPDFObjectPdfium::Build(poDocPdfium->doc->GetInfo()); |
5688 | | if (poInfo) |
5689 | | { |
5690 | | /* Read Info object */ |
5691 | | poDS->ParseInfo(poInfo); |
5692 | | delete poInfo; |
5693 | | } |
5694 | | } |
5695 | | #endif // ~ HAVE_PDFIUM |
5696 | | |
5697 | | // Patch band size with actual dataset size |
5698 | 130k | for (int iBand = 1; iBand <= poDS->nBands; iBand++) |
5699 | 98.0k | { |
5700 | 98.0k | cpl::down_cast<PDFRasterBand *>(poDS->GetRasterBand(iBand)) |
5701 | 98.0k | ->SetSize(poDS->nRasterXSize, poDS->nRasterYSize); |
5702 | 98.0k | } |
5703 | | |
5704 | | /* Check if this is a raster-only PDF file and that we are */ |
5705 | | /* opened in vector-only mode */ |
5706 | 32.6k | if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && |
5707 | 24.4k | (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && |
5708 | 5.63k | !poDS->OpenVectorLayers(poPageDict)) |
5709 | 4.48k | { |
5710 | 4.48k | CPLDebug("PDF", "This is a raster-only PDF dataset, " |
5711 | 4.48k | "but it has been opened in vector-only mode"); |
5712 | | /* Clear dirty flag */ |
5713 | 4.48k | poDS->m_bProjDirty = false; |
5714 | 4.48k | poDS->m_bNeatLineDirty = false; |
5715 | 4.48k | poDS->m_bInfoDirty = false; |
5716 | 4.48k | poDS->m_bXMPDirty = false; |
5717 | 4.48k | delete poDS; |
5718 | 4.48k | return nullptr; |
5719 | 4.48k | } |
5720 | | |
5721 | | /* -------------------------------------------------------------------- */ |
5722 | | /* Support overviews. */ |
5723 | | /* -------------------------------------------------------------------- */ |
5724 | 28.1k | if (!CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5725 | 9.40k | { |
5726 | 9.40k | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
5727 | 9.40k | } |
5728 | | |
5729 | | /* Clear dirty flag */ |
5730 | 28.1k | poDS->m_bProjDirty = false; |
5731 | 28.1k | poDS->m_bNeatLineDirty = false; |
5732 | 28.1k | poDS->m_bInfoDirty = false; |
5733 | 28.1k | poDS->m_bXMPDirty = false; |
5734 | | |
5735 | 28.1k | return (poDS); |
5736 | 32.6k | } |
5737 | | |
5738 | | /************************************************************************/ |
5739 | | /* ParseLGIDictObject() */ |
5740 | | /************************************************************************/ |
5741 | | |
5742 | | int PDFDataset::ParseLGIDictObject(GDALPDFObject *poLGIDict) |
5743 | 537 | { |
5744 | 537 | bool bOK = false; |
5745 | 537 | if (poLGIDict->GetType() == PDFObjectType_Array) |
5746 | 33 | { |
5747 | 33 | GDALPDFArray *poArray = poLGIDict->GetArray(); |
5748 | 33 | int nArrayLength = poArray->GetLength(); |
5749 | 33 | int iMax = -1; |
5750 | 33 | GDALPDFObject *poArrayElt = nullptr; |
5751 | 33 | for (int i = 0; i < nArrayLength; i++) |
5752 | 33 | { |
5753 | 33 | if ((poArrayElt = poArray->Get(i)) == nullptr || |
5754 | 33 | poArrayElt->GetType() != PDFObjectType_Dictionary) |
5755 | 33 | { |
5756 | 33 | CPLError(CE_Failure, CPLE_AppDefined, |
5757 | 33 | "LGIDict[%d] is not a dictionary", i); |
5758 | 33 | return FALSE; |
5759 | 33 | } |
5760 | | |
5761 | 0 | int bIsBestCandidate = FALSE; |
5762 | 0 | if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), |
5763 | 0 | &bIsBestCandidate)) |
5764 | 0 | { |
5765 | 0 | if (bIsBestCandidate || iMax < 0) |
5766 | 0 | iMax = i; |
5767 | 0 | } |
5768 | 0 | } |
5769 | | |
5770 | 0 | if (iMax < 0) |
5771 | 0 | return FALSE; |
5772 | | |
5773 | 0 | poArrayElt = poArray->Get(iMax); |
5774 | 0 | bOK = CPL_TO_BOOL( |
5775 | 0 | ParseLGIDictDictSecondPass(poArrayElt->GetDictionary())); |
5776 | 0 | } |
5777 | 504 | else if (poLGIDict->GetType() == PDFObjectType_Dictionary) |
5778 | 468 | { |
5779 | 468 | bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) && |
5780 | 307 | ParseLGIDictDictSecondPass(poLGIDict->GetDictionary()); |
5781 | 468 | } |
5782 | 36 | else |
5783 | 36 | { |
5784 | 36 | CPLError(CE_Failure, CPLE_AppDefined, "LGIDict is of type %s", |
5785 | 36 | poLGIDict->GetTypeName()); |
5786 | 36 | } |
5787 | | |
5788 | 504 | return bOK; |
5789 | 537 | } |
5790 | | |
5791 | | /************************************************************************/ |
5792 | | /* Get() */ |
5793 | | /************************************************************************/ |
5794 | | |
5795 | | static double Get(GDALPDFObject *poObj, int nIndice) |
5796 | 1.25M | { |
5797 | 1.25M | if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0) |
5798 | 626k | { |
5799 | 626k | poObj = poObj->GetArray()->Get(nIndice); |
5800 | 626k | if (poObj == nullptr) |
5801 | 262 | return 0; |
5802 | 626k | return Get(poObj); |
5803 | 626k | } |
5804 | 631k | else if (poObj->GetType() == PDFObjectType_Int) |
5805 | 191k | return poObj->GetInt(); |
5806 | 440k | else if (poObj->GetType() == PDFObjectType_Real) |
5807 | 123k | return poObj->GetReal(); |
5808 | 316k | else if (poObj->GetType() == PDFObjectType_String) |
5809 | 8.94k | { |
5810 | 8.94k | const char *pszStr = poObj->GetString().c_str(); |
5811 | 8.94k | size_t nLen = strlen(pszStr); |
5812 | 8.94k | if (nLen == 0) |
5813 | 5.26k | return 0; |
5814 | | /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" |
5815 | | */ |
5816 | 3.67k | char chLast = pszStr[nLen - 1]; |
5817 | 3.67k | if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S') |
5818 | 392 | { |
5819 | 392 | double dfDeg = CPLAtof(pszStr); |
5820 | 392 | double dfMin = 0.0; |
5821 | 392 | double dfSec = 0.0; |
5822 | 392 | const char *pszNext = strchr(pszStr, ' '); |
5823 | 392 | if (pszNext) |
5824 | 118 | pszNext++; |
5825 | 392 | if (pszNext) |
5826 | 118 | dfMin = CPLAtof(pszNext); |
5827 | 392 | if (pszNext) |
5828 | 118 | pszNext = strchr(pszNext, ' '); |
5829 | 392 | if (pszNext) |
5830 | 118 | pszNext++; |
5831 | 392 | if (pszNext) |
5832 | 118 | dfSec = CPLAtof(pszNext); |
5833 | 392 | double dfVal = dfDeg + dfMin / 60 + dfSec / 3600; |
5834 | 392 | if (chLast == 'W' || chLast == 'S') |
5835 | 0 | return -dfVal; |
5836 | 392 | else |
5837 | 392 | return dfVal; |
5838 | 392 | } |
5839 | 3.28k | return CPLAtof(pszStr); |
5840 | 3.67k | } |
5841 | 307k | else |
5842 | 307k | { |
5843 | 307k | CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s", |
5844 | 307k | poObj->GetTypeName()); |
5845 | 307k | return 0; |
5846 | 307k | } |
5847 | 1.25M | } |
5848 | | |
5849 | | /************************************************************************/ |
5850 | | /* Get() */ |
5851 | | /************************************************************************/ |
5852 | | |
5853 | | static double Get(GDALPDFDictionary *poDict, const char *pszName) |
5854 | 0 | { |
5855 | 0 | GDALPDFObject *poObj = poDict->Get(pszName); |
5856 | 0 | if (poObj != nullptr) |
5857 | 0 | return Get(poObj); |
5858 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find parameter %s", pszName); |
5859 | 0 | return 0; |
5860 | 0 | } |
5861 | | |
5862 | | /************************************************************************/ |
5863 | | /* ParseLGIDictDictFirstPass() */ |
5864 | | /************************************************************************/ |
5865 | | |
5866 | | int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary *poLGIDict, |
5867 | | int *pbIsBestCandidate) |
5868 | 468 | { |
5869 | 468 | if (pbIsBestCandidate) |
5870 | 0 | *pbIsBestCandidate = FALSE; |
5871 | | |
5872 | 468 | if (poLGIDict == nullptr) |
5873 | 0 | return FALSE; |
5874 | | |
5875 | | /* -------------------------------------------------------------------- */ |
5876 | | /* Extract Type attribute */ |
5877 | | /* -------------------------------------------------------------------- */ |
5878 | 468 | GDALPDFObject *poType = poLGIDict->Get("Type"); |
5879 | 468 | if (poType == nullptr) |
5880 | 43 | { |
5881 | 43 | CPLError(CE_Failure, CPLE_AppDefined, |
5882 | 43 | "Cannot find Type of LGIDict object"); |
5883 | 43 | return FALSE; |
5884 | 43 | } |
5885 | | |
5886 | 425 | if (poType->GetType() != PDFObjectType_Name) |
5887 | 0 | { |
5888 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5889 | 0 | "Invalid type for Type of LGIDict object"); |
5890 | 0 | return FALSE; |
5891 | 0 | } |
5892 | | |
5893 | 425 | if (strcmp(poType->GetName().c_str(), "LGIDict") != 0) |
5894 | 80 | { |
5895 | 80 | CPLError(CE_Failure, CPLE_AppDefined, |
5896 | 80 | "Invalid value for Type of LGIDict object : %s", |
5897 | 80 | poType->GetName().c_str()); |
5898 | 80 | return FALSE; |
5899 | 80 | } |
5900 | | |
5901 | | /* -------------------------------------------------------------------- */ |
5902 | | /* Extract Version attribute */ |
5903 | | /* -------------------------------------------------------------------- */ |
5904 | 345 | GDALPDFObject *poVersion = poLGIDict->Get("Version"); |
5905 | 345 | if (poVersion == nullptr) |
5906 | 0 | { |
5907 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5908 | 0 | "Cannot find Version of LGIDict object"); |
5909 | 0 | return FALSE; |
5910 | 0 | } |
5911 | | |
5912 | 345 | if (poVersion->GetType() == PDFObjectType_String) |
5913 | 290 | { |
5914 | | /* OGC best practice is 2.1 */ |
5915 | 290 | CPLDebug("PDF", "LGIDict Version : %s", poVersion->GetString().c_str()); |
5916 | 290 | } |
5917 | 55 | else if (poVersion->GetType() == PDFObjectType_Int) |
5918 | 1 | { |
5919 | | /* Old TerraGo is 2 */ |
5920 | 1 | CPLDebug("PDF", "LGIDict Version : %d", poVersion->GetInt()); |
5921 | 1 | } |
5922 | | |
5923 | | /* USGS PDF maps have several LGIDict. Keep the one whose description */ |
5924 | | /* is "Map Layers" by default */ |
5925 | 345 | const char *pszNeatlineToSelect = |
5926 | 345 | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
5927 | | |
5928 | | /* -------------------------------------------------------------------- */ |
5929 | | /* Extract Neatline attribute */ |
5930 | | /* -------------------------------------------------------------------- */ |
5931 | 345 | GDALPDFObject *poNeatline = poLGIDict->Get("Neatline"); |
5932 | 345 | if (poNeatline != nullptr && poNeatline->GetType() == PDFObjectType_Array) |
5933 | 338 | { |
5934 | 338 | int nLength = poNeatline->GetArray()->GetLength(); |
5935 | 338 | if ((nLength % 2) != 0 || nLength < 4) |
5936 | 38 | { |
5937 | 38 | CPLError(CE_Failure, CPLE_AppDefined, |
5938 | 38 | "Invalid length for Neatline"); |
5939 | 38 | return FALSE; |
5940 | 38 | } |
5941 | | |
5942 | 300 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
5943 | 300 | bool bIsAskedNeatline = false; |
5944 | 300 | if (poDescription != nullptr && |
5945 | 32 | poDescription->GetType() == PDFObjectType_String) |
5946 | 32 | { |
5947 | 32 | CPLDebug("PDF", "Description = %s", |
5948 | 32 | poDescription->GetString().c_str()); |
5949 | | |
5950 | 32 | if (EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect)) |
5951 | 0 | { |
5952 | 0 | m_dfMaxArea = 1e300; |
5953 | 0 | bIsAskedNeatline = true; |
5954 | 0 | } |
5955 | 32 | } |
5956 | | |
5957 | 300 | if (!bIsAskedNeatline) |
5958 | 300 | { |
5959 | 300 | double dfMinX = 0.0; |
5960 | 300 | double dfMinY = 0.0; |
5961 | 300 | double dfMaxX = 0.0; |
5962 | 300 | double dfMaxY = 0.0; |
5963 | 130k | for (int i = 0; i < nLength; i += 2) |
5964 | 130k | { |
5965 | 130k | double dfX = Get(poNeatline, i); |
5966 | 130k | double dfY = Get(poNeatline, i + 1); |
5967 | 130k | if (i == 0 || dfX < dfMinX) |
5968 | 765 | dfMinX = dfX; |
5969 | 130k | if (i == 0 || dfY < dfMinY) |
5970 | 1.22k | dfMinY = dfY; |
5971 | 130k | if (i == 0 || dfX > dfMaxX) |
5972 | 1.31k | dfMaxX = dfX; |
5973 | 130k | if (i == 0 || dfY > dfMaxY) |
5974 | 910 | dfMaxY = dfY; |
5975 | 130k | } |
5976 | 300 | double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY); |
5977 | 300 | if (dfArea < m_dfMaxArea) |
5978 | 0 | { |
5979 | 0 | CPLDebug("PDF", "Not the largest neatline. Skipping it"); |
5980 | 0 | return TRUE; |
5981 | 0 | } |
5982 | | |
5983 | 300 | CPLDebug("PDF", "This is the largest neatline for now"); |
5984 | 300 | m_dfMaxArea = dfArea; |
5985 | 300 | } |
5986 | 0 | else |
5987 | 0 | CPLDebug("PDF", "The \"%s\" registration will be selected", |
5988 | 0 | pszNeatlineToSelect); |
5989 | | |
5990 | 300 | if (pbIsBestCandidate) |
5991 | 0 | *pbIsBestCandidate = TRUE; |
5992 | | |
5993 | 300 | delete m_poNeatLine; |
5994 | 300 | m_poNeatLine = new OGRPolygon(); |
5995 | 300 | OGRLinearRing *poRing = new OGRLinearRing(); |
5996 | 300 | if (nLength == 4) |
5997 | 3 | { |
5998 | | /* 2 points only ? They are the bounding box */ |
5999 | 3 | double dfX1 = Get(poNeatline, 0); |
6000 | 3 | double dfY1 = Get(poNeatline, 1); |
6001 | 3 | double dfX2 = Get(poNeatline, 2); |
6002 | 3 | double dfY2 = Get(poNeatline, 3); |
6003 | 3 | poRing->addPoint(dfX1, dfY1); |
6004 | 3 | poRing->addPoint(dfX2, dfY1); |
6005 | 3 | poRing->addPoint(dfX2, dfY2); |
6006 | 3 | poRing->addPoint(dfX1, dfY2); |
6007 | 3 | } |
6008 | 297 | else |
6009 | 297 | { |
6010 | 130k | for (int i = 0; i < nLength; i += 2) |
6011 | 130k | { |
6012 | 130k | double dfX = Get(poNeatline, i); |
6013 | 130k | double dfY = Get(poNeatline, i + 1); |
6014 | 130k | poRing->addPoint(dfX, dfY); |
6015 | 130k | } |
6016 | 297 | } |
6017 | 300 | poRing->closeRings(); |
6018 | 300 | m_poNeatLine->addRingDirectly(poRing); |
6019 | 300 | } |
6020 | | |
6021 | 307 | return TRUE; |
6022 | 345 | } |
6023 | | |
6024 | | /************************************************************************/ |
6025 | | /* ParseLGIDictDictSecondPass() */ |
6026 | | /************************************************************************/ |
6027 | | |
6028 | | int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary *poLGIDict) |
6029 | 307 | { |
6030 | 307 | int i; |
6031 | | |
6032 | | /* -------------------------------------------------------------------- */ |
6033 | | /* Extract Description attribute */ |
6034 | | /* -------------------------------------------------------------------- */ |
6035 | 307 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
6036 | 307 | if (poDescription != nullptr && |
6037 | 36 | poDescription->GetType() == PDFObjectType_String) |
6038 | 36 | { |
6039 | 36 | CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str()); |
6040 | 36 | } |
6041 | | |
6042 | | /* -------------------------------------------------------------------- */ |
6043 | | /* Extract CTM attribute */ |
6044 | | /* -------------------------------------------------------------------- */ |
6045 | 307 | GDALPDFObject *poCTM = poLGIDict->Get("CTM"); |
6046 | 307 | m_bHasCTM = false; |
6047 | 307 | if (poCTM != nullptr && poCTM->GetType() == PDFObjectType_Array && |
6048 | 271 | CPLTestBool(CPLGetConfigOption("PDF_USE_CTM", "YES"))) |
6049 | 271 | { |
6050 | 271 | int nLength = poCTM->GetArray()->GetLength(); |
6051 | 271 | if (nLength != 6) |
6052 | 56 | { |
6053 | 56 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for CTM"); |
6054 | 56 | return FALSE; |
6055 | 56 | } |
6056 | | |
6057 | 215 | m_bHasCTM = true; |
6058 | 1.50k | for (i = 0; i < nLength; i++) |
6059 | 1.29k | { |
6060 | 1.29k | m_adfCTM[i] = Get(poCTM, i); |
6061 | | /* Nullify rotation terms that are significantly smaller than */ |
6062 | | /* scaling terms. */ |
6063 | 1.29k | if ((i == 1 || i == 2) && |
6064 | 430 | fabs(m_adfCTM[i]) < fabs(m_adfCTM[0]) * 1e-10) |
6065 | 125 | m_adfCTM[i] = 0; |
6066 | 1.29k | CPLDebug("PDF", "CTM[%d] = %.16g", i, m_adfCTM[i]); |
6067 | 1.29k | } |
6068 | 215 | } |
6069 | | |
6070 | | /* -------------------------------------------------------------------- */ |
6071 | | /* Extract Registration attribute */ |
6072 | | /* -------------------------------------------------------------------- */ |
6073 | 251 | GDALPDFObject *poRegistration = poLGIDict->Get("Registration"); |
6074 | 251 | if (poRegistration != nullptr && |
6075 | 23 | poRegistration->GetType() == PDFObjectType_Array) |
6076 | 23 | { |
6077 | 23 | GDALPDFArray *poRegistrationArray = poRegistration->GetArray(); |
6078 | 23 | int nLength = poRegistrationArray->GetLength(); |
6079 | 23 | if (nLength > 4 || (!m_bHasCTM && nLength >= 2) || |
6080 | 0 | CPLTestBool(CPLGetConfigOption("PDF_REPORT_GCPS", "NO"))) |
6081 | 23 | { |
6082 | 23 | m_nGCPCount = 0; |
6083 | 23 | m_pasGCPList = |
6084 | 23 | static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nLength)); |
6085 | | |
6086 | 116 | for (i = 0; i < nLength; i++) |
6087 | 93 | { |
6088 | 93 | GDALPDFObject *poGCP = poRegistrationArray->Get(i); |
6089 | 93 | if (poGCP != nullptr && |
6090 | 93 | poGCP->GetType() == PDFObjectType_Array && |
6091 | 90 | poGCP->GetArray()->GetLength() == 4) |
6092 | 90 | { |
6093 | 90 | double dfUserX = Get(poGCP, 0); |
6094 | 90 | double dfUserY = Get(poGCP, 1); |
6095 | 90 | double dfX = Get(poGCP, 2); |
6096 | 90 | double dfY = Get(poGCP, 3); |
6097 | 90 | CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX); |
6098 | 90 | CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY); |
6099 | 90 | CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX); |
6100 | 90 | CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY); |
6101 | | |
6102 | 90 | char szID[32]; |
6103 | 90 | snprintf(szID, sizeof(szID), "%d", m_nGCPCount + 1); |
6104 | 90 | m_pasGCPList[m_nGCPCount].pszId = CPLStrdup(szID); |
6105 | 90 | m_pasGCPList[m_nGCPCount].pszInfo = CPLStrdup(""); |
6106 | 90 | m_pasGCPList[m_nGCPCount].dfGCPPixel = dfUserX; |
6107 | 90 | m_pasGCPList[m_nGCPCount].dfGCPLine = dfUserY; |
6108 | 90 | m_pasGCPList[m_nGCPCount].dfGCPX = dfX; |
6109 | 90 | m_pasGCPList[m_nGCPCount].dfGCPY = dfY; |
6110 | 90 | m_nGCPCount++; |
6111 | 90 | } |
6112 | 93 | } |
6113 | | |
6114 | 23 | if (m_nGCPCount == 0) |
6115 | 0 | { |
6116 | 0 | CPLFree(m_pasGCPList); |
6117 | 0 | m_pasGCPList = nullptr; |
6118 | 0 | } |
6119 | 23 | } |
6120 | 23 | } |
6121 | | |
6122 | 251 | if (!m_bHasCTM && m_nGCPCount == 0) |
6123 | 13 | { |
6124 | 13 | CPLDebug("PDF", "Neither CTM nor Registration found"); |
6125 | 13 | return FALSE; |
6126 | 13 | } |
6127 | | |
6128 | | /* -------------------------------------------------------------------- */ |
6129 | | /* Extract Projection attribute */ |
6130 | | /* -------------------------------------------------------------------- */ |
6131 | 238 | GDALPDFObject *poProjection = poLGIDict->Get("Projection"); |
6132 | 238 | if (poProjection == nullptr || |
6133 | 219 | poProjection->GetType() != PDFObjectType_Dictionary) |
6134 | 27 | { |
6135 | 27 | CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection"); |
6136 | 27 | return FALSE; |
6137 | 27 | } |
6138 | | |
6139 | 211 | return ParseProjDict(poProjection->GetDictionary()); |
6140 | 238 | } |
6141 | | |
6142 | | /************************************************************************/ |
6143 | | /* ParseProjDict() */ |
6144 | | /************************************************************************/ |
6145 | | |
6146 | | int PDFDataset::ParseProjDict(GDALPDFDictionary *poProjDict) |
6147 | 211 | { |
6148 | 211 | if (poProjDict == nullptr) |
6149 | 0 | return FALSE; |
6150 | 211 | OGRSpatialReference oSRS; |
6151 | 211 | oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
6152 | | |
6153 | | /* -------------------------------------------------------------------- */ |
6154 | | /* Extract WKT attribute (GDAL extension) */ |
6155 | | /* -------------------------------------------------------------------- */ |
6156 | 211 | GDALPDFObject *poWKT = poProjDict->Get("WKT"); |
6157 | 211 | if (poWKT != nullptr && poWKT->GetType() == PDFObjectType_String && |
6158 | 21 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE"))) |
6159 | 21 | { |
6160 | 21 | CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it"); |
6161 | 21 | const char *pszWKTRead = poWKT->GetString().c_str(); |
6162 | 21 | if (pszWKTRead[0] != 0) |
6163 | 21 | m_oSRS.importFromWkt(pszWKTRead); |
6164 | 21 | return TRUE; |
6165 | 21 | } |
6166 | | |
6167 | | /* -------------------------------------------------------------------- */ |
6168 | | /* Extract Type attribute */ |
6169 | | /* -------------------------------------------------------------------- */ |
6170 | 190 | GDALPDFObject *poType = poProjDict->Get("Type"); |
6171 | 190 | if (poType == nullptr) |
6172 | 10 | { |
6173 | 10 | CPLError(CE_Failure, CPLE_AppDefined, |
6174 | 10 | "Cannot find Type of Projection object"); |
6175 | 10 | return FALSE; |
6176 | 10 | } |
6177 | | |
6178 | 180 | if (poType->GetType() != PDFObjectType_Name) |
6179 | 3 | { |
6180 | 3 | CPLError(CE_Failure, CPLE_AppDefined, |
6181 | 3 | "Invalid type for Type of Projection object"); |
6182 | 3 | return FALSE; |
6183 | 3 | } |
6184 | | |
6185 | 177 | if (strcmp(poType->GetName().c_str(), "Projection") != 0) |
6186 | 13 | { |
6187 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
6188 | 13 | "Invalid value for Type of Projection object : %s", |
6189 | 13 | poType->GetName().c_str()); |
6190 | 13 | return FALSE; |
6191 | 13 | } |
6192 | | |
6193 | | /* -------------------------------------------------------------------- */ |
6194 | | /* Extract Datum attribute */ |
6195 | | /* -------------------------------------------------------------------- */ |
6196 | 164 | int bIsWGS84 = FALSE; |
6197 | 164 | int bIsNAD83 = FALSE; |
6198 | | /* int bIsNAD27 = FALSE; */ |
6199 | | |
6200 | 164 | GDALPDFObject *poDatum = poProjDict->Get("Datum"); |
6201 | 164 | if (poDatum != nullptr) |
6202 | 43 | { |
6203 | 43 | if (poDatum->GetType() == PDFObjectType_String) |
6204 | 43 | { |
6205 | | /* Using Annex A of |
6206 | | * http://portal.opengeospatial.org/files/?artifact_id=40537 */ |
6207 | 43 | const char *pszDatum = poDatum->GetString().c_str(); |
6208 | 43 | CPLDebug("PDF", "Datum = %s", pszDatum); |
6209 | 43 | if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE")) |
6210 | 19 | { |
6211 | 19 | bIsWGS84 = TRUE; |
6212 | 19 | oSRS.SetWellKnownGeogCS("WGS84"); |
6213 | 19 | } |
6214 | 24 | else if (EQUAL(pszDatum, "NAR") || STARTS_WITH_CI(pszDatum, "NAR-")) |
6215 | 0 | { |
6216 | 0 | bIsNAD83 = TRUE; |
6217 | 0 | oSRS.SetWellKnownGeogCS("NAD83"); |
6218 | 0 | } |
6219 | 24 | else if (EQUAL(pszDatum, "NAS") || STARTS_WITH_CI(pszDatum, "NAS-")) |
6220 | 0 | { |
6221 | | /* bIsNAD27 = TRUE; */ |
6222 | 0 | oSRS.SetWellKnownGeogCS("NAD27"); |
6223 | 0 | } |
6224 | 24 | else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */ |
6225 | 0 | { |
6226 | 0 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6227 | 0 | "unknown" /*const char * pszDatumName */, |
6228 | 0 | "International 1924", 6378388, 297); |
6229 | 0 | oSRS.SetTOWGS84(-333, -222, 114); |
6230 | 0 | } |
6231 | 24 | else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */ |
6232 | 0 | { |
6233 | 0 | oSRS.importFromEPSG(4131); |
6234 | 0 | } |
6235 | 24 | else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */ |
6236 | 0 | { |
6237 | 0 | oSRS.importFromEPSG(4283); |
6238 | 0 | } |
6239 | 24 | else if (STARTS_WITH_CI(pszDatum, "OHA-")) /* Old Hawaiian */ |
6240 | 0 | { |
6241 | 0 | oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */ |
6242 | 0 | if (!EQUAL(pszDatum, "OHA-M")) |
6243 | 0 | { |
6244 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6245 | 0 | "Using OHA-M (Old Hawaiian Mean) definition for " |
6246 | 0 | "%s. Potential issue with datum shift parameters", |
6247 | 0 | pszDatum); |
6248 | 0 | OGR_SRSNode *poNode = oSRS.GetRoot(); |
6249 | 0 | int iChild = poNode->FindChild("AUTHORITY"); |
6250 | 0 | if (iChild != -1) |
6251 | 0 | poNode->DestroyChild(iChild); |
6252 | 0 | iChild = poNode->FindChild("DATUM"); |
6253 | 0 | if (iChild != -1) |
6254 | 0 | { |
6255 | 0 | poNode = poNode->GetChild(iChild); |
6256 | 0 | iChild = poNode->FindChild("AUTHORITY"); |
6257 | 0 | if (iChild != -1) |
6258 | 0 | poNode->DestroyChild(iChild); |
6259 | 0 | } |
6260 | 0 | } |
6261 | 0 | } |
6262 | 24 | else |
6263 | 24 | { |
6264 | 24 | CPLError(CE_Warning, CPLE_AppDefined, |
6265 | 24 | "Unhandled (yet) value for Datum : %s. Defaulting to " |
6266 | 24 | "WGS84...", |
6267 | 24 | pszDatum); |
6268 | 24 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6269 | 24 | "unknown" /*const char * pszDatumName */, |
6270 | 24 | "unknown", 6378137, 298.257223563); |
6271 | 24 | } |
6272 | 43 | } |
6273 | 0 | else if (poDatum->GetType() == PDFObjectType_Dictionary) |
6274 | 0 | { |
6275 | 0 | GDALPDFDictionary *poDatumDict = poDatum->GetDictionary(); |
6276 | |
|
6277 | 0 | GDALPDFObject *poDatumDescription = poDatumDict->Get("Description"); |
6278 | 0 | const char *pszDatumDescription = "unknown"; |
6279 | 0 | if (poDatumDescription != nullptr && |
6280 | 0 | poDatumDescription->GetType() == PDFObjectType_String) |
6281 | 0 | pszDatumDescription = poDatumDescription->GetString().c_str(); |
6282 | 0 | CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription); |
6283 | |
|
6284 | 0 | GDALPDFObject *poEllipsoid = poDatumDict->Get("Ellipsoid"); |
6285 | 0 | if (poEllipsoid == nullptr || |
6286 | 0 | !(poEllipsoid->GetType() == PDFObjectType_String || |
6287 | 0 | poEllipsoid->GetType() == PDFObjectType_Dictionary)) |
6288 | 0 | { |
6289 | 0 | CPLError( |
6290 | 0 | CE_Warning, CPLE_AppDefined, |
6291 | 0 | "Cannot find Ellipsoid in Datum. Defaulting to WGS84..."); |
6292 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "unknown", |
6293 | 0 | 6378137, 298.257223563); |
6294 | 0 | } |
6295 | 0 | else if (poEllipsoid->GetType() == PDFObjectType_String) |
6296 | 0 | { |
6297 | 0 | const char *pszEllipsoid = poEllipsoid->GetString().c_str(); |
6298 | 0 | CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid); |
6299 | 0 | if (EQUAL(pszEllipsoid, "WE")) |
6300 | 0 | { |
6301 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "WGS 84", |
6302 | 0 | 6378137, 298.257223563); |
6303 | 0 | } |
6304 | 0 | else |
6305 | 0 | { |
6306 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6307 | 0 | "Unhandled (yet) value for Ellipsoid : %s. " |
6308 | 0 | "Defaulting to WGS84...", |
6309 | 0 | pszEllipsoid); |
6310 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, pszEllipsoid, |
6311 | 0 | 6378137, 298.257223563); |
6312 | 0 | } |
6313 | 0 | } |
6314 | 0 | else // if (poEllipsoid->GetType() == PDFObjectType_Dictionary) |
6315 | 0 | { |
6316 | 0 | GDALPDFDictionary *poEllipsoidDict = |
6317 | 0 | poEllipsoid->GetDictionary(); |
6318 | |
|
6319 | 0 | GDALPDFObject *poEllipsoidDescription = |
6320 | 0 | poEllipsoidDict->Get("Description"); |
6321 | 0 | const char *pszEllipsoidDescription = "unknown"; |
6322 | 0 | if (poEllipsoidDescription != nullptr && |
6323 | 0 | poEllipsoidDescription->GetType() == PDFObjectType_String) |
6324 | 0 | pszEllipsoidDescription = |
6325 | 0 | poEllipsoidDescription->GetString().c_str(); |
6326 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", |
6327 | 0 | pszEllipsoidDescription); |
6328 | |
|
6329 | 0 | double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis"); |
6330 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", |
6331 | 0 | dfSemiMajor); |
6332 | 0 | double dfInvFlattening = -1.0; |
6333 | |
|
6334 | 0 | if (poEllipsoidDict->Get("InvFlattening")) |
6335 | 0 | { |
6336 | 0 | dfInvFlattening = Get(poEllipsoidDict, "InvFlattening"); |
6337 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", |
6338 | 0 | dfInvFlattening); |
6339 | 0 | } |
6340 | 0 | else if (poEllipsoidDict->Get("SemiMinorAxis")) |
6341 | 0 | { |
6342 | 0 | double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis"); |
6343 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", |
6344 | 0 | dfSemiMinor); |
6345 | 0 | dfInvFlattening = |
6346 | 0 | OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); |
6347 | 0 | } |
6348 | |
|
6349 | 0 | if (dfSemiMajor != 0.0 && dfInvFlattening != -1.0) |
6350 | 0 | { |
6351 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6352 | 0 | pszEllipsoidDescription, dfSemiMajor, |
6353 | 0 | dfInvFlattening); |
6354 | 0 | } |
6355 | 0 | else |
6356 | 0 | { |
6357 | 0 | CPLError( |
6358 | 0 | CE_Warning, CPLE_AppDefined, |
6359 | 0 | "Invalid Ellipsoid object. Defaulting to WGS84..."); |
6360 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6361 | 0 | pszEllipsoidDescription, 6378137, |
6362 | 0 | 298.257223563); |
6363 | 0 | } |
6364 | 0 | } |
6365 | |
|
6366 | 0 | GDALPDFObject *poTOWGS84 = poDatumDict->Get("ToWGS84"); |
6367 | 0 | if (poTOWGS84 != nullptr && |
6368 | 0 | poTOWGS84->GetType() == PDFObjectType_Dictionary) |
6369 | 0 | { |
6370 | 0 | GDALPDFDictionary *poTOWGS84Dict = poTOWGS84->GetDictionary(); |
6371 | 0 | double dx = Get(poTOWGS84Dict, "dx"); |
6372 | 0 | double dy = Get(poTOWGS84Dict, "dy"); |
6373 | 0 | double dz = Get(poTOWGS84Dict, "dz"); |
6374 | 0 | if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") && |
6375 | 0 | poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf")) |
6376 | 0 | { |
6377 | 0 | double rx = Get(poTOWGS84Dict, "rx"); |
6378 | 0 | double ry = Get(poTOWGS84Dict, "ry"); |
6379 | 0 | double rz = Get(poTOWGS84Dict, "rz"); |
6380 | 0 | double sf = Get(poTOWGS84Dict, "sf"); |
6381 | 0 | oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf); |
6382 | 0 | } |
6383 | 0 | else |
6384 | 0 | { |
6385 | 0 | oSRS.SetTOWGS84(dx, dy, dz); |
6386 | 0 | } |
6387 | 0 | } |
6388 | 0 | } |
6389 | 43 | } |
6390 | | |
6391 | | /* -------------------------------------------------------------------- */ |
6392 | | /* Extract Hemisphere attribute */ |
6393 | | /* -------------------------------------------------------------------- */ |
6394 | 164 | CPLString osHemisphere; |
6395 | 164 | GDALPDFObject *poHemisphere = poProjDict->Get("Hemisphere"); |
6396 | 164 | if (poHemisphere != nullptr && |
6397 | 0 | poHemisphere->GetType() == PDFObjectType_String) |
6398 | 0 | { |
6399 | 0 | osHemisphere = poHemisphere->GetString(); |
6400 | 0 | } |
6401 | | |
6402 | | /* -------------------------------------------------------------------- */ |
6403 | | /* Extract ProjectionType attribute */ |
6404 | | /* -------------------------------------------------------------------- */ |
6405 | 164 | GDALPDFObject *poProjectionType = poProjDict->Get("ProjectionType"); |
6406 | 164 | if (poProjectionType == nullptr || |
6407 | 160 | poProjectionType->GetType() != PDFObjectType_String) |
6408 | 4 | { |
6409 | 4 | CPLError(CE_Failure, CPLE_AppDefined, |
6410 | 4 | "Cannot find ProjectionType of Projection object"); |
6411 | 4 | return FALSE; |
6412 | 4 | } |
6413 | 160 | CPLString osProjectionType(poProjectionType->GetString()); |
6414 | 160 | CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str()); |
6415 | | |
6416 | | /* Unhandled: NONE, GEODETIC */ |
6417 | | |
6418 | 160 | if (EQUAL(osProjectionType, "GEOGRAPHIC")) |
6419 | 148 | { |
6420 | | /* Nothing to do */ |
6421 | 148 | } |
6422 | | |
6423 | | /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */ |
6424 | | |
6425 | 12 | else if (EQUAL(osProjectionType, "UT")) /* UTM */ |
6426 | 0 | { |
6427 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6428 | 0 | if (dfZone >= 1 && dfZone <= 60) |
6429 | 0 | { |
6430 | 0 | int nZone = static_cast<int>(dfZone); |
6431 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6432 | 0 | if (bIsWGS84) |
6433 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6434 | 0 | else |
6435 | 0 | oSRS.SetUTM(nZone, bNorth); |
6436 | 0 | } |
6437 | 0 | } |
6438 | | |
6439 | 12 | else if (EQUAL(osProjectionType, |
6440 | 12 | "UP")) /* Universal Polar Stereographic (UPS) */ |
6441 | 0 | { |
6442 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6443 | 0 | if (bIsWGS84) |
6444 | 0 | oSRS.importFromEPSG((bNorth) ? 32661 : 32761); |
6445 | 0 | else |
6446 | 0 | oSRS.SetPS((bNorth) ? 90 : -90, 0, 0.994, 200000, 200000); |
6447 | 0 | } |
6448 | | |
6449 | 12 | else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */ |
6450 | 0 | { |
6451 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6452 | 0 | if (dfZone >= 0 && dfZone <= INT_MAX) |
6453 | 0 | { |
6454 | 0 | int nZone = static_cast<int>(dfZone); |
6455 | 0 | oSRS.SetStatePlane(nZone, bIsNAD83); |
6456 | 0 | } |
6457 | 0 | } |
6458 | | |
6459 | 12 | else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */ |
6460 | 0 | { |
6461 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6462 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6463 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6464 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6465 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6466 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6467 | 0 | oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, |
6468 | 0 | dfFalseEasting, dfFalseNorthing); |
6469 | 0 | } |
6470 | | |
6471 | 12 | else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */ |
6472 | 0 | { |
6473 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6474 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6475 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6476 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6477 | 0 | oSRS.SetAE(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6478 | 0 | } |
6479 | | |
6480 | 12 | else if (EQUAL(osProjectionType, "BF")) /* Bonne */ |
6481 | 0 | { |
6482 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6483 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6484 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6485 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6486 | 0 | oSRS.SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6487 | 0 | dfFalseNorthing); |
6488 | 0 | } |
6489 | | |
6490 | 12 | else if (EQUAL(osProjectionType, "CS")) /* Cassini */ |
6491 | 0 | { |
6492 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6493 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6494 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6495 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6496 | 0 | oSRS.SetCS(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6497 | 0 | } |
6498 | | |
6499 | 12 | else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */ |
6500 | 0 | { |
6501 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6502 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6503 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6504 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6505 | 0 | oSRS.SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6506 | 0 | dfFalseNorthing); |
6507 | 0 | } |
6508 | | |
6509 | 12 | else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */ |
6510 | 0 | { |
6511 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6512 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6513 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6514 | 0 | oSRS.SetEckertIV(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6515 | 0 | } |
6516 | | |
6517 | 12 | else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */ |
6518 | 0 | { |
6519 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6520 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6521 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6522 | 0 | oSRS.SetEckertVI(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6523 | 0 | } |
6524 | | |
6525 | 12 | else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */ |
6526 | 0 | { |
6527 | 0 | double dfCenterLat = Get(poProjDict, "StandardParallel"); |
6528 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6529 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6530 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6531 | 0 | oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting, |
6532 | 0 | dfFalseNorthing); |
6533 | 0 | } |
6534 | | |
6535 | 12 | else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */ |
6536 | 0 | { |
6537 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6538 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6539 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6540 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6541 | 0 | oSRS.SetGnomonic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6542 | 0 | dfFalseNorthing); |
6543 | 0 | } |
6544 | | |
6545 | 12 | else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */ |
6546 | 0 | { |
6547 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6548 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6549 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6550 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6551 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6552 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6553 | 0 | oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting, |
6554 | 0 | dfFalseNorthing); |
6555 | 0 | } |
6556 | | |
6557 | 12 | else if (EQUAL(osProjectionType, "MC")) /* Mercator */ |
6558 | 0 | { |
6559 | | #ifdef not_supported |
6560 | | if (poProjDict->Get("StandardParallelOne") == nullptr) |
6561 | | #endif |
6562 | 0 | { |
6563 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6564 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6565 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6566 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6567 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6568 | 0 | oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6569 | 0 | dfFalseNorthing); |
6570 | 0 | } |
6571 | | #ifdef not_supported |
6572 | | else |
6573 | | { |
6574 | | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6575 | | double dfCenterLat = poProjDict->Get("OriginLatitude") |
6576 | | ? Get(poProjDict, "OriginLatitude") |
6577 | | : 0; |
6578 | | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6579 | | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6580 | | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6581 | | oSRS.SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong, |
6582 | | dfFalseEasting, dfFalseNorthing); |
6583 | | } |
6584 | | #endif |
6585 | 0 | } |
6586 | | |
6587 | 12 | else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */ |
6588 | 0 | { |
6589 | 0 | double dfCenterLat = 0 /* ? */; |
6590 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6591 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6592 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6593 | 0 | oSRS.SetMC(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6594 | 0 | } |
6595 | | |
6596 | 12 | else if (EQUAL(osProjectionType, "MP")) /* Mollweide */ |
6597 | 0 | { |
6598 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6599 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6600 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6601 | 0 | oSRS.SetMollweide(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6602 | 0 | } |
6603 | | |
6604 | | /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */ |
6605 | | |
6606 | 12 | else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */ |
6607 | 0 | { |
6608 | | /* No parameter specified in the PDF, so let's take the ones of |
6609 | | * EPSG:27200 */ |
6610 | 0 | double dfCenterLat = -41; |
6611 | 0 | double dfCenterLong = 173; |
6612 | 0 | double dfFalseEasting = 2510000; |
6613 | 0 | double dfFalseNorthing = 6023150; |
6614 | 0 | oSRS.SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting, |
6615 | 0 | dfFalseNorthing); |
6616 | 0 | } |
6617 | | |
6618 | 12 | else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */ |
6619 | 0 | { |
6620 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6621 | 0 | double dfLat1 = Get(poProjDict, "LatitudeOne"); |
6622 | 0 | double dfLong1 = Get(poProjDict, "LongitudeOne"); |
6623 | 0 | double dfLat2 = Get(poProjDict, "LatitudeTwo"); |
6624 | 0 | double dfLong2 = Get(poProjDict, "LongitudeTwo"); |
6625 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6626 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6627 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6628 | 0 | oSRS.SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2, dfScale, |
6629 | 0 | dfFalseEasting, dfFalseNorthing); |
6630 | 0 | } |
6631 | | |
6632 | 12 | else if (EQUAL(osProjectionType, "OD")) /* Orthographic */ |
6633 | 0 | { |
6634 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6635 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6636 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6637 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6638 | 0 | oSRS.SetOrthographic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6639 | 0 | dfFalseNorthing); |
6640 | 0 | } |
6641 | | |
6642 | 12 | else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */ |
6643 | 0 | { |
6644 | 0 | double dfCenterLat = Get(poProjDict, "LatitudeTrueScale"); |
6645 | 0 | double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole"); |
6646 | 0 | double dfScale = 1.0; |
6647 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6648 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6649 | 0 | oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6650 | 0 | dfFalseNorthing); |
6651 | 0 | } |
6652 | | |
6653 | 12 | else if (EQUAL(osProjectionType, "PH")) /* Polyconic */ |
6654 | 0 | { |
6655 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6656 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6657 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6658 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6659 | 0 | oSRS.SetPolyconic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6660 | 0 | dfFalseNorthing); |
6661 | 0 | } |
6662 | | |
6663 | 12 | else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */ |
6664 | 0 | { |
6665 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6666 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6667 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6668 | 0 | oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6669 | 0 | } |
6670 | | |
6671 | 12 | else if (EQUAL(osProjectionType, "SD")) /* Stereographic */ |
6672 | 0 | { |
6673 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6674 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6675 | 0 | double dfScale = 1.0; |
6676 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6677 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6678 | 0 | oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale, |
6679 | 0 | dfFalseEasting, dfFalseNorthing); |
6680 | 0 | } |
6681 | | |
6682 | 12 | else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */ |
6683 | 0 | { |
6684 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6685 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6686 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6687 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6688 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6689 | 0 | if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfCenterLong >= -180 && |
6690 | 0 | dfCenterLong <= 180 && dfFalseEasting == 500000 && |
6691 | 0 | (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0)) |
6692 | 0 | { |
6693 | 0 | const int nZone = |
6694 | 0 | static_cast<int>(floor((dfCenterLong + 180.0) / 6.0) + 1); |
6695 | 0 | int bNorth = dfFalseNorthing == 0; |
6696 | 0 | if (bIsWGS84) |
6697 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6698 | 0 | else if (bIsNAD83 && bNorth) |
6699 | 0 | oSRS.importFromEPSG(26900 + nZone); |
6700 | 0 | else |
6701 | 0 | oSRS.SetUTM(nZone, bNorth); |
6702 | 0 | } |
6703 | 0 | else |
6704 | 0 | { |
6705 | 0 | oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6706 | 0 | dfFalseNorthing); |
6707 | 0 | } |
6708 | 0 | } |
6709 | | |
6710 | | /* Unhandled TX : Transverse Cylindrical Equal Area */ |
6711 | | |
6712 | 12 | else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */ |
6713 | 0 | { |
6714 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6715 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6716 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6717 | 0 | oSRS.SetVDG(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6718 | 0 | } |
6719 | | |
6720 | 12 | else |
6721 | 12 | { |
6722 | 12 | CPLError(CE_Failure, CPLE_AppDefined, |
6723 | 12 | "Unhandled (yet) value for ProjectionType : %s", |
6724 | 12 | osProjectionType.c_str()); |
6725 | 12 | return FALSE; |
6726 | 12 | } |
6727 | | |
6728 | | /* -------------------------------------------------------------------- */ |
6729 | | /* Extract Units attribute */ |
6730 | | /* -------------------------------------------------------------------- */ |
6731 | 148 | CPLString osUnits; |
6732 | 148 | GDALPDFObject *poUnits = poProjDict->Get("Units"); |
6733 | 148 | if (poUnits != nullptr && poUnits->GetType() == PDFObjectType_String && |
6734 | 0 | !EQUAL(osProjectionType, "GEOGRAPHIC")) |
6735 | 0 | { |
6736 | 0 | osUnits = poUnits->GetString(); |
6737 | 0 | CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str()); |
6738 | | |
6739 | | // This is super weird. The false easting/northing of the SRS |
6740 | | // are expressed in the unit, but the geotransform is expressed in |
6741 | | // meters. Hence this hack to have an equivalent SRS definition, but |
6742 | | // with linear units converted in meters. |
6743 | 0 | if (EQUAL(osUnits, "M")) |
6744 | 0 | oSRS.SetLinearUnits("Meter", 1.0); |
6745 | 0 | else if (EQUAL(osUnits, "FT")) |
6746 | 0 | { |
6747 | 0 | oSRS.SetLinearUnits("foot", 0.3048); |
6748 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6749 | 0 | } |
6750 | 0 | else if (EQUAL(osUnits, "USSF")) |
6751 | 0 | { |
6752 | 0 | oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV)); |
6753 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6754 | 0 | } |
6755 | 0 | else |
6756 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unhandled unit: %s", |
6757 | 0 | osUnits.c_str()); |
6758 | 0 | } |
6759 | | |
6760 | | /* -------------------------------------------------------------------- */ |
6761 | | /* Export SpatialRef */ |
6762 | | /* -------------------------------------------------------------------- */ |
6763 | 148 | m_oSRS = std::move(oSRS); |
6764 | | |
6765 | 148 | return TRUE; |
6766 | 160 | } |
6767 | | |
6768 | | /************************************************************************/ |
6769 | | /* ParseVP() */ |
6770 | | /************************************************************************/ |
6771 | | |
6772 | | int PDFDataset::ParseVP(GDALPDFObject *poVP, double dfMediaBoxWidth, |
6773 | | double dfMediaBoxHeight) |
6774 | 5.80k | { |
6775 | 5.80k | int i; |
6776 | | |
6777 | 5.80k | if (poVP->GetType() != PDFObjectType_Array) |
6778 | 55 | return FALSE; |
6779 | | |
6780 | 5.75k | GDALPDFArray *poVPArray = poVP->GetArray(); |
6781 | | |
6782 | 5.75k | int nLength = poVPArray->GetLength(); |
6783 | 5.75k | CPLDebug("PDF", "VP length = %d", nLength); |
6784 | 5.75k | if (nLength < 1) |
6785 | 0 | return FALSE; |
6786 | | |
6787 | | /* -------------------------------------------------------------------- */ |
6788 | | /* Find the largest BBox */ |
6789 | | /* -------------------------------------------------------------------- */ |
6790 | 5.75k | const char *pszNeatlineToSelect = |
6791 | 5.75k | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
6792 | | |
6793 | 5.75k | int iLargest = 0; |
6794 | 5.75k | int iRequestedVP = -1; |
6795 | 5.75k | double dfLargestArea = 0; |
6796 | | |
6797 | 12.0k | for (i = 0; i < nLength; i++) |
6798 | 7.42k | { |
6799 | 7.42k | GDALPDFObject *poVPElt = poVPArray->Get(i); |
6800 | 7.42k | if (poVPElt == nullptr || |
6801 | 6.99k | poVPElt->GetType() != PDFObjectType_Dictionary) |
6802 | 966 | { |
6803 | 966 | return FALSE; |
6804 | 966 | } |
6805 | | |
6806 | 6.45k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6807 | | |
6808 | 6.45k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6809 | 6.45k | if (poMeasure == nullptr || |
6810 | 4.91k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6811 | 1.62k | { |
6812 | 1.62k | continue; |
6813 | 1.62k | } |
6814 | | /* -------------------------------------------------------------------- |
6815 | | */ |
6816 | | /* Extract Subtype attribute */ |
6817 | | /* -------------------------------------------------------------------- |
6818 | | */ |
6819 | 4.83k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6820 | 4.83k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6821 | 4.83k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
6822 | 304 | { |
6823 | 304 | continue; |
6824 | 304 | } |
6825 | | |
6826 | 4.52k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
6827 | 4.52k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
6828 | 191 | { |
6829 | 191 | continue; |
6830 | 191 | } |
6831 | | |
6832 | 4.33k | GDALPDFObject *poName = poVPEltDict->Get("Name"); |
6833 | 4.33k | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
6834 | 3.64k | { |
6835 | 3.64k | CPLDebug("PDF", "Name = %s", poName->GetString().c_str()); |
6836 | 3.64k | if (EQUAL(poName->GetString().c_str(), pszNeatlineToSelect)) |
6837 | 0 | { |
6838 | 0 | iRequestedVP = i; |
6839 | 0 | } |
6840 | 3.64k | } |
6841 | | |
6842 | 4.33k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6843 | 4.33k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6844 | 113 | { |
6845 | 113 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6846 | 113 | return FALSE; |
6847 | 113 | } |
6848 | | |
6849 | 4.22k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6850 | 4.22k | if (nBboxLength != 4) |
6851 | 81 | { |
6852 | 81 | CPLError(CE_Failure, CPLE_AppDefined, |
6853 | 81 | "Invalid length for Bbox object"); |
6854 | 81 | return FALSE; |
6855 | 81 | } |
6856 | | |
6857 | 4.14k | double adfBBox[4]; |
6858 | 4.14k | adfBBox[0] = Get(poBBox, 0); |
6859 | 4.14k | adfBBox[1] = Get(poBBox, 1); |
6860 | 4.14k | adfBBox[2] = Get(poBBox, 2); |
6861 | 4.14k | adfBBox[3] = Get(poBBox, 3); |
6862 | 4.14k | double dfArea = |
6863 | 4.14k | fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]); |
6864 | 4.14k | if (dfArea > dfLargestArea) |
6865 | 3.59k | { |
6866 | 3.59k | iLargest = i; |
6867 | 3.59k | dfLargestArea = dfArea; |
6868 | 3.59k | } |
6869 | 4.14k | } |
6870 | | |
6871 | 4.59k | if (nLength > 1) |
6872 | 1.00k | { |
6873 | 1.00k | CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest); |
6874 | 1.00k | } |
6875 | | |
6876 | 4.59k | GDALPDFObject *poVPElt = nullptr; |
6877 | | |
6878 | 4.59k | if (iRequestedVP > -1) |
6879 | 0 | { |
6880 | 0 | CPLDebug("PDF", "Requested NEATLINE BBox in VP array is element %d", |
6881 | 0 | iRequestedVP); |
6882 | 0 | poVPElt = poVPArray->Get(iRequestedVP); |
6883 | 0 | } |
6884 | 4.59k | else |
6885 | 4.59k | { |
6886 | 4.59k | poVPElt = poVPArray->Get(iLargest); |
6887 | 4.59k | } |
6888 | | |
6889 | 4.59k | if (poVPElt == nullptr || poVPElt->GetType() != PDFObjectType_Dictionary) |
6890 | 0 | { |
6891 | 0 | return FALSE; |
6892 | 0 | } |
6893 | | |
6894 | 4.59k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6895 | | |
6896 | 4.59k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6897 | 4.59k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6898 | 110 | { |
6899 | 110 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6900 | 110 | return FALSE; |
6901 | 110 | } |
6902 | | |
6903 | 4.48k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6904 | 4.48k | if (nBboxLength != 4) |
6905 | 192 | { |
6906 | 192 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for Bbox object"); |
6907 | 192 | return FALSE; |
6908 | 192 | } |
6909 | | |
6910 | 4.29k | double dfULX = Get(poBBox, 0); |
6911 | 4.29k | double dfULY = dfMediaBoxHeight - Get(poBBox, 1); |
6912 | 4.29k | double dfLRX = Get(poBBox, 2); |
6913 | 4.29k | double dfLRY = dfMediaBoxHeight - Get(poBBox, 3); |
6914 | | |
6915 | | /* -------------------------------------------------------------------- */ |
6916 | | /* Extract Measure attribute */ |
6917 | | /* -------------------------------------------------------------------- */ |
6918 | 4.29k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6919 | 4.29k | if (poMeasure == nullptr || |
6920 | 3.84k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6921 | 465 | { |
6922 | 465 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Measure object"); |
6923 | 465 | return FALSE; |
6924 | 465 | } |
6925 | | |
6926 | 3.82k | int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, dfULX, |
6927 | 3.82k | dfULY, dfLRX, dfLRY); |
6928 | | |
6929 | | /* -------------------------------------------------------------------- */ |
6930 | | /* Extract PointData attribute */ |
6931 | | /* -------------------------------------------------------------------- */ |
6932 | 3.82k | GDALPDFObject *poPointData = poVPEltDict->Get("PtData"); |
6933 | 3.82k | if (poPointData != nullptr && |
6934 | 0 | poPointData->GetType() == PDFObjectType_Dictionary) |
6935 | 0 | { |
6936 | 0 | CPLDebug("PDF", "Found PointData"); |
6937 | 0 | } |
6938 | | |
6939 | 3.82k | return bRet; |
6940 | 4.29k | } |
6941 | | |
6942 | | /************************************************************************/ |
6943 | | /* ParseMeasure() */ |
6944 | | /************************************************************************/ |
6945 | | |
6946 | | int PDFDataset::ParseMeasure(GDALPDFObject *poMeasure, double dfMediaBoxWidth, |
6947 | | double dfMediaBoxHeight, double dfULX, |
6948 | | double dfULY, double dfLRX, double dfLRY) |
6949 | 3.82k | { |
6950 | 3.82k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6951 | | |
6952 | | /* -------------------------------------------------------------------- */ |
6953 | | /* Extract Subtype attribute */ |
6954 | | /* -------------------------------------------------------------------- */ |
6955 | 3.82k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6956 | 3.82k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
6957 | 243 | { |
6958 | 243 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Subtype object"); |
6959 | 243 | return FALSE; |
6960 | 243 | } |
6961 | | |
6962 | 3.58k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
6963 | 3.58k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
6964 | 12 | return FALSE; |
6965 | | |
6966 | | /* -------------------------------------------------------------------- */ |
6967 | | /* Extract Bounds attribute (optional) */ |
6968 | | /* -------------------------------------------------------------------- */ |
6969 | | |
6970 | | /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf |
6971 | | */ |
6972 | | /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */ |
6973 | | /* LPTS, GPTS and Bounds. Use those ones */ |
6974 | | |
6975 | 3.57k | GDALPDFObject *poBounds = poMeasureDict->Get("lgit:Bounds"); |
6976 | 3.57k | if (poBounds != nullptr && poBounds->GetType() == PDFObjectType_Array) |
6977 | 0 | { |
6978 | 0 | CPLDebug("PDF", "Using lgit:Bounds"); |
6979 | 0 | } |
6980 | 3.57k | else if ((poBounds = poMeasureDict->Get("Bounds")) == nullptr || |
6981 | 2.14k | poBounds->GetType() != PDFObjectType_Array) |
6982 | 1.43k | { |
6983 | 1.43k | poBounds = nullptr; |
6984 | 1.43k | } |
6985 | | |
6986 | 3.57k | if (poBounds != nullptr) |
6987 | 2.13k | { |
6988 | 2.13k | int nBoundsLength = poBounds->GetArray()->GetLength(); |
6989 | 2.13k | if (nBoundsLength == 8) |
6990 | 1.79k | { |
6991 | 1.79k | double adfBounds[8]; |
6992 | 16.1k | for (int i = 0; i < 8; i++) |
6993 | 14.3k | { |
6994 | 14.3k | adfBounds[i] = Get(poBounds, i); |
6995 | 14.3k | CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]); |
6996 | 14.3k | } |
6997 | | |
6998 | | // TODO we should use it to restrict the neatline but |
6999 | | // I have yet to set a sample where bounds are not the four |
7000 | | // corners of the unit square. |
7001 | 1.79k | } |
7002 | 2.13k | } |
7003 | | |
7004 | | /* -------------------------------------------------------------------- */ |
7005 | | /* Extract GPTS attribute */ |
7006 | | /* -------------------------------------------------------------------- */ |
7007 | 3.57k | GDALPDFObject *poGPTS = poMeasureDict->Get("lgit:GPTS"); |
7008 | 3.57k | if (poGPTS != nullptr && poGPTS->GetType() == PDFObjectType_Array) |
7009 | 0 | { |
7010 | 0 | CPLDebug("PDF", "Using lgit:GPTS"); |
7011 | 0 | } |
7012 | 3.57k | else if ((poGPTS = poMeasureDict->Get("GPTS")) == nullptr || |
7013 | 3.53k | poGPTS->GetType() != PDFObjectType_Array) |
7014 | 51 | { |
7015 | 51 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GPTS object"); |
7016 | 51 | return FALSE; |
7017 | 51 | } |
7018 | | |
7019 | 3.51k | int nGPTSLength = poGPTS->GetArray()->GetLength(); |
7020 | 3.51k | if ((nGPTSLength % 2) != 0 || nGPTSLength < 6) |
7021 | 147 | { |
7022 | 147 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for GPTS object"); |
7023 | 147 | return FALSE; |
7024 | 147 | } |
7025 | | |
7026 | 3.37k | std::vector<double> adfGPTS(nGPTSLength); |
7027 | 33.4k | for (int i = 0; i < nGPTSLength; i++) |
7028 | 30.0k | { |
7029 | 30.0k | adfGPTS[i] = Get(poGPTS, i); |
7030 | 30.0k | CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]); |
7031 | 30.0k | } |
7032 | | |
7033 | | /* -------------------------------------------------------------------- */ |
7034 | | /* Extract LPTS attribute */ |
7035 | | /* -------------------------------------------------------------------- */ |
7036 | 3.37k | GDALPDFObject *poLPTS = poMeasureDict->Get("lgit:LPTS"); |
7037 | 3.37k | if (poLPTS != nullptr && poLPTS->GetType() == PDFObjectType_Array) |
7038 | 0 | { |
7039 | 0 | CPLDebug("PDF", "Using lgit:LPTS"); |
7040 | 0 | } |
7041 | 3.37k | else if ((poLPTS = poMeasureDict->Get("LPTS")) == nullptr || |
7042 | 3.28k | poLPTS->GetType() != PDFObjectType_Array) |
7043 | 93 | { |
7044 | 93 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find LPTS object"); |
7045 | 93 | return FALSE; |
7046 | 93 | } |
7047 | | |
7048 | 3.27k | int nLPTSLength = poLPTS->GetArray()->GetLength(); |
7049 | 3.27k | if (nLPTSLength != nGPTSLength) |
7050 | 237 | { |
7051 | 237 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for LPTS object"); |
7052 | 237 | return FALSE; |
7053 | 237 | } |
7054 | | |
7055 | 3.04k | std::vector<double> adfLPTS(nLPTSLength); |
7056 | 27.3k | for (int i = 0; i < nLPTSLength; i++) |
7057 | 24.3k | { |
7058 | 24.3k | adfLPTS[i] = Get(poLPTS, i); |
7059 | 24.3k | CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]); |
7060 | 24.3k | } |
7061 | | |
7062 | | /* -------------------------------------------------------------------- */ |
7063 | | /* Extract GCS attribute */ |
7064 | | /* -------------------------------------------------------------------- */ |
7065 | 3.04k | GDALPDFObject *poGCS = poMeasureDict->Get("GCS"); |
7066 | 3.04k | if (poGCS == nullptr || poGCS->GetType() != PDFObjectType_Dictionary) |
7067 | 119 | { |
7068 | 119 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS object"); |
7069 | 119 | return FALSE; |
7070 | 119 | } |
7071 | | |
7072 | 2.92k | GDALPDFDictionary *poGCSDict = poGCS->GetDictionary(); |
7073 | | |
7074 | | /* -------------------------------------------------------------------- */ |
7075 | | /* Extract GCS.Type attribute */ |
7076 | | /* -------------------------------------------------------------------- */ |
7077 | 2.92k | GDALPDFObject *poGCSType = poGCSDict->Get("Type"); |
7078 | 2.92k | if (poGCSType == nullptr || poGCSType->GetType() != PDFObjectType_Name) |
7079 | 59 | { |
7080 | 59 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS.Type object"); |
7081 | 59 | return FALSE; |
7082 | 59 | } |
7083 | | |
7084 | 2.86k | CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str()); |
7085 | | |
7086 | | /* -------------------------------------------------------------------- */ |
7087 | | /* Extract EPSG attribute */ |
7088 | | /* -------------------------------------------------------------------- */ |
7089 | 2.86k | GDALPDFObject *poEPSG = poGCSDict->Get("EPSG"); |
7090 | 2.86k | int nEPSGCode = 0; |
7091 | 2.86k | if (poEPSG != nullptr && poEPSG->GetType() == PDFObjectType_Int) |
7092 | 1.62k | { |
7093 | 1.62k | nEPSGCode = poEPSG->GetInt(); |
7094 | 1.62k | CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode); |
7095 | 1.62k | } |
7096 | | |
7097 | | /* -------------------------------------------------------------------- */ |
7098 | | /* Extract GCS.WKT attribute */ |
7099 | | /* -------------------------------------------------------------------- */ |
7100 | 2.86k | GDALPDFObject *poGCSWKT = poGCSDict->Get("WKT"); |
7101 | 2.86k | if (poGCSWKT != nullptr && poGCSWKT->GetType() != PDFObjectType_String) |
7102 | 2 | { |
7103 | 2 | poGCSWKT = nullptr; |
7104 | 2 | } |
7105 | | |
7106 | 2.86k | if (poGCSWKT != nullptr) |
7107 | 2.82k | CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str()); |
7108 | | |
7109 | 2.86k | if (nEPSGCode <= 0 && poGCSWKT == nullptr) |
7110 | 9 | { |
7111 | 9 | CPLError(CE_Failure, CPLE_AppDefined, |
7112 | 9 | "Cannot find GCS.WKT or GCS.EPSG objects"); |
7113 | 9 | return FALSE; |
7114 | 9 | } |
7115 | | |
7116 | 2.85k | if (poGCSWKT != nullptr) |
7117 | 2.82k | { |
7118 | 2.82k | m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()); |
7119 | 2.82k | } |
7120 | | |
7121 | 2.85k | bool bSRSOK = false; |
7122 | 2.85k | if (nEPSGCode != 0) |
7123 | 1.62k | { |
7124 | | // At time of writing EPSG CRS codes are <= 32767. |
7125 | | // The usual practice is that codes >= 100000 are in the ESRI namespace |
7126 | | // instead |
7127 | 1.62k | if (nEPSGCode >= 100000) |
7128 | 68 | { |
7129 | 68 | CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler); |
7130 | 68 | OGRSpatialReference oSRS_ESRI; |
7131 | 68 | oSRS_ESRI.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
7132 | 68 | if (oSRS_ESRI.SetFromUserInput(CPLSPrintf("ESRI:%d", nEPSGCode)) == |
7133 | 68 | OGRERR_NONE) |
7134 | 68 | { |
7135 | 68 | bSRSOK = true; |
7136 | | |
7137 | | // Check consistency of ESRI:xxxx and WKT definitions |
7138 | 68 | if (poGCSWKT != nullptr) |
7139 | 64 | { |
7140 | 64 | if (!m_oSRS.GetName() || |
7141 | 45 | (!EQUAL(oSRS_ESRI.GetName(), m_oSRS.GetName()) && |
7142 | 16 | !oSRS_ESRI.IsSame(&m_oSRS))) |
7143 | 35 | { |
7144 | 35 | CPLDebug("PDF", |
7145 | 35 | "Definition from ESRI:%d and WKT=%s do not " |
7146 | 35 | "match. Using WKT string", |
7147 | 35 | nEPSGCode, poGCSWKT->GetString().c_str()); |
7148 | 35 | bSRSOK = false; |
7149 | 35 | } |
7150 | 64 | } |
7151 | 68 | if (bSRSOK) |
7152 | 33 | { |
7153 | 33 | m_oSRS = std::move(oSRS_ESRI); |
7154 | 33 | } |
7155 | 68 | } |
7156 | 68 | } |
7157 | 1.55k | else if (m_oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE) |
7158 | 935 | { |
7159 | 935 | bSRSOK = true; |
7160 | 935 | } |
7161 | 1.62k | } |
7162 | | |
7163 | 2.85k | if (!bSRSOK) |
7164 | 1.88k | { |
7165 | 1.88k | if (poGCSWKT == nullptr) |
7166 | 6 | { |
7167 | 6 | CPLError(CE_Failure, CPLE_AppDefined, |
7168 | 6 | "Cannot resolve EPSG object, and GCS.WKT not found"); |
7169 | 6 | return FALSE; |
7170 | 6 | } |
7171 | | |
7172 | 1.88k | if (m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()) != OGRERR_NONE) |
7173 | 608 | { |
7174 | 608 | m_oSRS.Clear(); |
7175 | 608 | return FALSE; |
7176 | 608 | } |
7177 | 1.88k | } |
7178 | | |
7179 | | /* -------------------------------------------------------------------- */ |
7180 | | /* Compute geotransform */ |
7181 | | /* -------------------------------------------------------------------- */ |
7182 | 2.24k | OGRSpatialReference *poSRSGeog = m_oSRS.CloneGeogCS(); |
7183 | | |
7184 | | /* Files found at |
7185 | | * http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ |
7186 | | */ |
7187 | | /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, |
7188 | | * long) as required by the */ |
7189 | | /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is |
7190 | | * able to understand that, */ |
7191 | | /* so let's also try to do it with a heuristics. */ |
7192 | | |
7193 | 2.24k | bool bReproject = true; |
7194 | 2.24k | if (m_oSRS.IsProjected()) |
7195 | 1.33k | { |
7196 | 6.55k | for (int i = 0; i < nGPTSLength / 2; i++) |
7197 | 5.27k | { |
7198 | 5.27k | if (fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361) |
7199 | 61 | { |
7200 | 61 | CPLDebug("PDF", "GPTS coordinates seems to be in (northing, " |
7201 | 61 | "easting), which is non-standard"); |
7202 | 61 | bReproject = false; |
7203 | 61 | break; |
7204 | 61 | } |
7205 | 5.27k | } |
7206 | 1.33k | } |
7207 | | |
7208 | 2.24k | OGRCoordinateTransformation *poCT = nullptr; |
7209 | 2.24k | if (bReproject) |
7210 | 2.18k | { |
7211 | 2.18k | poCT = OGRCreateCoordinateTransformation(poSRSGeog, &m_oSRS); |
7212 | 2.18k | if (poCT == nullptr) |
7213 | 3 | { |
7214 | 3 | delete poSRSGeog; |
7215 | 3 | m_oSRS.Clear(); |
7216 | 3 | return FALSE; |
7217 | 3 | } |
7218 | 2.18k | } |
7219 | | |
7220 | 2.23k | std::vector<GDAL_GCP> asGCPS(nGPTSLength / 2); |
7221 | | |
7222 | | /* Create NEATLINE */ |
7223 | 2.23k | OGRLinearRing *poRing = nullptr; |
7224 | 2.23k | if (nGPTSLength == 8) |
7225 | 2.23k | { |
7226 | 2.23k | m_poNeatLine = new OGRPolygon(); |
7227 | 2.23k | poRing = new OGRLinearRing(); |
7228 | 2.23k | m_poNeatLine->addRingDirectly(poRing); |
7229 | 2.23k | } |
7230 | | |
7231 | 10.3k | for (int i = 0; i < nGPTSLength / 2; i++) |
7232 | 8.32k | { |
7233 | | /* We probably assume LPTS is 0 or 1 */ |
7234 | 8.32k | asGCPS[i].dfGCPPixel = |
7235 | 8.32k | (dfULX * (1 - adfLPTS[2 * i + 0]) + dfLRX * adfLPTS[2 * i + 0]) / |
7236 | 8.32k | dfMediaBoxWidth * nRasterXSize; |
7237 | 8.32k | asGCPS[i].dfGCPLine = |
7238 | 8.32k | (dfULY * (1 - adfLPTS[2 * i + 1]) + dfLRY * adfLPTS[2 * i + 1]) / |
7239 | 8.32k | dfMediaBoxHeight * nRasterYSize; |
7240 | | |
7241 | 8.32k | double lat = adfGPTS[2 * i]; |
7242 | 8.32k | double lon = adfGPTS[2 * i + 1]; |
7243 | 8.32k | double x = lon; |
7244 | 8.32k | double y = lat; |
7245 | 8.32k | if (bReproject) |
7246 | 8.08k | { |
7247 | 8.08k | if (!poCT->Transform(1, &x, &y, nullptr)) |
7248 | 210 | { |
7249 | 210 | CPLError(CE_Failure, CPLE_AppDefined, |
7250 | 210 | "Cannot reproject (%f, %f)", lon, lat); |
7251 | 210 | delete poSRSGeog; |
7252 | 210 | delete poCT; |
7253 | 210 | m_oSRS.Clear(); |
7254 | 210 | return FALSE; |
7255 | 210 | } |
7256 | 8.08k | } |
7257 | | |
7258 | 8.11k | x = ROUND_IF_CLOSE(x); |
7259 | 8.11k | y = ROUND_IF_CLOSE(y); |
7260 | | |
7261 | 8.11k | asGCPS[i].dfGCPX = x; |
7262 | 8.11k | asGCPS[i].dfGCPY = y; |
7263 | | |
7264 | 8.11k | if (poRing) |
7265 | 8.11k | poRing->addPoint(x, y); |
7266 | 8.11k | } |
7267 | | |
7268 | 2.02k | delete poSRSGeog; |
7269 | 2.02k | delete poCT; |
7270 | | |
7271 | 2.02k | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7272 | 2.02k | FALSE)) |
7273 | 926 | { |
7274 | 926 | CPLDebug("PDF", |
7275 | 926 | "Could not compute GT with exact match. Try with approximate"); |
7276 | 926 | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7277 | 926 | TRUE)) |
7278 | 420 | { |
7279 | 420 | CPLError(CE_Failure, CPLE_AppDefined, |
7280 | 420 | "Could not compute GT with approximate match."); |
7281 | 420 | return FALSE; |
7282 | 420 | } |
7283 | 926 | } |
7284 | 1.60k | m_bGeoTransformValid = true; |
7285 | | |
7286 | | // If the non scaling terms of the geotransform are significantly smaller |
7287 | | // than the pixel size, then nullify them as being just artifacts of |
7288 | | // reprojection and GDALGCPsToGeoTransform() numerical imprecisions. |
7289 | 1.60k | const double dfPixelSize = std::min(fabs(m_gt.xscale), fabs(m_gt.yscale)); |
7290 | 1.60k | const double dfRotationShearTerm = |
7291 | 1.60k | std::max(fabs(m_gt.xrot), fabs(m_gt.yrot)); |
7292 | 1.60k | if (dfRotationShearTerm < 1e-5 * dfPixelSize || |
7293 | 630 | (m_bUseLib.test(PDFLIB_PDFIUM) && |
7294 | 0 | std::min(fabs(m_gt.xrot), fabs(m_gt.yrot)) < 1e-5 * dfPixelSize)) |
7295 | 978 | { |
7296 | 978 | dfLRX = |
7297 | 978 | m_gt.xorig + nRasterXSize * m_gt.xscale + nRasterYSize * m_gt.xrot; |
7298 | 978 | dfLRY = |
7299 | 978 | m_gt.yorig + nRasterXSize * m_gt.yrot + nRasterYSize * m_gt.yscale; |
7300 | 978 | m_gt.xscale = (dfLRX - m_gt.xorig) / nRasterXSize; |
7301 | 978 | m_gt.yscale = (dfLRY - m_gt.yorig) / nRasterYSize; |
7302 | 978 | m_gt.xrot = m_gt.yrot = 0; |
7303 | 978 | } |
7304 | | |
7305 | 1.60k | return TRUE; |
7306 | 2.02k | } |
7307 | | |
7308 | | /************************************************************************/ |
7309 | | /* GetSpatialRef() */ |
7310 | | /************************************************************************/ |
7311 | | |
7312 | | const OGRSpatialReference *PDFDataset::GetSpatialRef() const |
7313 | 33.0k | { |
7314 | 33.0k | const auto poSRS = GDALPamDataset::GetSpatialRef(); |
7315 | 33.0k | if (poSRS) |
7316 | 736 | return poSRS; |
7317 | | |
7318 | 32.2k | if (!m_oSRS.IsEmpty() && m_bGeoTransformValid) |
7319 | 570 | return &m_oSRS; |
7320 | 31.7k | return nullptr; |
7321 | 32.2k | } |
7322 | | |
7323 | | /************************************************************************/ |
7324 | | /* GetGeoTransform() */ |
7325 | | /************************************************************************/ |
7326 | | |
7327 | | CPLErr PDFDataset::GetGeoTransform(GDALGeoTransform >) const |
7328 | | |
7329 | 7.33k | { |
7330 | 7.33k | if (GDALPamDataset::GetGeoTransform(gt) == CE_None) |
7331 | 0 | { |
7332 | 0 | return CE_None; |
7333 | 0 | } |
7334 | | |
7335 | 7.33k | gt = m_gt; |
7336 | 7.33k | return ((m_bGeoTransformValid) ? CE_None : CE_Failure); |
7337 | 7.33k | } |
7338 | | |
7339 | | /************************************************************************/ |
7340 | | /* SetSpatialRef() */ |
7341 | | /************************************************************************/ |
7342 | | |
7343 | | CPLErr PDFDataset::SetSpatialRef(const OGRSpatialReference *poSRS) |
7344 | 0 | { |
7345 | 0 | if (eAccess == GA_ReadOnly) |
7346 | 0 | GDALPamDataset::SetSpatialRef(poSRS); |
7347 | |
|
7348 | 0 | m_oSRS.Clear(); |
7349 | 0 | if (poSRS) |
7350 | 0 | m_oSRS = *poSRS; |
7351 | 0 | m_bProjDirty = true; |
7352 | 0 | return CE_None; |
7353 | 0 | } |
7354 | | |
7355 | | /************************************************************************/ |
7356 | | /* SetGeoTransform() */ |
7357 | | /************************************************************************/ |
7358 | | |
7359 | | CPLErr PDFDataset::SetGeoTransform(const GDALGeoTransform >) |
7360 | 0 | { |
7361 | 0 | if (eAccess == GA_ReadOnly) |
7362 | 0 | GDALPamDataset::SetGeoTransform(gt); |
7363 | |
|
7364 | 0 | m_gt = gt; |
7365 | 0 | m_bGeoTransformValid = true; |
7366 | 0 | m_bProjDirty = true; |
7367 | | |
7368 | | /* Reset NEATLINE if not explicitly set by the user */ |
7369 | 0 | if (!m_bNeatLineDirty) |
7370 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7371 | 0 | return CE_None; |
7372 | 0 | } |
7373 | | |
7374 | | /************************************************************************/ |
7375 | | /* GetMetadataDomainList() */ |
7376 | | /************************************************************************/ |
7377 | | |
7378 | | char **PDFDataset::GetMetadataDomainList() |
7379 | 0 | { |
7380 | 0 | return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), |
7381 | 0 | TRUE, "xml:XMP", "LAYERS", |
7382 | 0 | "EMBEDDED_METADATA", nullptr); |
7383 | 0 | } |
7384 | | |
7385 | | /************************************************************************/ |
7386 | | /* GetMetadata() */ |
7387 | | /************************************************************************/ |
7388 | | |
7389 | | CSLConstList PDFDataset::GetMetadata(const char *pszDomain) |
7390 | 59.2k | { |
7391 | 59.2k | if (pszDomain != nullptr && EQUAL(pszDomain, "EMBEDDED_METADATA")) |
7392 | 0 | { |
7393 | 0 | char **papszRet = m_oMDMD_PDF.GetMetadata(pszDomain); |
7394 | 0 | if (papszRet) |
7395 | 0 | return papszRet; |
7396 | | |
7397 | 0 | GDALPDFObject *poCatalog = GetCatalog(); |
7398 | 0 | if (poCatalog == nullptr) |
7399 | 0 | return nullptr; |
7400 | 0 | GDALPDFObject *poFirstElt = |
7401 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]"); |
7402 | 0 | GDALPDFObject *poF = |
7403 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F"); |
7404 | |
|
7405 | 0 | if (poFirstElt == nullptr || |
7406 | 0 | poFirstElt->GetType() != PDFObjectType_String || |
7407 | 0 | poFirstElt->GetString() != "Metadata") |
7408 | 0 | return nullptr; |
7409 | 0 | if (poF == nullptr || poF->GetType() != PDFObjectType_Dictionary) |
7410 | 0 | return nullptr; |
7411 | 0 | GDALPDFStream *poStream = poF->GetStream(); |
7412 | 0 | if (poStream == nullptr) |
7413 | 0 | return nullptr; |
7414 | | |
7415 | 0 | char *apszMetadata[2] = {nullptr, nullptr}; |
7416 | 0 | apszMetadata[0] = poStream->GetBytes(); |
7417 | 0 | m_oMDMD_PDF.SetMetadata(apszMetadata, pszDomain); |
7418 | 0 | VSIFree(apszMetadata[0]); |
7419 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7420 | 0 | } |
7421 | 59.2k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7422 | 22.0k | { |
7423 | 22.0k | CSLConstList papszPAMMD = GDALPamDataset::GetMetadata(pszDomain); |
7424 | 108k | for (CSLConstList papszIter = papszPAMMD; papszIter && *papszIter; |
7425 | 86.7k | ++papszIter) |
7426 | 86.7k | { |
7427 | 86.7k | char *pszKey = nullptr; |
7428 | 86.7k | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7429 | 86.7k | if (pszKey && pszValue) |
7430 | 86.7k | { |
7431 | 86.7k | if (m_oMDMD_PDF.GetMetadataItem(pszKey, pszDomain) == nullptr) |
7432 | 28.9k | m_oMDMD_PDF.SetMetadataItem(pszKey, pszValue, pszDomain); |
7433 | 86.7k | } |
7434 | 86.7k | CPLFree(pszKey); |
7435 | 86.7k | } |
7436 | 22.0k | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7437 | 22.0k | } |
7438 | 37.2k | if (EQUAL(pszDomain, "LAYERS") || EQUAL(pszDomain, "xml:XMP") || |
7439 | 37.2k | EQUAL(pszDomain, "SUBDATASETS")) |
7440 | 0 | { |
7441 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7442 | 0 | } |
7443 | 37.2k | return GDALPamDataset::GetMetadata(pszDomain); |
7444 | 37.2k | } |
7445 | | |
7446 | | /************************************************************************/ |
7447 | | /* SetMetadata() */ |
7448 | | /************************************************************************/ |
7449 | | |
7450 | | CPLErr PDFDataset::SetMetadata(CSLConstList papszMetadata, |
7451 | | const char *pszDomain) |
7452 | 8.64k | { |
7453 | 8.64k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7454 | 53 | { |
7455 | 53 | char **papszMetadataDup = CSLDuplicate(papszMetadata); |
7456 | 53 | m_oMDMD_PDF.SetMetadata(nullptr, pszDomain); |
7457 | | |
7458 | 284 | for (char **papszIter = papszMetadataDup; papszIter && *papszIter; |
7459 | 231 | ++papszIter) |
7460 | 231 | { |
7461 | 231 | char *pszKey = nullptr; |
7462 | 231 | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7463 | 231 | if (pszKey && pszValue) |
7464 | 231 | { |
7465 | 231 | SetMetadataItem(pszKey, pszValue, pszDomain); |
7466 | 231 | } |
7467 | 231 | CPLFree(pszKey); |
7468 | 231 | } |
7469 | 53 | CSLDestroy(papszMetadataDup); |
7470 | 53 | return CE_None; |
7471 | 53 | } |
7472 | 8.59k | else if (EQUAL(pszDomain, "xml:XMP")) |
7473 | 5.03k | { |
7474 | 5.03k | m_bXMPDirty = true; |
7475 | 5.03k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7476 | 5.03k | } |
7477 | 3.56k | else if (EQUAL(pszDomain, "SUBDATASETS")) |
7478 | 3.56k | { |
7479 | 3.56k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7480 | 3.56k | } |
7481 | 0 | else |
7482 | 0 | { |
7483 | 0 | return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); |
7484 | 0 | } |
7485 | 8.64k | } |
7486 | | |
7487 | | /************************************************************************/ |
7488 | | /* GetMetadataItem() */ |
7489 | | /************************************************************************/ |
7490 | | |
7491 | | const char *PDFDataset::GetMetadataItem(const char *pszName, |
7492 | | const char *pszDomain) |
7493 | 51.8k | { |
7494 | 51.8k | if (pszDomain != nullptr && EQUAL(pszDomain, "_INTERNAL_") && |
7495 | 0 | pszName != nullptr && EQUAL(pszName, "PDF_LIB")) |
7496 | 0 | { |
7497 | 0 | if (m_bUseLib.test(PDFLIB_POPPLER)) |
7498 | 0 | return "POPPLER"; |
7499 | 0 | if (m_bUseLib.test(PDFLIB_PODOFO)) |
7500 | 0 | return "PODOFO"; |
7501 | 0 | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
7502 | 0 | return "PDFIUM"; |
7503 | 0 | } |
7504 | 51.8k | return CSLFetchNameValue(GetMetadata(pszDomain), pszName); |
7505 | 51.8k | } |
7506 | | |
7507 | | /************************************************************************/ |
7508 | | /* SetMetadataItem() */ |
7509 | | /************************************************************************/ |
7510 | | |
7511 | | CPLErr PDFDataset::SetMetadataItem(const char *pszName, const char *pszValue, |
7512 | | const char *pszDomain) |
7513 | 119k | { |
7514 | 119k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7515 | 62.7k | { |
7516 | 62.7k | if (EQUAL(pszName, "NEATLINE")) |
7517 | 2.55k | { |
7518 | 2.55k | const char *pszOldValue = |
7519 | 2.55k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7520 | 2.55k | if ((pszValue == nullptr && pszOldValue != nullptr) || |
7521 | 2.55k | (pszValue != nullptr && pszOldValue == nullptr) || |
7522 | 0 | (pszValue != nullptr && pszOldValue != nullptr && |
7523 | 0 | strcmp(pszValue, pszOldValue) != 0)) |
7524 | 2.55k | { |
7525 | 2.55k | m_bProjDirty = true; |
7526 | 2.55k | m_bNeatLineDirty = true; |
7527 | 2.55k | } |
7528 | 2.55k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7529 | 2.55k | } |
7530 | 60.1k | else |
7531 | 60.1k | { |
7532 | 60.1k | if (EQUAL(pszName, "AUTHOR") || EQUAL(pszName, "PRODUCER") || |
7533 | 51.1k | EQUAL(pszName, "CREATOR") || EQUAL(pszName, "CREATION_DATE") || |
7534 | 38.8k | EQUAL(pszName, "SUBJECT") || EQUAL(pszName, "TITLE") || |
7535 | 34.3k | EQUAL(pszName, "KEYWORDS")) |
7536 | 26.8k | { |
7537 | 26.8k | if (pszValue == nullptr) |
7538 | 0 | pszValue = ""; |
7539 | 26.8k | const char *pszOldValue = |
7540 | 26.8k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7541 | 26.8k | if (pszOldValue == nullptr || |
7542 | 0 | strcmp(pszValue, pszOldValue) != 0) |
7543 | 26.8k | { |
7544 | 26.8k | m_bInfoDirty = true; |
7545 | 26.8k | } |
7546 | 26.8k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7547 | 26.8k | pszDomain); |
7548 | 26.8k | } |
7549 | 33.3k | else if (EQUAL(pszName, "DPI")) |
7550 | 33.2k | { |
7551 | 33.2k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7552 | 33.2k | pszDomain); |
7553 | 33.2k | } |
7554 | 157 | else |
7555 | 157 | { |
7556 | 157 | m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7557 | 157 | return GDALPamDataset::SetMetadataItem(pszName, pszValue, |
7558 | 157 | pszDomain); |
7559 | 157 | } |
7560 | 60.1k | } |
7561 | 62.7k | } |
7562 | 56.4k | else if (EQUAL(pszDomain, "xml:XMP")) |
7563 | 0 | { |
7564 | 0 | m_bXMPDirty = true; |
7565 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7566 | 0 | } |
7567 | 56.4k | else if (EQUAL(pszDomain, "SUBDATASETS")) |
7568 | 0 | { |
7569 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7570 | 0 | } |
7571 | 56.4k | else |
7572 | 56.4k | { |
7573 | 56.4k | return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); |
7574 | 56.4k | } |
7575 | 119k | } |
7576 | | |
7577 | | /************************************************************************/ |
7578 | | /* GetGCPCount() */ |
7579 | | /************************************************************************/ |
7580 | | |
7581 | | int PDFDataset::GetGCPCount() |
7582 | 7.32k | { |
7583 | 7.32k | return m_nGCPCount; |
7584 | 7.32k | } |
7585 | | |
7586 | | /************************************************************************/ |
7587 | | /* GetGCPSpatialRef() */ |
7588 | | /************************************************************************/ |
7589 | | |
7590 | | const OGRSpatialReference *PDFDataset::GetGCPSpatialRef() const |
7591 | 7.32k | { |
7592 | 7.32k | if (!m_oSRS.IsEmpty() && m_nGCPCount != 0) |
7593 | 2 | return &m_oSRS; |
7594 | 7.32k | return nullptr; |
7595 | 7.32k | } |
7596 | | |
7597 | | /************************************************************************/ |
7598 | | /* GetGCPs() */ |
7599 | | /************************************************************************/ |
7600 | | |
7601 | | const GDAL_GCP *PDFDataset::GetGCPs() |
7602 | 7.32k | { |
7603 | 7.32k | return m_pasGCPList; |
7604 | 7.32k | } |
7605 | | |
7606 | | /************************************************************************/ |
7607 | | /* SetGCPs() */ |
7608 | | /************************************************************************/ |
7609 | | |
7610 | | CPLErr PDFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn, |
7611 | | const OGRSpatialReference *poSRS) |
7612 | 0 | { |
7613 | 0 | const char *pszGEO_ENCODING = |
7614 | 0 | CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); |
7615 | 0 | if (nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000")) |
7616 | 0 | { |
7617 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
7618 | 0 | "PDF driver only supports writing 4 GCPs when " |
7619 | 0 | "GDAL_PDF_GEO_ENCODING=ISO32000."); |
7620 | 0 | return CE_Failure; |
7621 | 0 | } |
7622 | | |
7623 | | /* Free previous GCPs */ |
7624 | 0 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
7625 | 0 | CPLFree(m_pasGCPList); |
7626 | | |
7627 | | /* Duplicate in GCPs */ |
7628 | 0 | m_nGCPCount = nGCPCountIn; |
7629 | 0 | m_pasGCPList = GDALDuplicateGCPs(m_nGCPCount, pasGCPListIn); |
7630 | |
|
7631 | 0 | m_oSRS.Clear(); |
7632 | 0 | if (poSRS) |
7633 | 0 | m_oSRS = *poSRS; |
7634 | |
|
7635 | 0 | m_bProjDirty = true; |
7636 | | |
7637 | | /* Reset NEATLINE if not explicitly set by the user */ |
7638 | 0 | if (!m_bNeatLineDirty) |
7639 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7640 | |
|
7641 | 0 | return CE_None; |
7642 | 0 | } |
7643 | | |
7644 | | #endif // #ifdef HAVE_PDF_READ_SUPPORT |
7645 | | |
7646 | | /************************************************************************/ |
7647 | | /* GDALPDFOpen() */ |
7648 | | /************************************************************************/ |
7649 | | |
7650 | | GDALDataset *GDALPDFOpen( |
7651 | | #ifdef HAVE_PDF_READ_SUPPORT |
7652 | | const char *pszFilename, GDALAccess eAccess |
7653 | | #else |
7654 | | CPL_UNUSED const char *pszFilename, CPL_UNUSED GDALAccess eAccess |
7655 | | #endif |
7656 | | ) |
7657 | 53 | { |
7658 | 53 | #ifdef HAVE_PDF_READ_SUPPORT |
7659 | 53 | GDALOpenInfo oOpenInfo(pszFilename, eAccess); |
7660 | 53 | return PDFDataset::Open(&oOpenInfo); |
7661 | | #else |
7662 | | return nullptr; |
7663 | | #endif |
7664 | 53 | } |
7665 | | |
7666 | | /************************************************************************/ |
7667 | | /* GDALPDFUnloadDriver() */ |
7668 | | /************************************************************************/ |
7669 | | |
7670 | | static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver *poDriver) |
7671 | 0 | { |
7672 | 0 | #ifdef HAVE_POPPLER |
7673 | 0 | if (hGlobalParamsMutex != nullptr) |
7674 | 0 | CPLDestroyMutex(hGlobalParamsMutex); |
7675 | 0 | #endif |
7676 | | #ifdef HAVE_PDFIUM |
7677 | | if (PDFDataset::g_bPdfiumInit) |
7678 | | { |
7679 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
7680 | | // Destroy every loaded document or page |
7681 | | TMapPdfiumDatasets::iterator itDoc; |
7682 | | TMapPdfiumPages::iterator itPage; |
7683 | | for (itDoc = g_mPdfiumDatasets.begin(); |
7684 | | itDoc != g_mPdfiumDatasets.end(); ++itDoc) |
7685 | | { |
7686 | | TPdfiumDocumentStruct *pDoc = itDoc->second; |
7687 | | for (itPage = pDoc->pages.begin(); itPage != pDoc->pages.end(); |
7688 | | ++itPage) |
7689 | | { |
7690 | | TPdfiumPageStruct *pPage = itPage->second; |
7691 | | |
7692 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, |
7693 | | PDFIUM_MUTEX_TIMEOUT); |
7694 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), |
7695 | | PDFIUM_MUTEX_TIMEOUT); |
7696 | | CPLReleaseMutex(pPage->readMutex); |
7697 | | CPLDestroyMutex(pPage->readMutex); |
7698 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
7699 | | delete pPage; |
7700 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
7701 | | } // ~ foreach page |
7702 | | |
7703 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
7704 | | CPLFree(pDoc->filename); |
7705 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
7706 | | delete pDoc->psFileAccess; |
7707 | | pDoc->pages.clear(); |
7708 | | |
7709 | | delete pDoc; |
7710 | | } // ~ foreach document |
7711 | | g_mPdfiumDatasets.clear(); |
7712 | | FPDF_DestroyLibrary(); |
7713 | | PDFDataset::g_bPdfiumInit = FALSE; |
7714 | | |
7715 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
7716 | | |
7717 | | if (g_oPdfiumReadMutex) |
7718 | | CPLDestroyMutex(g_oPdfiumReadMutex); |
7719 | | CPLDestroyMutex(g_oPdfiumLoadDocMutex); |
7720 | | } |
7721 | | #endif |
7722 | 0 | } |
7723 | | |
7724 | | /************************************************************************/ |
7725 | | /* PDFSanitizeLayerName() */ |
7726 | | /************************************************************************/ |
7727 | | |
7728 | | CPLString PDFSanitizeLayerName(const char *pszName) |
7729 | 627k | { |
7730 | 627k | if (!CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES"))) |
7731 | 0 | return pszName; |
7732 | | |
7733 | 627k | CPLString osName; |
7734 | 48.3M | for (int i = 0; pszName[i] != '\0'; i++) |
7735 | 47.6M | { |
7736 | 47.6M | if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',') |
7737 | 4.66M | osName += "_"; |
7738 | 43.0M | else if (pszName[i] != '"') |
7739 | 42.9M | osName += pszName[i]; |
7740 | 47.6M | } |
7741 | 627k | if (osName.empty()) |
7742 | 492 | osName = "unnamed"; |
7743 | 627k | return osName; |
7744 | 627k | } |
7745 | | |
7746 | | /************************************************************************/ |
7747 | | /* GDALPDFListLayersAlgorithm */ |
7748 | | /************************************************************************/ |
7749 | | |
7750 | | #ifdef HAVE_PDF_READ_SUPPORT |
7751 | | |
7752 | | class GDALPDFListLayersAlgorithm final : public GDALAlgorithm |
7753 | | { |
7754 | | public: |
7755 | | GDALPDFListLayersAlgorithm() |
7756 | 0 | : GDALAlgorithm("list-layers", |
7757 | 0 | std::string("List layers of a PDF dataset"), |
7758 | 0 | "/drivers/raster/pdf.html") |
7759 | 0 | { |
7760 | 0 | AddInputDatasetArg(&m_dataset, GDAL_OF_RASTER | GDAL_OF_VECTOR); |
7761 | 0 | AddOutputFormatArg(&m_format).SetDefault(m_format).SetChoices("json", |
7762 | 0 | "text"); |
7763 | 0 | AddOutputStringArg(&m_output); |
7764 | 0 | } |
7765 | | |
7766 | | protected: |
7767 | | bool RunImpl(GDALProgressFunc, void *) override; |
7768 | | |
7769 | | private: |
7770 | | GDALArgDatasetValue m_dataset{}; |
7771 | | std::string m_format = "json"; |
7772 | | std::string m_output{}; |
7773 | | }; |
7774 | | |
7775 | | bool GDALPDFListLayersAlgorithm::RunImpl(GDALProgressFunc, void *) |
7776 | 0 | { |
7777 | 0 | auto poDS = dynamic_cast<PDFDataset *>(m_dataset.GetDatasetRef()); |
7778 | 0 | if (!poDS) |
7779 | 0 | { |
7780 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "%s is not a PDF", |
7781 | 0 | m_dataset.GetName().c_str()); |
7782 | 0 | return false; |
7783 | 0 | } |
7784 | 0 | if (m_format == "json") |
7785 | 0 | { |
7786 | 0 | CPLJSonStreamingWriter oWriter(nullptr, nullptr); |
7787 | 0 | oWriter.StartArray(); |
7788 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7789 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7790 | 0 | { |
7791 | 0 | CPL_IGNORE_RET_VAL(key); |
7792 | 0 | oWriter.Add(value); |
7793 | 0 | } |
7794 | 0 | oWriter.EndArray(); |
7795 | 0 | m_output = oWriter.GetString(); |
7796 | 0 | m_output += '\n'; |
7797 | 0 | } |
7798 | 0 | else |
7799 | 0 | { |
7800 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7801 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7802 | 0 | { |
7803 | 0 | CPL_IGNORE_RET_VAL(key); |
7804 | 0 | m_output += value; |
7805 | 0 | m_output += '\n'; |
7806 | 0 | } |
7807 | 0 | } |
7808 | 0 | return true; |
7809 | 0 | } |
7810 | | |
7811 | | /************************************************************************/ |
7812 | | /* GDALPDFInstantiateAlgorithm() */ |
7813 | | /************************************************************************/ |
7814 | | |
7815 | | static GDALAlgorithm * |
7816 | | GDALPDFInstantiateAlgorithm(const std::vector<std::string> &aosPath) |
7817 | 0 | { |
7818 | 0 | if (aosPath.size() == 1 && aosPath[0] == "list-layers") |
7819 | 0 | { |
7820 | 0 | return std::make_unique<GDALPDFListLayersAlgorithm>().release(); |
7821 | 0 | } |
7822 | 0 | else |
7823 | 0 | { |
7824 | 0 | return nullptr; |
7825 | 0 | } |
7826 | 0 | } |
7827 | | |
7828 | | #endif // HAVE_PDF_READ_SUPPORT |
7829 | | |
7830 | | /************************************************************************/ |
7831 | | /* GDALRegister_PDF() */ |
7832 | | /************************************************************************/ |
7833 | | |
7834 | | void GDALRegister_PDF() |
7835 | | |
7836 | 22 | { |
7837 | 22 | if (!GDAL_CHECK_VERSION("PDF driver")) |
7838 | 0 | return; |
7839 | | |
7840 | 22 | if (GDALGetDriverByName(DRIVER_NAME) != nullptr) |
7841 | 0 | return; |
7842 | | |
7843 | 22 | GDALDriver *poDriver = new GDALDriver(); |
7844 | 22 | PDFDriverSetCommonMetadata(poDriver); |
7845 | | |
7846 | 22 | #ifdef HAVE_PDF_READ_SUPPORT |
7847 | 22 | poDriver->pfnOpen = PDFDataset::OpenWrapper; |
7848 | 22 | poDriver->pfnInstantiateAlgorithm = GDALPDFInstantiateAlgorithm; |
7849 | 22 | #endif // HAVE_PDF_READ_SUPPORT |
7850 | | |
7851 | 22 | poDriver->pfnCreateCopy = GDALPDFCreateCopy; |
7852 | 22 | poDriver->pfnCreate = PDFWritableVectorDataset::Create; |
7853 | 22 | poDriver->pfnUnloadDriver = GDALPDFUnloadDriver; |
7854 | | |
7855 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
7856 | 22 | } |