/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 <cassert> |
42 | | #include <limits> |
43 | | #include <set> |
44 | | |
45 | | #ifdef HAVE_PDFIUM |
46 | | // To be able to use |
47 | | // https://github.com/rouault/pdfium_build_gdal_3_5/releases/download/v1_pdfium_5106/install-win10-vs2019-x64-rev5106.zip |
48 | | // with newer Visual Studio versions. |
49 | | // Trick from https://github.com/conan-io/conan-center-index/issues/4826 |
50 | | #if _MSC_VER >= 1932 // Visual Studio 2022 version 17.2+ |
51 | | #pragma comment( \ |
52 | | linker, \ |
53 | | "/alternatename:__imp___std_init_once_complete=__imp_InitOnceComplete") |
54 | | #pragma comment( \ |
55 | | linker, \ |
56 | | "/alternatename:__imp___std_init_once_begin_initialize=__imp_InitOnceBeginInitialize") |
57 | | #endif |
58 | | #endif |
59 | | |
60 | | /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport |
61 | | * -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */ |
62 | | |
63 | | #ifdef HAVE_PDF_READ_SUPPORT |
64 | | |
65 | | static double Get(GDALPDFObject *poObj, int nIndice = -1); |
66 | | |
67 | | #ifdef HAVE_POPPLER |
68 | | |
69 | | static CPLMutex *hGlobalParamsMutex = nullptr; |
70 | | |
71 | | /************************************************************************/ |
72 | | /* GDALPDFOutputDev */ |
73 | | /************************************************************************/ |
74 | | |
75 | | class GDALPDFOutputDev final : public SplashOutputDev |
76 | | { |
77 | | private: |
78 | | int bEnableVector; |
79 | | int bEnableText; |
80 | | int bEnableBitmap; |
81 | | |
82 | | void skipBytes(Stream *str, int width, int height, int nComps, int nBits) |
83 | 0 | { |
84 | 0 | int nVals = width * nComps; |
85 | 0 | int nLineSize = (nVals * nBits + 7) >> 3; |
86 | 0 | int nBytes = nLineSize * height; |
87 | 0 | for (int i = 0; i < nBytes; i++) |
88 | 0 | { |
89 | 0 | if (str->getChar() == EOF) |
90 | 0 | break; |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | public: |
95 | | GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, |
96 | | bool reverseVideoA, SplashColorPtr paperColorA) |
97 | 7.20k | : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, |
98 | 7.20k | paperColorA), |
99 | 7.20k | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE) |
100 | 7.20k | { |
101 | 7.20k | } |
102 | | |
103 | | void SetEnableVector(int bFlag) |
104 | 14.4k | { |
105 | 14.4k | bEnableVector = bFlag; |
106 | 14.4k | } |
107 | | |
108 | | void SetEnableText(int bFlag) |
109 | 7.20k | { |
110 | 7.20k | bEnableText = bFlag; |
111 | 7.20k | } |
112 | | |
113 | | void SetEnableBitmap(int bFlag) |
114 | 14.4k | { |
115 | 14.4k | bEnableBitmap = bFlag; |
116 | 14.4k | } |
117 | | |
118 | | void startPage(int pageNum, GfxState *state, XRef *xrefIn) override; |
119 | | |
120 | | void stroke(GfxState *state) override |
121 | 1.18M | { |
122 | 1.18M | if (bEnableVector) |
123 | 1.18M | SplashOutputDev::stroke(state); |
124 | 1.18M | } |
125 | | |
126 | | void fill(GfxState *state) override |
127 | 8.40M | { |
128 | 8.40M | if (bEnableVector) |
129 | 8.40M | SplashOutputDev::fill(state); |
130 | 8.40M | } |
131 | | |
132 | | void eoFill(GfxState *state) override |
133 | 4.16k | { |
134 | 4.16k | if (bEnableVector) |
135 | 4.16k | SplashOutputDev::eoFill(state); |
136 | 4.16k | } |
137 | | |
138 | | virtual void drawChar(GfxState *state, double x, double y, double dx, |
139 | | double dy, double originX, double originY, |
140 | | CharCode code, int nBytes, const Unicode *u, |
141 | | int uLen) override |
142 | 1.37M | { |
143 | 1.37M | if (bEnableText) |
144 | 0 | SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY, |
145 | 0 | code, nBytes, u, uLen); |
146 | 1.37M | } |
147 | | |
148 | | void beginTextObject(GfxState *state) override |
149 | 151k | { |
150 | 151k | if (bEnableText) |
151 | 0 | SplashOutputDev::beginTextObject(state); |
152 | 151k | } |
153 | | |
154 | | void endTextObject(GfxState *state) override |
155 | 153k | { |
156 | 153k | if (bEnableText) |
157 | 0 | SplashOutputDev::endTextObject(state); |
158 | 153k | } |
159 | | |
160 | | virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, |
161 | | int width, int height, bool invert, |
162 | | bool interpolate, bool inlineImg) override |
163 | 30.2k | { |
164 | 30.2k | if (bEnableBitmap) |
165 | 30.2k | SplashOutputDev::drawImageMask(state, ref, str, width, height, |
166 | 30.2k | invert, interpolate, inlineImg); |
167 | 0 | else |
168 | 0 | { |
169 | 0 | VSIPDFFileStream::resetNoCheckReturnValue(str); |
170 | 0 | if (inlineImg) |
171 | 0 | { |
172 | 0 | skipBytes(str, width, height, 1, 1); |
173 | 0 | } |
174 | 0 | str->close(); |
175 | 0 | } |
176 | 30.2k | } |
177 | | |
178 | | virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref, |
179 | | Stream *str, int width, int height, |
180 | | bool invert, bool inlineImg, |
181 | | double *baseMatrix) override |
182 | 1.96M | { |
183 | 1.96M | if (bEnableBitmap) |
184 | 1.96M | SplashOutputDev::setSoftMaskFromImageMask( |
185 | 1.96M | state, ref, str, width, height, invert, inlineImg, baseMatrix); |
186 | 0 | else |
187 | 0 | str->close(); |
188 | 1.96M | } |
189 | | |
190 | | virtual void unsetSoftMaskFromImageMask(GfxState *state, |
191 | | double *baseMatrix) override |
192 | 1.96M | { |
193 | 1.96M | if (bEnableBitmap) |
194 | 1.96M | SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix); |
195 | 1.96M | } |
196 | | |
197 | | virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, |
198 | | int height, GfxImageColorMap *colorMap, |
199 | | bool interpolate, const int *maskColors, |
200 | | bool inlineImg) override |
201 | 3.44k | { |
202 | 3.44k | if (bEnableBitmap) |
203 | 3.44k | SplashOutputDev::drawImage(state, ref, str, width, height, colorMap, |
204 | 3.44k | interpolate, maskColors, inlineImg); |
205 | 0 | else |
206 | 0 | { |
207 | 0 | VSIPDFFileStream::resetNoCheckReturnValue(str); |
208 | 0 | if (inlineImg) |
209 | 0 | { |
210 | 0 | skipBytes(str, width, height, colorMap->getNumPixelComps(), |
211 | 0 | colorMap->getBits()); |
212 | 0 | } |
213 | 0 | str->close(); |
214 | 0 | } |
215 | 3.44k | } |
216 | | |
217 | | virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, |
218 | | int width, int height, |
219 | | GfxImageColorMap *colorMap, bool interpolate, |
220 | | Stream *maskStr, int maskWidth, int maskHeight, |
221 | | bool maskInvert, bool maskInterpolate) override |
222 | 76 | { |
223 | 76 | if (bEnableBitmap) |
224 | 76 | SplashOutputDev::drawMaskedImage( |
225 | 76 | state, ref, str, width, height, colorMap, interpolate, maskStr, |
226 | 76 | maskWidth, maskHeight, maskInvert, maskInterpolate); |
227 | 0 | else |
228 | 0 | str->close(); |
229 | 76 | } |
230 | | |
231 | | virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, |
232 | | int width, int height, |
233 | | GfxImageColorMap *colorMap, |
234 | | bool interpolate, Stream *maskStr, |
235 | | int maskWidth, int maskHeight, |
236 | | GfxImageColorMap *maskColorMap, |
237 | | bool maskInterpolate) override |
238 | 3.03k | { |
239 | 3.03k | if (bEnableBitmap) |
240 | 3.03k | { |
241 | 3.03k | if (maskColorMap->getBits() <= |
242 | 3.03k | 0) /* workaround poppler bug (robustness) */ |
243 | 0 | { |
244 | 0 | str->close(); |
245 | 0 | return; |
246 | 0 | } |
247 | 3.03k | SplashOutputDev::drawSoftMaskedImage( |
248 | 3.03k | state, ref, str, width, height, colorMap, interpolate, maskStr, |
249 | 3.03k | maskWidth, maskHeight, maskColorMap, maskInterpolate); |
250 | 3.03k | } |
251 | 0 | else |
252 | 0 | str->close(); |
253 | 3.03k | } |
254 | | }; |
255 | | |
256 | | void GDALPDFOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefIn) |
257 | 7.20k | { |
258 | 7.20k | SplashOutputDev::startPage(pageNum, state, xrefIn); |
259 | 7.20k | SplashBitmap *poBitmap = getBitmap(); |
260 | 7.20k | memset(poBitmap->getDataPtr(), 255, |
261 | 7.20k | static_cast<size_t>(poBitmap->getRowSize()) * poBitmap->getHeight()); |
262 | 7.20k | } |
263 | | |
264 | | #endif // ~ HAVE_POPPLER |
265 | | |
266 | | /************************************************************************/ |
267 | | /* Dump routines */ |
268 | | /************************************************************************/ |
269 | | |
270 | | class GDALPDFDumper |
271 | | { |
272 | | private: |
273 | | FILE *f = nullptr; |
274 | | const int nDepthLimit; |
275 | | std::set<int> aoSetObjectExplored{}; |
276 | | const bool bDumpParent; |
277 | | |
278 | | void DumpSimplified(GDALPDFObject *poObj); |
279 | | |
280 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFDumper) |
281 | | |
282 | | public: |
283 | | GDALPDFDumper(const char *pszFilename, const char *pszDumpFile, |
284 | | int nDepthLimitIn = -1) |
285 | 0 | : nDepthLimit(nDepthLimitIn), |
286 | 0 | bDumpParent(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE")) |
287 | 0 | { |
288 | 0 | if (strcmp(pszDumpFile, "stderr") == 0) |
289 | 0 | f = stderr; |
290 | 0 | else if (EQUAL(pszDumpFile, "YES")) |
291 | 0 | f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), |
292 | 0 | "wt"); |
293 | 0 | else |
294 | 0 | f = fopen(pszDumpFile, "wt"); |
295 | 0 | if (f == nullptr) |
296 | 0 | f = stderr; |
297 | 0 | } |
298 | | |
299 | | ~GDALPDFDumper() |
300 | 0 | { |
301 | 0 | if (f != stderr) |
302 | 0 | fclose(f); |
303 | 0 | } |
304 | | |
305 | | void Dump(GDALPDFObject *poObj, int nDepth = 0); |
306 | | void Dump(GDALPDFDictionary *poDict, int nDepth = 0); |
307 | | void Dump(GDALPDFArray *poArray, int nDepth = 0); |
308 | | }; |
309 | | |
310 | | void GDALPDFDumper::Dump(GDALPDFArray *poArray, int nDepth) |
311 | 0 | { |
312 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
313 | 0 | return; |
314 | | |
315 | 0 | int nLength = poArray->GetLength(); |
316 | 0 | int i; |
317 | 0 | CPLString osIndent; |
318 | 0 | for (i = 0; i < nDepth; i++) |
319 | 0 | osIndent += " "; |
320 | 0 | for (i = 0; i < nLength; i++) |
321 | 0 | { |
322 | 0 | fprintf(f, "%sItem[%d]:", osIndent.c_str(), i); |
323 | 0 | GDALPDFObject *poObj = nullptr; |
324 | 0 | if ((poObj = poArray->Get(i)) != nullptr) |
325 | 0 | { |
326 | 0 | if (poObj->GetType() == PDFObjectType_String || |
327 | 0 | poObj->GetType() == PDFObjectType_Null || |
328 | 0 | poObj->GetType() == PDFObjectType_Bool || |
329 | 0 | poObj->GetType() == PDFObjectType_Int || |
330 | 0 | poObj->GetType() == PDFObjectType_Real || |
331 | 0 | poObj->GetType() == PDFObjectType_Name) |
332 | 0 | { |
333 | 0 | fprintf(f, " "); |
334 | 0 | DumpSimplified(poObj); |
335 | 0 | fprintf(f, "\n"); |
336 | 0 | } |
337 | 0 | else |
338 | 0 | { |
339 | 0 | fprintf(f, "\n"); |
340 | 0 | Dump(poObj, nDepth + 1); |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | | void GDALPDFDumper::DumpSimplified(GDALPDFObject *poObj) |
347 | 0 | { |
348 | 0 | switch (poObj->GetType()) |
349 | 0 | { |
350 | 0 | case PDFObjectType_String: |
351 | 0 | fprintf(f, "%s (string)", poObj->GetString().c_str()); |
352 | 0 | break; |
353 | | |
354 | 0 | case PDFObjectType_Null: |
355 | 0 | fprintf(f, "null"); |
356 | 0 | break; |
357 | | |
358 | 0 | case PDFObjectType_Bool: |
359 | 0 | fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false"); |
360 | 0 | break; |
361 | | |
362 | 0 | case PDFObjectType_Int: |
363 | 0 | fprintf(f, "%d (int)", poObj->GetInt()); |
364 | 0 | break; |
365 | | |
366 | 0 | case PDFObjectType_Real: |
367 | 0 | fprintf(f, "%f (real)", poObj->GetReal()); |
368 | 0 | break; |
369 | | |
370 | 0 | case PDFObjectType_Name: |
371 | 0 | fprintf(f, "%s (name)", poObj->GetName().c_str()); |
372 | 0 | break; |
373 | | |
374 | 0 | default: |
375 | 0 | fprintf(f, "unknown !"); |
376 | 0 | break; |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | void GDALPDFDumper::Dump(GDALPDFObject *poObj, int nDepth) |
381 | 0 | { |
382 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
383 | 0 | return; |
384 | | |
385 | 0 | int i; |
386 | 0 | CPLString osIndent; |
387 | 0 | for (i = 0; i < nDepth; i++) |
388 | 0 | osIndent += " "; |
389 | 0 | fprintf(f, "%sType = %s", osIndent.c_str(), poObj->GetTypeName()); |
390 | 0 | int nRefNum = poObj->GetRefNum().toInt(); |
391 | 0 | if (nRefNum != 0) |
392 | 0 | fprintf(f, ", Num = %d, Gen = %d", nRefNum, poObj->GetRefGen()); |
393 | 0 | fprintf(f, "\n"); |
394 | |
|
395 | 0 | if (nRefNum != 0) |
396 | 0 | { |
397 | 0 | if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end()) |
398 | 0 | return; |
399 | 0 | aoSetObjectExplored.insert(nRefNum); |
400 | 0 | } |
401 | | |
402 | 0 | switch (poObj->GetType()) |
403 | 0 | { |
404 | 0 | case PDFObjectType_Array: |
405 | 0 | Dump(poObj->GetArray(), nDepth + 1); |
406 | 0 | break; |
407 | | |
408 | 0 | case PDFObjectType_Dictionary: |
409 | 0 | Dump(poObj->GetDictionary(), nDepth + 1); |
410 | 0 | break; |
411 | | |
412 | 0 | case PDFObjectType_String: |
413 | 0 | case PDFObjectType_Null: |
414 | 0 | case PDFObjectType_Bool: |
415 | 0 | case PDFObjectType_Int: |
416 | 0 | case PDFObjectType_Real: |
417 | 0 | case PDFObjectType_Name: |
418 | 0 | fprintf(f, "%s", osIndent.c_str()); |
419 | 0 | DumpSimplified(poObj); |
420 | 0 | fprintf(f, "\n"); |
421 | 0 | break; |
422 | | |
423 | 0 | default: |
424 | 0 | fprintf(f, "%s", osIndent.c_str()); |
425 | 0 | fprintf(f, "unknown !\n"); |
426 | 0 | break; |
427 | 0 | } |
428 | | |
429 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
430 | 0 | if (poStream != nullptr) |
431 | 0 | { |
432 | 0 | fprintf(f, |
433 | 0 | "%sHas stream (" CPL_FRMT_GIB |
434 | 0 | " uncompressed bytes, " CPL_FRMT_GIB " raw bytes)\n", |
435 | 0 | osIndent.c_str(), static_cast<GIntBig>(poStream->GetLength()), |
436 | 0 | static_cast<GIntBig>(poStream->GetRawLength())); |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | void GDALPDFDumper::Dump(GDALPDFDictionary *poDict, int nDepth) |
441 | 0 | { |
442 | 0 | if (nDepthLimit >= 0 && nDepth > nDepthLimit) |
443 | 0 | return; |
444 | | |
445 | 0 | CPLString osIndent; |
446 | 0 | for (int i = 0; i < nDepth; i++) |
447 | 0 | osIndent += " "; |
448 | 0 | int i = 0; |
449 | 0 | const auto &oMap = poDict->GetValues(); |
450 | 0 | for (const auto &[osKey, poObj] : oMap) |
451 | 0 | { |
452 | 0 | fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, osKey.c_str()); |
453 | 0 | ++i; |
454 | 0 | if (osKey == "Parent" && !bDumpParent) |
455 | 0 | { |
456 | 0 | if (poObj->GetRefNum().toBool()) |
457 | 0 | fprintf(f, ", Num = %d, Gen = %d", poObj->GetRefNum().toInt(), |
458 | 0 | poObj->GetRefGen()); |
459 | 0 | fprintf(f, "\n"); |
460 | 0 | continue; |
461 | 0 | } |
462 | 0 | if (poObj != nullptr) |
463 | 0 | { |
464 | 0 | if (poObj->GetType() == PDFObjectType_String || |
465 | 0 | poObj->GetType() == PDFObjectType_Null || |
466 | 0 | poObj->GetType() == PDFObjectType_Bool || |
467 | 0 | poObj->GetType() == PDFObjectType_Int || |
468 | 0 | poObj->GetType() == PDFObjectType_Real || |
469 | 0 | poObj->GetType() == PDFObjectType_Name) |
470 | 0 | { |
471 | 0 | fprintf(f, " = "); |
472 | 0 | DumpSimplified(poObj); |
473 | 0 | fprintf(f, "\n"); |
474 | 0 | } |
475 | 0 | else |
476 | 0 | { |
477 | 0 | fprintf(f, "\n"); |
478 | 0 | Dump(poObj, nDepth + 1); |
479 | 0 | } |
480 | 0 | } |
481 | 0 | } |
482 | 0 | } |
483 | | |
484 | | /************************************************************************/ |
485 | | /* PDFRasterBand() */ |
486 | | /************************************************************************/ |
487 | | |
488 | | PDFRasterBand::PDFRasterBand(PDFDataset *poDSIn, int nBandIn, |
489 | | int nResolutionLevelIn) |
490 | 95.9k | : nResolutionLevel(nResolutionLevelIn) |
491 | 95.9k | { |
492 | 95.9k | poDS = poDSIn; |
493 | 95.9k | nBand = nBandIn; |
494 | | |
495 | 95.9k | eDataType = GDT_UInt8; |
496 | | |
497 | 95.9k | if (nResolutionLevel > 0) |
498 | 0 | { |
499 | 0 | nBlockXSize = 256; |
500 | 0 | nBlockYSize = 256; |
501 | 0 | poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
502 | 0 | } |
503 | 95.9k | else if (poDSIn->m_nBlockXSize) |
504 | 55.3k | { |
505 | 55.3k | nBlockXSize = poDSIn->m_nBlockXSize; |
506 | 55.3k | nBlockYSize = poDSIn->m_nBlockYSize; |
507 | 55.3k | poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
508 | 55.3k | } |
509 | 40.5k | else if (poDSIn->GetRasterXSize() < |
510 | 40.5k | 64 * 1024 * 1024 / poDSIn->GetRasterYSize()) |
511 | 40.4k | { |
512 | 40.4k | nBlockXSize = poDSIn->GetRasterXSize(); |
513 | 40.4k | nBlockYSize = 1; |
514 | 40.4k | } |
515 | 93 | else |
516 | 93 | { |
517 | 93 | nBlockXSize = std::min(1024, poDSIn->GetRasterXSize()); |
518 | 93 | nBlockYSize = std::min(1024, poDSIn->GetRasterYSize()); |
519 | 93 | poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
520 | 93 | } |
521 | 95.9k | } |
522 | | |
523 | | /************************************************************************/ |
524 | | /* InitOverviews() */ |
525 | | /************************************************************************/ |
526 | | |
527 | | void PDFDataset::InitOverviews() |
528 | 26.2k | { |
529 | | #ifdef HAVE_PDFIUM |
530 | | // Only if used pdfium, make "arbitrary overviews" |
531 | | // Blocks are 256x256 |
532 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
533 | | m_apoOvrDSBackup.empty()) |
534 | | { |
535 | | int nXSize = nRasterXSize; |
536 | | int nYSize = nRasterYSize; |
537 | | constexpr int minSize = 256; |
538 | | int nDiscard = 1; |
539 | | while (nXSize > minSize || nYSize > minSize) |
540 | | { |
541 | | nXSize = (nXSize + 1) / 2; |
542 | | nYSize = (nYSize + 1) / 2; |
543 | | |
544 | | auto poOvrDS = std::make_unique<PDFDataset>(this, nXSize, nYSize); |
545 | | |
546 | | for (int i = 0; i < nBands; i++) |
547 | | poOvrDS->SetBand( |
548 | | i + 1, new PDFRasterBand(poOvrDS.get(), i + 1, nDiscard)); |
549 | | |
550 | | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
551 | | ++nDiscard; |
552 | | } |
553 | | } |
554 | | #endif |
555 | 26.2k | #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) |
556 | 26.2k | if (!m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() && |
557 | 7.23k | m_apoOvrDSBackup.empty() && m_osUserPwd != "ASK_INTERACTIVE") |
558 | 7.23k | { |
559 | 7.23k | int nXSize = nRasterXSize; |
560 | 7.23k | int nYSize = nRasterYSize; |
561 | 7.23k | constexpr int minSize = 256; |
562 | 7.23k | double dfDPI = m_dfDPI; |
563 | 25.6k | while (nXSize > minSize || nYSize > minSize) |
564 | 18.4k | { |
565 | 18.4k | nXSize = (nXSize + 1) / 2; |
566 | 18.4k | nYSize = (nYSize + 1) / 2; |
567 | 18.4k | dfDPI /= 2; |
568 | | |
569 | 18.4k | GDALOpenInfo oOpenInfo(GetDescription(), GA_ReadOnly); |
570 | 18.4k | CPLStringList aosOpenOptions(CSLDuplicate(papszOpenOptions)); |
571 | 18.4k | aosOpenOptions.SetNameValue("DPI", CPLSPrintf("%g", dfDPI)); |
572 | 18.4k | aosOpenOptions.SetNameValue("BANDS", CPLSPrintf("%d", nBands)); |
573 | 18.4k | aosOpenOptions.SetNameValue("@OPEN_FOR_OVERVIEW", "YES"); |
574 | 18.4k | if (!m_osUserPwd.empty()) |
575 | 0 | aosOpenOptions.SetNameValue("USER_PWD", m_osUserPwd.c_str()); |
576 | 18.4k | oOpenInfo.papszOpenOptions = aosOpenOptions.List(); |
577 | 18.4k | auto poOvrDS = std::unique_ptr<PDFDataset>(Open(&oOpenInfo)); |
578 | 18.4k | if (!poOvrDS || poOvrDS->nBands != nBands) |
579 | 18 | break; |
580 | 18.4k | poOvrDS->m_bIsOvrDS = true; |
581 | 18.4k | m_apoOvrDS.emplace_back(std::move(poOvrDS)); |
582 | 18.4k | } |
583 | 7.23k | } |
584 | 26.2k | #endif |
585 | 26.2k | } |
586 | | |
587 | | /************************************************************************/ |
588 | | /* GetColorInterpretation() */ |
589 | | /************************************************************************/ |
590 | | |
591 | | GDALColorInterp PDFRasterBand::GetColorInterpretation() |
592 | 0 | { |
593 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
594 | 0 | if (poGDS->nBands == 1) |
595 | 0 | return GCI_GrayIndex; |
596 | 0 | else |
597 | 0 | return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1)); |
598 | 0 | } |
599 | | |
600 | | /************************************************************************/ |
601 | | /* GetOverviewCount() */ |
602 | | /************************************************************************/ |
603 | | |
604 | | int PDFRasterBand::GetOverviewCount() |
605 | 27.7k | { |
606 | 27.7k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
607 | 27.7k | if (poGDS->m_bIsOvrDS) |
608 | 0 | return 0; |
609 | 27.7k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
610 | 1.48k | return GDALPamRasterBand::GetOverviewCount(); |
611 | 26.2k | else |
612 | 26.2k | { |
613 | 26.2k | poGDS->InitOverviews(); |
614 | 26.2k | return static_cast<int>(poGDS->m_apoOvrDS.size()); |
615 | 26.2k | } |
616 | 27.7k | } |
617 | | |
618 | | /************************************************************************/ |
619 | | /* GetOverview() */ |
620 | | /************************************************************************/ |
621 | | |
622 | | GDALRasterBand *PDFRasterBand::GetOverview(int iOverviewIndex) |
623 | 19.3k | { |
624 | 19.3k | if (GDALPamRasterBand::GetOverviewCount() > 0) |
625 | 734 | return GDALPamRasterBand::GetOverview(iOverviewIndex); |
626 | | |
627 | 18.5k | else if (iOverviewIndex < 0 || iOverviewIndex >= GetOverviewCount()) |
628 | 0 | return nullptr; |
629 | 18.5k | else |
630 | 18.5k | { |
631 | 18.5k | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
632 | 18.5k | return poGDS->m_apoOvrDS[iOverviewIndex]->GetRasterBand(nBand); |
633 | 18.5k | } |
634 | 19.3k | } |
635 | | |
636 | | /************************************************************************/ |
637 | | /* ~PDFRasterBand() */ |
638 | | /************************************************************************/ |
639 | | |
640 | | PDFRasterBand::~PDFRasterBand() |
641 | 95.9k | { |
642 | 95.9k | } |
643 | | |
644 | | /************************************************************************/ |
645 | | /* IReadBlockFromTile() */ |
646 | | /************************************************************************/ |
647 | | |
648 | | CPLErr PDFRasterBand::IReadBlockFromTile(int nBlockXOff, int nBlockYOff, |
649 | | void *pImage) |
650 | | |
651 | 0 | { |
652 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
653 | |
|
654 | 0 | int nReqXSize = nBlockXSize; |
655 | 0 | int nReqYSize = nBlockYSize; |
656 | 0 | if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize) |
657 | 0 | nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize; |
658 | 0 | if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize) |
659 | 0 | nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize; |
660 | |
|
661 | 0 | int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize); |
662 | 0 | int iTile = poGDS->m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff]; |
663 | 0 | if (iTile < 0) |
664 | 0 | { |
665 | 0 | memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
666 | 0 | return CE_None; |
667 | 0 | } |
668 | | |
669 | 0 | GDALPDFTileDesc &sTile = poGDS->m_asTiles[iTile]; |
670 | 0 | GDALPDFObject *poImage = sTile.poImage; |
671 | |
|
672 | 0 | if (nBand == 4) |
673 | 0 | { |
674 | 0 | GDALPDFDictionary *poImageDict = poImage->GetDictionary(); |
675 | 0 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
676 | 0 | if (poSMask != nullptr && |
677 | 0 | poSMask->GetType() == PDFObjectType_Dictionary) |
678 | 0 | { |
679 | 0 | GDALPDFDictionary *poSMaskDict = poSMask->GetDictionary(); |
680 | 0 | GDALPDFObject *poWidth = poSMaskDict->Get("Width"); |
681 | 0 | GDALPDFObject *poHeight = poSMaskDict->Get("Height"); |
682 | 0 | GDALPDFObject *poColorSpace = poSMaskDict->Get("ColorSpace"); |
683 | 0 | GDALPDFObject *poBitsPerComponent = |
684 | 0 | poSMaskDict->Get("BitsPerComponent"); |
685 | 0 | double dfBits = 0; |
686 | 0 | if (poBitsPerComponent) |
687 | 0 | dfBits = Get(poBitsPerComponent); |
688 | 0 | if (poWidth && Get(poWidth) == nReqXSize && poHeight && |
689 | 0 | Get(poHeight) == nReqYSize && poColorSpace && |
690 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
691 | 0 | poColorSpace->GetName() == "DeviceGray" && |
692 | 0 | (dfBits == 1 || dfBits == 8)) |
693 | 0 | { |
694 | 0 | GDALPDFStream *poStream = poSMask->GetStream(); |
695 | 0 | GByte *pabyStream = nullptr; |
696 | |
|
697 | 0 | if (poStream == nullptr) |
698 | 0 | return CE_Failure; |
699 | | |
700 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
701 | 0 | if (pabyStream == nullptr) |
702 | 0 | return CE_Failure; |
703 | | |
704 | 0 | const int nReqXSize1 = (nReqXSize + 7) / 8; |
705 | 0 | if ((dfBits == 8 && |
706 | 0 | static_cast<size_t>(poStream->GetLength()) != |
707 | 0 | static_cast<size_t>(nReqXSize) * nReqYSize) || |
708 | 0 | (dfBits == 1 && |
709 | 0 | static_cast<size_t>(poStream->GetLength()) != |
710 | 0 | static_cast<size_t>(nReqXSize1) * nReqYSize)) |
711 | 0 | { |
712 | 0 | VSIFree(pabyStream); |
713 | 0 | return CE_Failure; |
714 | 0 | } |
715 | | |
716 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
717 | 0 | if (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize) |
718 | 0 | { |
719 | 0 | memset(pabyData, 0, |
720 | 0 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
721 | 0 | } |
722 | |
|
723 | 0 | if (dfBits == 8) |
724 | 0 | { |
725 | 0 | for (int j = 0; j < nReqYSize; j++) |
726 | 0 | { |
727 | 0 | for (int i = 0; i < nReqXSize; i++) |
728 | 0 | { |
729 | 0 | pabyData[j * nBlockXSize + i] = |
730 | 0 | pabyStream[j * nReqXSize + i]; |
731 | 0 | } |
732 | 0 | } |
733 | 0 | } |
734 | 0 | else |
735 | 0 | { |
736 | 0 | for (int j = 0; j < nReqYSize; j++) |
737 | 0 | { |
738 | 0 | for (int i = 0; i < nReqXSize; i++) |
739 | 0 | { |
740 | 0 | if (pabyStream[j * nReqXSize1 + i / 8] & |
741 | 0 | (1 << (7 - (i % 8)))) |
742 | 0 | pabyData[j * nBlockXSize + i] = 255; |
743 | 0 | else |
744 | 0 | pabyData[j * nBlockXSize + i] = 0; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | } |
748 | |
|
749 | 0 | VSIFree(pabyStream); |
750 | 0 | return CE_None; |
751 | 0 | } |
752 | 0 | } |
753 | | |
754 | 0 | memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
755 | 0 | return CE_None; |
756 | 0 | } |
757 | | |
758 | 0 | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
759 | 0 | poGDS->m_nLastBlockYOff == nBlockYOff && |
760 | 0 | poGDS->m_pabyCachedData != nullptr) |
761 | 0 | { |
762 | | #ifdef DEBUG |
763 | | CPLDebug("PDF", "Using cached block (%d, %d)", nBlockXOff, nBlockYOff); |
764 | | #endif |
765 | | // do nothing |
766 | 0 | } |
767 | 0 | else |
768 | 0 | { |
769 | 0 | if (!poGDS->m_bTried) |
770 | 0 | { |
771 | 0 | poGDS->m_bTried = true; |
772 | 0 | poGDS->m_pabyCachedData = |
773 | 0 | static_cast<GByte *>(VSIMalloc3(3, nBlockXSize, nBlockYSize)); |
774 | 0 | } |
775 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
776 | 0 | return CE_Failure; |
777 | | |
778 | 0 | GDALPDFStream *poStream = poImage->GetStream(); |
779 | 0 | GByte *pabyStream = nullptr; |
780 | |
|
781 | 0 | if (poStream == nullptr) |
782 | 0 | return CE_Failure; |
783 | | |
784 | 0 | pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes()); |
785 | 0 | if (pabyStream == nullptr) |
786 | 0 | return CE_Failure; |
787 | | |
788 | 0 | if (static_cast<size_t>(poStream->GetLength()) != |
789 | 0 | static_cast<size_t>(sTile.nBands) * nReqXSize * nReqYSize) |
790 | 0 | { |
791 | 0 | VSIFree(pabyStream); |
792 | 0 | return CE_Failure; |
793 | 0 | } |
794 | | |
795 | 0 | memcpy(poGDS->m_pabyCachedData, pabyStream, |
796 | 0 | static_cast<size_t>(poStream->GetLength())); |
797 | 0 | VSIFree(pabyStream); |
798 | 0 | poGDS->m_nLastBlockXOff = nBlockXOff; |
799 | 0 | poGDS->m_nLastBlockYOff = nBlockYOff; |
800 | 0 | } |
801 | | |
802 | 0 | GByte *pabyData = static_cast<GByte *>(pImage); |
803 | 0 | if (nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize)) |
804 | 0 | { |
805 | 0 | memset(pabyData, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize); |
806 | 0 | } |
807 | |
|
808 | 0 | if (poGDS->nBands >= 3 && sTile.nBands == 3) |
809 | 0 | { |
810 | 0 | for (int j = 0; j < nReqYSize; j++) |
811 | 0 | { |
812 | 0 | for (int i = 0; i < nReqXSize; i++) |
813 | 0 | { |
814 | 0 | pabyData[j * nBlockXSize + i] = |
815 | 0 | poGDS |
816 | 0 | ->m_pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1]; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | } |
820 | 0 | else if (sTile.nBands == 1) |
821 | 0 | { |
822 | 0 | for (int j = 0; j < nReqYSize; j++) |
823 | 0 | { |
824 | 0 | for (int i = 0; i < nReqXSize; i++) |
825 | 0 | { |
826 | 0 | pabyData[j * nBlockXSize + i] = |
827 | 0 | poGDS->m_pabyCachedData[j * nReqXSize + i]; |
828 | 0 | } |
829 | 0 | } |
830 | 0 | } |
831 | |
|
832 | 0 | return CE_None; |
833 | 0 | } |
834 | | |
835 | | /************************************************************************/ |
836 | | /* GetSuggestedBlockAccessPattern() */ |
837 | | /************************************************************************/ |
838 | | |
839 | | GDALSuggestedBlockAccessPattern |
840 | | PDFRasterBand::GetSuggestedBlockAccessPattern() const |
841 | 0 | { |
842 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
843 | 0 | if (!poGDS->m_aiTiles.empty()) |
844 | 0 | return GSBAP_RANDOM; |
845 | 0 | return GSBAP_LARGEST_CHUNK_POSSIBLE; |
846 | 0 | } |
847 | | |
848 | | /************************************************************************/ |
849 | | /* IReadBlock() */ |
850 | | /************************************************************************/ |
851 | | |
852 | | CPLErr PDFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
853 | | |
854 | 9.23M | { |
855 | 9.23M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
856 | | |
857 | 9.23M | if (!poGDS->m_aiTiles.empty()) |
858 | 0 | { |
859 | 0 | if (IReadBlockFromTile(nBlockXOff, nBlockYOff, pImage) == CE_None) |
860 | 0 | { |
861 | 0 | return CE_None; |
862 | 0 | } |
863 | 0 | else |
864 | 0 | { |
865 | 0 | poGDS->m_aiTiles.resize(0); |
866 | 0 | poGDS->m_bTried = false; |
867 | 0 | CPLFree(poGDS->m_pabyCachedData); |
868 | 0 | poGDS->m_pabyCachedData = nullptr; |
869 | 0 | poGDS->m_nLastBlockXOff = -1; |
870 | 0 | poGDS->m_nLastBlockYOff = -1; |
871 | 0 | } |
872 | 0 | } |
873 | | |
874 | 9.23M | int nReqXSize = nBlockXSize; |
875 | 9.23M | int nReqYSize = nBlockYSize; |
876 | 9.23M | if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize) |
877 | 0 | nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize; |
878 | 9.23M | if (nBlockYSize == 1) |
879 | 9.23M | nReqYSize = nRasterYSize; |
880 | 16 | else if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize) |
881 | 0 | nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize; |
882 | | |
883 | 9.23M | if (!poGDS->m_bTried) |
884 | 7.20k | { |
885 | 7.20k | poGDS->m_bTried = true; |
886 | 7.20k | if (nBlockYSize == 1) |
887 | 7.18k | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
888 | 7.18k | std::max(3, poGDS->nBands), nRasterXSize, nRasterYSize)); |
889 | 16 | else |
890 | 16 | poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3( |
891 | 16 | std::max(3, poGDS->nBands), nBlockXSize, nBlockYSize)); |
892 | 7.20k | } |
893 | 9.23M | if (poGDS->m_pabyCachedData == nullptr) |
894 | 0 | return CE_Failure; |
895 | | |
896 | 9.23M | if (poGDS->m_nLastBlockXOff == nBlockXOff && |
897 | 9.22M | (nBlockYSize == 1 || poGDS->m_nLastBlockYOff == nBlockYOff) && |
898 | 9.22M | poGDS->m_pabyCachedData != nullptr) |
899 | 9.22M | { |
900 | | /*CPLDebug("PDF", "Using cached block (%d, %d)", |
901 | | nBlockXOff, nBlockYOff);*/ |
902 | | // do nothing |
903 | 9.22M | } |
904 | 7.20k | else |
905 | 7.20k | { |
906 | | #ifdef HAVE_PODOFO |
907 | | if (poGDS->m_bUseLib.test(PDFLIB_PODOFO) && nBand == 4) |
908 | | { |
909 | | memset(pImage, 255, nBlockXSize * nBlockYSize); |
910 | | return CE_None; |
911 | | } |
912 | | #endif |
913 | | |
914 | 7.20k | const int nReqXOff = nBlockXOff * nBlockXSize; |
915 | 7.20k | const int nReqYOff = (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize; |
916 | 7.20k | const GSpacing nPixelSpace = 1; |
917 | 7.20k | const GSpacing nLineSpace = nBlockXSize; |
918 | 7.20k | const GSpacing nBandSpace = |
919 | 7.20k | static_cast<GSpacing>(nBlockXSize) * |
920 | 7.20k | ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize); |
921 | | |
922 | 7.20k | CPLErr eErr = poGDS->ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
923 | 7.20k | nReqYSize, nPixelSpace, nLineSpace, |
924 | 7.20k | nBandSpace, poGDS->m_pabyCachedData); |
925 | | |
926 | 7.20k | if (eErr == CE_None) |
927 | 7.20k | { |
928 | 7.20k | poGDS->m_nLastBlockXOff = nBlockXOff; |
929 | 7.20k | poGDS->m_nLastBlockYOff = nBlockYOff; |
930 | 7.20k | } |
931 | 0 | else |
932 | 0 | { |
933 | 0 | CPLFree(poGDS->m_pabyCachedData); |
934 | 0 | poGDS->m_pabyCachedData = nullptr; |
935 | 0 | } |
936 | 7.20k | } |
937 | 9.23M | if (poGDS->m_pabyCachedData == nullptr) |
938 | 0 | return CE_Failure; |
939 | | |
940 | 9.23M | if (nBlockYSize == 1) |
941 | 9.23M | memcpy(pImage, |
942 | 9.23M | poGDS->m_pabyCachedData + |
943 | 9.23M | (nBand - 1) * nBlockXSize * nRasterYSize + |
944 | 9.23M | nBlockYOff * nBlockXSize, |
945 | 9.23M | nBlockXSize); |
946 | 16 | else |
947 | 16 | { |
948 | 16 | memcpy(pImage, |
949 | 16 | poGDS->m_pabyCachedData + |
950 | 16 | static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize, |
951 | 16 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
952 | | |
953 | 16 | if (poGDS->m_bCacheBlocksForOtherBands && nBand == 1) |
954 | 16 | { |
955 | 48 | for (int iBand = 2; iBand <= poGDS->nBands; ++iBand) |
956 | 32 | { |
957 | 32 | auto poOtherBand = cpl::down_cast<PDFRasterBand *>( |
958 | 32 | poGDS->papoBands[iBand - 1]); |
959 | 32 | GDALRasterBlock *poBlock = |
960 | 32 | poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff); |
961 | 32 | if (poBlock) |
962 | 0 | { |
963 | 0 | poBlock->DropLock(); |
964 | 0 | } |
965 | 32 | else |
966 | 32 | { |
967 | 32 | poBlock = poOtherBand->GetLockedBlockRef(nBlockXOff, |
968 | 32 | nBlockYOff, TRUE); |
969 | 32 | if (poBlock) |
970 | 32 | { |
971 | 32 | memcpy(poBlock->GetDataRef(), |
972 | 32 | poGDS->m_pabyCachedData + |
973 | 32 | static_cast<size_t>(iBand - 1) * |
974 | 32 | nBlockXSize * nBlockYSize, |
975 | 32 | static_cast<size_t>(nBlockXSize) * nBlockYSize); |
976 | 32 | poBlock->DropLock(); |
977 | 32 | } |
978 | 32 | } |
979 | 32 | } |
980 | 16 | } |
981 | 16 | } |
982 | | |
983 | 9.23M | return CE_None; |
984 | 9.23M | } |
985 | | |
986 | | /************************************************************************/ |
987 | | /* PDFEnterPasswordFromConsoleIfNeeded() */ |
988 | | /************************************************************************/ |
989 | | |
990 | | static const char *PDFEnterPasswordFromConsoleIfNeeded(const char *pszUserPwd) |
991 | 0 | { |
992 | 0 | if (EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
993 | 0 | { |
994 | 0 | static char szPassword[81]; |
995 | 0 | printf("Enter password (will be echo'ed in the console): "); /*ok*/ |
996 | 0 | if (nullptr == fgets(szPassword, sizeof(szPassword), stdin)) |
997 | 0 | { |
998 | 0 | fprintf(stderr, "WARNING: Error getting password.\n"); /*ok*/ |
999 | 0 | } |
1000 | 0 | szPassword[sizeof(szPassword) - 1] = 0; |
1001 | 0 | char *sz10 = strchr(szPassword, '\n'); |
1002 | 0 | if (sz10) |
1003 | 0 | *sz10 = 0; |
1004 | 0 | return szPassword; |
1005 | 0 | } |
1006 | 0 | return pszUserPwd; |
1007 | 0 | } |
1008 | | |
1009 | | #ifdef HAVE_PDFIUM |
1010 | | |
1011 | | /************************************************************************/ |
1012 | | /* Pdfium Load/Unload */ |
1013 | | /* Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/) */ |
1014 | | /* Author: Martin Mikita <martin.mikita@klokantech.com> */ |
1015 | | /************************************************************************/ |
1016 | | |
1017 | | // Flag for calling PDFium Init and Destroy methods |
1018 | | bool PDFDataset::g_bPdfiumInit = false; |
1019 | | |
1020 | | // Pdfium global read mutex - Pdfium is not multi-thread |
1021 | | static CPLMutex *g_oPdfiumReadMutex = nullptr; |
1022 | | static CPLMutex *g_oPdfiumLoadDocMutex = nullptr; |
1023 | | |
1024 | | // Comparison of char* for std::map |
1025 | | struct cmp_str |
1026 | | { |
1027 | | bool operator()(char const *a, char const *b) const |
1028 | | { |
1029 | | return strcmp(a, b) < 0; |
1030 | | } |
1031 | | }; |
1032 | | |
1033 | | static int GDALPdfiumGetBlock(void *param, unsigned long position, |
1034 | | unsigned char *pBuf, unsigned long size) |
1035 | | { |
1036 | | VSILFILE *fp = static_cast<VSILFILE *>(param); |
1037 | | VSIFSeekL(fp, position, SEEK_SET); |
1038 | | return VSIFReadL(pBuf, size, 1, fp) == 1; |
1039 | | } |
1040 | | |
1041 | | // List of all PDF datasets |
1042 | | typedef std::map<const char *, TPdfiumDocumentStruct *, cmp_str> |
1043 | | TMapPdfiumDatasets; |
1044 | | static TMapPdfiumDatasets g_mPdfiumDatasets; |
1045 | | |
1046 | | /** |
1047 | | * Loading PDFIUM page |
1048 | | * - multithreading requires "mutex" |
1049 | | * - one page can require too much RAM |
1050 | | * - we will have one document per filename and one object per page |
1051 | | */ |
1052 | | |
1053 | | static int LoadPdfiumDocumentPage(const char *pszFilename, |
1054 | | const char *pszUserPwd, int pageNum, |
1055 | | TPdfiumDocumentStruct **doc, |
1056 | | TPdfiumPageStruct **page, int *pnPageCount) |
1057 | | { |
1058 | | // Prepare nullptr for error returning |
1059 | | if (doc) |
1060 | | *doc = nullptr; |
1061 | | if (page) |
1062 | | *page = nullptr; |
1063 | | if (pnPageCount) |
1064 | | *pnPageCount = 0; |
1065 | | |
1066 | | // Loading document and page must be only in one thread! |
1067 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1068 | | |
1069 | | // Library can be destroyed if every PDF dataset was closed! |
1070 | | if (!PDFDataset::g_bPdfiumInit) |
1071 | | { |
1072 | | FPDF_InitLibrary(); |
1073 | | PDFDataset::g_bPdfiumInit = TRUE; |
1074 | | } |
1075 | | |
1076 | | TMapPdfiumDatasets::iterator it; |
1077 | | it = g_mPdfiumDatasets.find(pszFilename); |
1078 | | TPdfiumDocumentStruct *poDoc = nullptr; |
1079 | | // Load new document if missing |
1080 | | if (it == g_mPdfiumDatasets.end()) |
1081 | | { |
1082 | | // Try without password (if PDF not requires password it can fail) |
1083 | | |
1084 | | VSILFILE *fp = VSIFOpenL(pszFilename, "rb"); |
1085 | | if (fp == nullptr) |
1086 | | { |
1087 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1088 | | return FALSE; |
1089 | | } |
1090 | | VSIFSeekL(fp, 0, SEEK_END); |
1091 | | const auto nFileLen64 = VSIFTellL(fp); |
1092 | | if constexpr (LONG_MAX < std::numeric_limits<vsi_l_offset>::max()) |
1093 | | { |
1094 | | if (nFileLen64 > LONG_MAX) |
1095 | | { |
1096 | | VSIFCloseL(fp); |
1097 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1098 | | return FALSE; |
1099 | | } |
1100 | | } |
1101 | | |
1102 | | FPDF_FILEACCESS *psFileAccess = new FPDF_FILEACCESS; |
1103 | | psFileAccess->m_Param = fp; |
1104 | | psFileAccess->m_FileLen = static_cast<unsigned long>(nFileLen64); |
1105 | | psFileAccess->m_GetBlock = GDALPdfiumGetBlock; |
1106 | | CPDF_Document *docPdfium = CPDFDocumentFromFPDFDocument( |
1107 | | FPDF_LoadCustomDocument(psFileAccess, nullptr)); |
1108 | | if (docPdfium == nullptr) |
1109 | | { |
1110 | | unsigned long err = FPDF_GetLastError(); |
1111 | | if (err == FPDF_ERR_PASSWORD) |
1112 | | { |
1113 | | if (pszUserPwd) |
1114 | | { |
1115 | | pszUserPwd = |
1116 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
1117 | | docPdfium = CPDFDocumentFromFPDFDocument( |
1118 | | FPDF_LoadCustomDocument(psFileAccess, pszUserPwd)); |
1119 | | if (docPdfium == nullptr) |
1120 | | err = FPDF_GetLastError(); |
1121 | | else |
1122 | | err = FPDF_ERR_SUCCESS; |
1123 | | } |
1124 | | else |
1125 | | { |
1126 | | CPLError(CE_Failure, CPLE_AppDefined, |
1127 | | "A password is needed. You can specify it through " |
1128 | | "the PDF_USER_PWD " |
1129 | | "configuration option / USER_PWD open option " |
1130 | | "(that can be set to ASK_INTERACTIVE)"); |
1131 | | |
1132 | | VSIFCloseL(fp); |
1133 | | delete psFileAccess; |
1134 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1135 | | return FALSE; |
1136 | | } |
1137 | | } // First Error Password [null password given] |
1138 | | if (err != FPDF_ERR_SUCCESS) |
1139 | | { |
1140 | | if (err == FPDF_ERR_PASSWORD) |
1141 | | CPLError(CE_Failure, CPLE_AppDefined, |
1142 | | "PDFium Invalid password."); |
1143 | | else if (err == FPDF_ERR_SECURITY) |
1144 | | CPLError(CE_Failure, CPLE_AppDefined, |
1145 | | "PDFium Unsupported security scheme."); |
1146 | | else if (err == FPDF_ERR_FORMAT) |
1147 | | CPLError(CE_Failure, CPLE_AppDefined, |
1148 | | "PDFium File not in PDF format or corrupted."); |
1149 | | else if (err == FPDF_ERR_FILE) |
1150 | | CPLError(CE_Failure, CPLE_AppDefined, |
1151 | | "PDFium File not found or could not be opened."); |
1152 | | else |
1153 | | CPLError(CE_Failure, CPLE_AppDefined, |
1154 | | "PDFium Unknown PDF error or invalid PDF."); |
1155 | | |
1156 | | VSIFCloseL(fp); |
1157 | | delete psFileAccess; |
1158 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1159 | | return FALSE; |
1160 | | } |
1161 | | } // ~ wrong PDF or password required |
1162 | | |
1163 | | // Create new poDoc |
1164 | | poDoc = new TPdfiumDocumentStruct; |
1165 | | if (!poDoc) |
1166 | | { |
1167 | | CPLError(CE_Failure, CPLE_AppDefined, |
1168 | | "Not enough memory for Pdfium Document object"); |
1169 | | |
1170 | | VSIFCloseL(fp); |
1171 | | delete psFileAccess; |
1172 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1173 | | return FALSE; |
1174 | | } |
1175 | | poDoc->filename = CPLStrdup(pszFilename); |
1176 | | poDoc->doc = docPdfium; |
1177 | | poDoc->psFileAccess = psFileAccess; |
1178 | | |
1179 | | g_mPdfiumDatasets[poDoc->filename] = poDoc; |
1180 | | } |
1181 | | // Document already loaded |
1182 | | else |
1183 | | { |
1184 | | poDoc = it->second; |
1185 | | } |
1186 | | |
1187 | | // Check page num in document |
1188 | | int nPages = poDoc->doc->GetPageCount(); |
1189 | | if (pageNum < 1 || pageNum > nPages) |
1190 | | { |
1191 | | CPLError(CE_Failure, CPLE_AppDefined, |
1192 | | "PDFium Invalid page number (%d/%d) for document %s", pageNum, |
1193 | | nPages, pszFilename); |
1194 | | |
1195 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1196 | | return FALSE; |
1197 | | } |
1198 | | |
1199 | | /* Sanity check to validate page count */ |
1200 | | if (pageNum != nPages) |
1201 | | { |
1202 | | if (poDoc->doc->GetPageDictionary(nPages - 1) == nullptr) |
1203 | | { |
1204 | | CPLError(CE_Failure, CPLE_AppDefined, |
1205 | | "Invalid PDF : invalid page count"); |
1206 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1207 | | return FALSE; |
1208 | | } |
1209 | | } |
1210 | | |
1211 | | TMapPdfiumPages::iterator itPage; |
1212 | | itPage = poDoc->pages.find(pageNum); |
1213 | | TPdfiumPageStruct *poPage = nullptr; |
1214 | | // Page not loaded |
1215 | | if (itPage == poDoc->pages.end()) |
1216 | | { |
1217 | | auto pDict = poDoc->doc->GetMutablePageDictionary(pageNum - 1); |
1218 | | if (pDict == nullptr) |
1219 | | { |
1220 | | CPLError(CE_Failure, CPLE_AppDefined, |
1221 | | "Invalid PDFium : invalid page"); |
1222 | | |
1223 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1224 | | return FALSE; |
1225 | | } |
1226 | | auto pPage = pdfium::MakeRetain<CPDF_Page>(poDoc->doc, pDict); |
1227 | | |
1228 | | poPage = new TPdfiumPageStruct; |
1229 | | if (!poPage) |
1230 | | { |
1231 | | CPLError(CE_Failure, CPLE_AppDefined, |
1232 | | "Not enough memory for Pdfium Page object"); |
1233 | | |
1234 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1235 | | return FALSE; |
1236 | | } |
1237 | | poPage->pageNum = pageNum; |
1238 | | poPage->page = pPage.Leak(); |
1239 | | poPage->readMutex = nullptr; |
1240 | | poPage->sharedNum = 0; |
1241 | | |
1242 | | poDoc->pages[pageNum] = poPage; |
1243 | | } |
1244 | | // Page already loaded |
1245 | | else |
1246 | | { |
1247 | | poPage = itPage->second; |
1248 | | } |
1249 | | |
1250 | | // Increase number of used |
1251 | | ++poPage->sharedNum; |
1252 | | |
1253 | | if (doc) |
1254 | | *doc = poDoc; |
1255 | | if (page) |
1256 | | *page = poPage; |
1257 | | if (pnPageCount) |
1258 | | *pnPageCount = nPages; |
1259 | | |
1260 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1261 | | |
1262 | | return TRUE; |
1263 | | } |
1264 | | |
1265 | | // ~ static int LoadPdfiumDocumentPage() |
1266 | | |
1267 | | static int UnloadPdfiumDocumentPage(TPdfiumDocumentStruct **doc, |
1268 | | TPdfiumPageStruct **page) |
1269 | | { |
1270 | | if (!doc || !page) |
1271 | | return FALSE; |
1272 | | |
1273 | | TPdfiumPageStruct *pPage = *page; |
1274 | | TPdfiumDocumentStruct *pDoc = *doc; |
1275 | | |
1276 | | // Get mutex for loading pdfium |
1277 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
1278 | | |
1279 | | // Decrease page use |
1280 | | --pPage->sharedNum; |
1281 | | |
1282 | | #ifdef DEBUG |
1283 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: page shared num %d", |
1284 | | pPage->sharedNum); |
1285 | | #endif |
1286 | | // Page is used (also document) |
1287 | | if (pPage->sharedNum != 0) |
1288 | | { |
1289 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1290 | | return TRUE; |
1291 | | } |
1292 | | |
1293 | | // Get mutex, release and destroy it |
1294 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), PDFIUM_MUTEX_TIMEOUT); |
1295 | | CPLReleaseMutex(pPage->readMutex); |
1296 | | CPLDestroyMutex(pPage->readMutex); |
1297 | | // Close page and remove from map |
1298 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
1299 | | |
1300 | | pDoc->pages.erase(pPage->pageNum); |
1301 | | delete pPage; |
1302 | | pPage = nullptr; |
1303 | | |
1304 | | #ifdef DEBUG |
1305 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: pages %lu", |
1306 | | pDoc->pages.size()); |
1307 | | #endif |
1308 | | // Another page is used |
1309 | | if (!pDoc->pages.empty()) |
1310 | | { |
1311 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1312 | | return TRUE; |
1313 | | } |
1314 | | |
1315 | | // Close document and remove from map |
1316 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
1317 | | g_mPdfiumDatasets.erase(pDoc->filename); |
1318 | | CPLFree(pDoc->filename); |
1319 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
1320 | | delete pDoc->psFileAccess; |
1321 | | delete pDoc; |
1322 | | pDoc = nullptr; |
1323 | | |
1324 | | #ifdef DEBUG |
1325 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: documents %lu", |
1326 | | g_mPdfiumDatasets.size()); |
1327 | | #endif |
1328 | | // Another document is used |
1329 | | if (!g_mPdfiumDatasets.empty()) |
1330 | | { |
1331 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1332 | | return TRUE; |
1333 | | } |
1334 | | |
1335 | | #ifdef DEBUG |
1336 | | CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: Nothing loaded, " |
1337 | | "destroy Library"); |
1338 | | #endif |
1339 | | // No document loaded, destroy pdfium |
1340 | | FPDF_DestroyLibrary(); |
1341 | | PDFDataset::g_bPdfiumInit = FALSE; |
1342 | | |
1343 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
1344 | | |
1345 | | return TRUE; |
1346 | | } |
1347 | | |
1348 | | // ~ static int UnloadPdfiumDocumentPage() |
1349 | | |
1350 | | #endif // ~ HAVE_PDFIUM |
1351 | | |
1352 | | /************************************************************************/ |
1353 | | /* GetOption() */ |
1354 | | /************************************************************************/ |
1355 | | |
1356 | | const char *PDFDataset::GetOption(char **papszOpenOptionsIn, |
1357 | | const char *pszOptionName, |
1358 | | const char *pszDefaultVal) |
1359 | 133k | { |
1360 | 133k | CPLErr eLastErrType = CPLGetLastErrorType(); |
1361 | 133k | CPLErrorNum nLastErrno = CPLGetLastErrorNo(); |
1362 | 133k | CPLString osLastErrorMsg(CPLGetLastErrorMsg()); |
1363 | 133k | CPLXMLNode *psNode = CPLParseXMLString(PDFGetOpenOptionList()); |
1364 | 133k | CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg); |
1365 | 133k | if (psNode == nullptr) |
1366 | 0 | return pszDefaultVal; |
1367 | 133k | CPLXMLNode *psIter = psNode->psChild; |
1368 | 496k | while (psIter != nullptr) |
1369 | 496k | { |
1370 | 496k | if (EQUAL(CPLGetXMLValue(psIter, "name", ""), pszOptionName)) |
1371 | 133k | { |
1372 | 133k | const char *pszVal = |
1373 | 133k | CSLFetchNameValue(papszOpenOptionsIn, pszOptionName); |
1374 | 133k | if (pszVal != nullptr) |
1375 | 36.9k | { |
1376 | 36.9k | CPLDestroyXMLNode(psNode); |
1377 | 36.9k | return pszVal; |
1378 | 36.9k | } |
1379 | 97.0k | const char *pszAltConfigOption = |
1380 | 97.0k | CPLGetXMLValue(psIter, "alt_config_option", nullptr); |
1381 | 97.0k | if (pszAltConfigOption != nullptr) |
1382 | 97.0k | { |
1383 | 97.0k | pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal); |
1384 | 97.0k | CPLDestroyXMLNode(psNode); |
1385 | 97.0k | return pszVal; |
1386 | 97.0k | } |
1387 | 0 | CPLDestroyXMLNode(psNode); |
1388 | 0 | return pszDefaultVal; |
1389 | 97.0k | } |
1390 | 362k | psIter = psIter->psNext; |
1391 | 362k | } |
1392 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1393 | 0 | "Requesting an undocumented open option '%s'", pszOptionName); |
1394 | 0 | CPLDestroyXMLNode(psNode); |
1395 | 0 | return pszDefaultVal; |
1396 | 133k | } |
1397 | | |
1398 | | #ifdef HAVE_PDFIUM |
1399 | | |
1400 | | /************************************************************************/ |
1401 | | /* GDALPDFiumOCContext */ |
1402 | | /************************************************************************/ |
1403 | | |
1404 | | class GDALPDFiumOCContext final : public CPDF_OCContextInterface |
1405 | | { |
1406 | | PDFDataset *m_poDS; |
1407 | | RetainPtr<CPDF_OCContext> m_DefaultOCContext; |
1408 | | |
1409 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumOCContext) |
1410 | | |
1411 | | public: |
1412 | | GDALPDFiumOCContext(PDFDataset *poDS, CPDF_Document *pDoc, |
1413 | | CPDF_OCContext::UsageType usage) |
1414 | | : m_poDS(poDS), |
1415 | | m_DefaultOCContext(pdfium::MakeRetain<CPDF_OCContext>(pDoc, usage)) |
1416 | | { |
1417 | | } |
1418 | | |
1419 | | virtual bool |
1420 | | CheckOCGDictVisible(const CPDF_Dictionary *pOCGDict) const override |
1421 | | { |
1422 | | // CPLDebug("PDF", "CheckOCGDictVisible(%d,%d)", |
1423 | | // pOCGDict->GetObjNum(), pOCGDict->GetGenNum() ); |
1424 | | PDFDataset::VisibilityState eVisibility = |
1425 | | m_poDS->GetVisibilityStateForOGCPdfium(pOCGDict->GetObjNum(), |
1426 | | pOCGDict->GetGenNum()); |
1427 | | if (eVisibility == PDFDataset::VISIBILITY_ON) |
1428 | | return true; |
1429 | | if (eVisibility == PDFDataset::VISIBILITY_OFF) |
1430 | | return false; |
1431 | | return m_DefaultOCContext->CheckOCGDictVisible(pOCGDict); |
1432 | | } |
1433 | | }; |
1434 | | |
1435 | | /************************************************************************/ |
1436 | | /* GDALPDFiumRenderDeviceDriver */ |
1437 | | /************************************************************************/ |
1438 | | |
1439 | | class GDALPDFiumRenderDeviceDriver final : public RenderDeviceDriverIface |
1440 | | { |
1441 | | std::unique_ptr<RenderDeviceDriverIface> m_poParent; |
1442 | | CFX_RenderDevice *device_; |
1443 | | |
1444 | | int bEnableVector; |
1445 | | int bEnableText; |
1446 | | int bEnableBitmap; |
1447 | | int bTemporaryEnableVectorForTextStroking; |
1448 | | |
1449 | | CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumRenderDeviceDriver) |
1450 | | |
1451 | | public: |
1452 | | GDALPDFiumRenderDeviceDriver( |
1453 | | std::unique_ptr<RenderDeviceDriverIface> &&poParent, |
1454 | | CFX_RenderDevice *pDevice) |
1455 | | : m_poParent(std::move(poParent)), device_(pDevice), |
1456 | | bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE), |
1457 | | bTemporaryEnableVectorForTextStroking(FALSE) |
1458 | | { |
1459 | | } |
1460 | | |
1461 | | virtual ~GDALPDFiumRenderDeviceDriver() = default; |
1462 | | |
1463 | | void SetEnableVector(int bFlag) |
1464 | | { |
1465 | | bEnableVector = bFlag; |
1466 | | } |
1467 | | |
1468 | | void SetEnableText(int bFlag) |
1469 | | { |
1470 | | bEnableText = bFlag; |
1471 | | } |
1472 | | |
1473 | | void SetEnableBitmap(int bFlag) |
1474 | | { |
1475 | | bEnableBitmap = bFlag; |
1476 | | } |
1477 | | |
1478 | | DeviceType GetDeviceType() const override |
1479 | | { |
1480 | | return m_poParent->GetDeviceType(); |
1481 | | } |
1482 | | |
1483 | | int GetDeviceCaps(int caps_id) const override |
1484 | | { |
1485 | | return m_poParent->GetDeviceCaps(caps_id); |
1486 | | } |
1487 | | |
1488 | | void SaveState() override |
1489 | | { |
1490 | | m_poParent->SaveState(); |
1491 | | } |
1492 | | |
1493 | | void RestoreState(bool bKeepSaved) override |
1494 | | { |
1495 | | m_poParent->RestoreState(bKeepSaved); |
1496 | | } |
1497 | | |
1498 | | void SetBaseClip(const FX_RECT &rect) override |
1499 | | { |
1500 | | m_poParent->SetBaseClip(rect); |
1501 | | } |
1502 | | |
1503 | | virtual bool |
1504 | | SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1505 | | const CFX_FillRenderOptions &fill_options) override |
1506 | | { |
1507 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1508 | | return true; |
1509 | | return m_poParent->SetClip_PathFill(path, pObject2Device, fill_options); |
1510 | | } |
1511 | | |
1512 | | virtual bool |
1513 | | SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, |
1514 | | const CFX_GraphStateData *pGraphState) override |
1515 | | { |
1516 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1517 | | return true; |
1518 | | return m_poParent->SetClip_PathStroke(path, pObject2Device, |
1519 | | pGraphState); |
1520 | | } |
1521 | | |
1522 | | virtual bool DrawPath(const CFX_Path &path, |
1523 | | const CFX_Matrix *pObject2Device, |
1524 | | const CFX_GraphStateData *pGraphState, |
1525 | | uint32_t fill_color, uint32_t stroke_color, |
1526 | | const CFX_FillRenderOptions &fill_options) override |
1527 | | { |
1528 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1529 | | return true; |
1530 | | return m_poParent->DrawPath(path, pObject2Device, pGraphState, |
1531 | | fill_color, stroke_color, fill_options); |
1532 | | } |
1533 | | |
1534 | | bool FillRect(const FX_RECT &rect, uint32_t fill_color) override |
1535 | | { |
1536 | | return m_poParent->FillRect(rect, fill_color); |
1537 | | } |
1538 | | |
1539 | | virtual bool DrawCosmeticLine(const CFX_PointF &ptMoveTo, |
1540 | | const CFX_PointF &ptLineTo, |
1541 | | uint32_t color) override |
1542 | | { |
1543 | | if (!bEnableVector && !bTemporaryEnableVectorForTextStroking) |
1544 | | return TRUE; |
1545 | | return m_poParent->DrawCosmeticLine(ptMoveTo, ptLineTo, color); |
1546 | | } |
1547 | | |
1548 | | FX_RECT GetClipBox() const override |
1549 | | { |
1550 | | return m_poParent->GetClipBox(); |
1551 | | } |
1552 | | |
1553 | | virtual bool GetDIBits(RetainPtr<CFX_DIBitmap> bitmap, int left, |
1554 | | int top) const override |
1555 | | { |
1556 | | return m_poParent->GetDIBits(std::move(bitmap), left, top); |
1557 | | } |
1558 | | |
1559 | | RetainPtr<const CFX_DIBitmap> GetBackDrop() const override |
1560 | | { |
1561 | | return m_poParent->GetBackDrop(); |
1562 | | } |
1563 | | |
1564 | | virtual bool SetDIBits(RetainPtr<const CFX_DIBBase> bitmap, uint32_t color, |
1565 | | const FX_RECT &src_rect, int dest_left, int dest_top, |
1566 | | BlendMode blend_type) override |
1567 | | { |
1568 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1569 | | return true; |
1570 | | return m_poParent->SetDIBits(std::move(bitmap), color, src_rect, |
1571 | | dest_left, dest_top, blend_type); |
1572 | | } |
1573 | | |
1574 | | virtual bool StretchDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1575 | | uint32_t color, int dest_left, int dest_top, |
1576 | | int dest_width, int dest_height, |
1577 | | const FX_RECT *pClipRect, |
1578 | | const FXDIB_ResampleOptions &options, |
1579 | | BlendMode blend_type) override |
1580 | | { |
1581 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1582 | | return true; |
1583 | | return m_poParent->StretchDIBits(std::move(bitmap), color, dest_left, |
1584 | | dest_top, dest_width, dest_height, |
1585 | | pClipRect, options, blend_type); |
1586 | | } |
1587 | | |
1588 | | virtual StartResult StartDIBits(RetainPtr<const CFX_DIBBase> bitmap, |
1589 | | float alpha, uint32_t color, |
1590 | | const CFX_Matrix &matrix, |
1591 | | const FXDIB_ResampleOptions &options, |
1592 | | BlendMode blend_type) override |
1593 | | { |
1594 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1595 | | return StartResult(Result::kSuccess, nullptr); |
1596 | | return m_poParent->StartDIBits(std::move(bitmap), alpha, color, matrix, |
1597 | | options, blend_type); |
1598 | | } |
1599 | | |
1600 | | virtual bool ContinueDIBits(CFX_AggImageRenderer *handle, |
1601 | | PauseIndicatorIface *pPause) override |
1602 | | { |
1603 | | return m_poParent->ContinueDIBits(handle, pPause); |
1604 | | } |
1605 | | |
1606 | | virtual bool DrawDeviceText(const pdfium::span<const TextCharPos> &pCharPos, |
1607 | | CFX_Font *pFont, |
1608 | | const CFX_Matrix &mtObject2Device, |
1609 | | float font_size, uint32_t color, |
1610 | | const CFX_TextRenderOptions &options) override |
1611 | | { |
1612 | | if (bEnableText) |
1613 | | { |
1614 | | // This is quite tricky. We call again the guy who called us |
1615 | | // (CFX_RenderDevice::DrawNormalText()) but we set a special flag to |
1616 | | // allow vector&raster operations so that the rendering will happen |
1617 | | // in the next phase |
1618 | | if (bTemporaryEnableVectorForTextStroking) |
1619 | | return FALSE; // this is the default behavior of the parent |
1620 | | bTemporaryEnableVectorForTextStroking = true; |
1621 | | bool bRet = device_->DrawNormalText( |
1622 | | pCharPos, pFont, font_size, mtObject2Device, color, options); |
1623 | | bTemporaryEnableVectorForTextStroking = FALSE; |
1624 | | return bRet; |
1625 | | } |
1626 | | else |
1627 | | return true; // pretend that we did the job |
1628 | | } |
1629 | | |
1630 | | int GetDriverType() const override |
1631 | | { |
1632 | | return m_poParent->GetDriverType(); |
1633 | | } |
1634 | | |
1635 | | #if defined(_SKIA_SUPPORT_) |
1636 | | virtual bool DrawShading(const CPDF_ShadingPattern &pattern, |
1637 | | const CFX_Matrix &matrix, const FX_RECT &clip_rect, |
1638 | | int alpha) override |
1639 | | { |
1640 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1641 | | return true; |
1642 | | return m_poParent->DrawShading(pattern, matrix, clip_rect, alpha); |
1643 | | } |
1644 | | #endif |
1645 | | |
1646 | | bool MultiplyAlpha(float alpha) override |
1647 | | { |
1648 | | return m_poParent->MultiplyAlpha(alpha); |
1649 | | } |
1650 | | |
1651 | | bool MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) override |
1652 | | { |
1653 | | return m_poParent->MultiplyAlphaMask(std::move(mask)); |
1654 | | } |
1655 | | |
1656 | | #if defined(_SKIA_SUPPORT_) |
1657 | | virtual bool SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap, |
1658 | | RetainPtr<const CFX_DIBBase> mask, int left, |
1659 | | int top, float alpha, |
1660 | | BlendMode blend_type) override |
1661 | | { |
1662 | | if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking) |
1663 | | return true; |
1664 | | return m_poParent->SetBitsWithMask(std::move(bitmap), std::move(mask), |
1665 | | left, top, alpha, blend_type); |
1666 | | } |
1667 | | |
1668 | | void SetGroupKnockout(bool group_knockout) override |
1669 | | { |
1670 | | m_poParent->SetGroupKnockout(group_knockout); |
1671 | | } |
1672 | | #endif |
1673 | | #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ |
1674 | | void Flush() override |
1675 | | { |
1676 | | return m_poParent->Flush(); |
1677 | | } |
1678 | | #endif |
1679 | | }; |
1680 | | |
1681 | | /************************************************************************/ |
1682 | | /* PDFiumRenderPageBitmap() */ |
1683 | | /************************************************************************/ |
1684 | | |
1685 | | /* This method is a customization of RenderPageImpl() |
1686 | | from pdfium/fpdfsdk/cpdfsdk_renderpage.cpp to allow selection of which OGC/layer are |
1687 | | active. Thus it inherits the following license */ |
1688 | | // Copyright 2014-2020 PDFium Authors. All rights reserved. |
1689 | | // |
1690 | | // Redistribution and use in source and binary forms, with or without |
1691 | | // modification, are permitted provided that the following conditions are |
1692 | | // met: |
1693 | | // |
1694 | | // * Redistributions of source code must retain the above copyright |
1695 | | // notice, this list of conditions and the following disclaimer. |
1696 | | // * Redistributions in binary form must reproduce the above |
1697 | | // copyright notice, this list of conditions and the following disclaimer |
1698 | | // in the documentation and/or other materials provided with the |
1699 | | // distribution. |
1700 | | // * Neither the name of Google Inc. nor the names of its |
1701 | | // contributors may be used to endorse or promote products derived from |
1702 | | // this software without specific prior written permission. |
1703 | | // |
1704 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
1705 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
1706 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
1707 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
1708 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
1709 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
1710 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
1711 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
1712 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1713 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
1714 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1715 | | |
1716 | | static void myRenderPageImpl(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1717 | | CPDF_Page *pPage, const CFX_Matrix &matrix, |
1718 | | const FX_RECT &clipping_rect, int flags, |
1719 | | const FPDF_COLORSCHEME *color_scheme, |
1720 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1721 | | { |
1722 | | if (!pContext->options_) |
1723 | | pContext->options_ = std::make_unique<CPDF_RenderOptions>(); |
1724 | | |
1725 | | auto &options = pContext->options_->GetOptions(); |
1726 | | options.bClearType = !!(flags & FPDF_LCD_TEXT); |
1727 | | options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT); |
1728 | | options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE); |
1729 | | options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE); |
1730 | | options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT); |
1731 | | options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE); |
1732 | | options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH); |
1733 | | |
1734 | | // Grayscale output |
1735 | | if (flags & FPDF_GRAYSCALE) |
1736 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kGray); |
1737 | | |
1738 | | if (color_scheme) |
1739 | | { |
1740 | | pContext->options_->SetColorMode(CPDF_RenderOptions::kForcedColor); |
1741 | | SetColorFromScheme(color_scheme, pContext->options_.get()); |
1742 | | options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE); |
1743 | | } |
1744 | | |
1745 | | const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING) |
1746 | | ? CPDF_OCContext::kPrint |
1747 | | : CPDF_OCContext::kView; |
1748 | | pContext->options_->SetOCContext(pdfium::MakeRetain<GDALPDFiumOCContext>( |
1749 | | poDS, pPage->GetDocument(), usage)); |
1750 | | |
1751 | | pContext->device_->SaveState(); |
1752 | | pContext->device_->SetBaseClip(clipping_rect); |
1753 | | pContext->device_->SetClip_Rect(clipping_rect); |
1754 | | pContext->context_ = std::make_unique<CPDF_RenderContext>( |
1755 | | pPage->GetDocument(), pPage->GetMutablePageResources(), |
1756 | | pPage->GetPageImageCache()); |
1757 | | |
1758 | | pContext->context_->AppendLayer(pPage, matrix); |
1759 | | |
1760 | | if (flags & FPDF_ANNOT) |
1761 | | { |
1762 | | auto pOwnedList = std::make_unique<CPDF_AnnotList>(pPage); |
1763 | | CPDF_AnnotList *pList = pOwnedList.get(); |
1764 | | pContext->annots_ = std::move(pOwnedList); |
1765 | | bool bPrinting = |
1766 | | pContext->device_->GetDeviceType() != DeviceType::kDisplay; |
1767 | | |
1768 | | // TODO(https://crbug.com/pdfium/993) - maybe pass true here. |
1769 | | const bool bShowWidget = false; |
1770 | | pList->DisplayAnnots(pContext->context_.get(), bPrinting, matrix, |
1771 | | bShowWidget); |
1772 | | } |
1773 | | |
1774 | | pContext->renderer_ = std::make_unique<CPDF_ProgressiveRenderer>( |
1775 | | pContext->context_.get(), pContext->device_.get(), |
1776 | | pContext->options_.get()); |
1777 | | pContext->renderer_->Start(pause); |
1778 | | if (bNeedToRestore) |
1779 | | pContext->device_->RestoreState(false); |
1780 | | } |
1781 | | |
1782 | | static void |
1783 | | myRenderPageWithContext(PDFDataset *poDS, CPDF_PageRenderContext *pContext, |
1784 | | FPDF_PAGE page, int start_x, int start_y, int size_x, |
1785 | | int size_y, int rotate, int flags, |
1786 | | const FPDF_COLORSCHEME *color_scheme, |
1787 | | bool bNeedToRestore, CPDFSDK_PauseAdapter *pause) |
1788 | | { |
1789 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1790 | | if (!pPage) |
1791 | | return; |
1792 | | |
1793 | | const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); |
1794 | | myRenderPageImpl(poDS, pContext, pPage, |
1795 | | pPage->GetDisplayMatrixForRect(rect, rotate), rect, flags, |
1796 | | color_scheme, bNeedToRestore, pause); |
1797 | | } |
1798 | | |
1799 | | class MyRenderDevice final : public CFX_RenderDevice |
1800 | | { |
1801 | | |
1802 | | public: |
1803 | | // Substitution for CFX_DefaultRenderDevice::Attach |
1804 | | bool Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, bool bRgbByteOrder, |
1805 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1806 | | bool bGroupKnockout, const char *pszRenderingOptions); |
1807 | | }; |
1808 | | |
1809 | | bool MyRenderDevice::Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, |
1810 | | bool bRgbByteOrder, |
1811 | | const RetainPtr<CFX_DIBitmap> &pBackdropBitmap, |
1812 | | bool bGroupKnockout, |
1813 | | const char *pszRenderingOptions) |
1814 | | { |
1815 | | SetBitmap(pBitmap); |
1816 | | |
1817 | | std::unique_ptr<RenderDeviceDriverIface> driver = |
1818 | | std::make_unique<pdfium::CFX_AggDeviceDriver>( |
1819 | | pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout); |
1820 | | if (pszRenderingOptions != nullptr) |
1821 | | { |
1822 | | int bEnableVector = FALSE; |
1823 | | int bEnableText = FALSE; |
1824 | | int bEnableBitmap = FALSE; |
1825 | | |
1826 | | char **papszTokens = CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
1827 | | for (int i = 0; papszTokens[i] != nullptr; i++) |
1828 | | { |
1829 | | if (EQUAL(papszTokens[i], "VECTOR")) |
1830 | | bEnableVector = TRUE; |
1831 | | else if (EQUAL(papszTokens[i], "TEXT")) |
1832 | | bEnableText = TRUE; |
1833 | | else if (EQUAL(papszTokens[i], "RASTER") || |
1834 | | EQUAL(papszTokens[i], "BITMAP")) |
1835 | | bEnableBitmap = TRUE; |
1836 | | else |
1837 | | { |
1838 | | CPLError(CE_Warning, CPLE_NotSupported, |
1839 | | "Value %s is not a valid value for " |
1840 | | "GDAL_PDF_RENDERING_OPTIONS", |
1841 | | papszTokens[i]); |
1842 | | } |
1843 | | } |
1844 | | CSLDestroy(papszTokens); |
1845 | | |
1846 | | if (!bEnableVector || !bEnableText || !bEnableBitmap) |
1847 | | { |
1848 | | std::unique_ptr<GDALPDFiumRenderDeviceDriver> poGDALRDDriver = |
1849 | | std::make_unique<GDALPDFiumRenderDeviceDriver>( |
1850 | | std::move(driver), this); |
1851 | | poGDALRDDriver->SetEnableVector(bEnableVector); |
1852 | | poGDALRDDriver->SetEnableText(bEnableText); |
1853 | | poGDALRDDriver->SetEnableBitmap(bEnableBitmap); |
1854 | | driver = std::move(poGDALRDDriver); |
1855 | | } |
1856 | | } |
1857 | | |
1858 | | SetDeviceDriver(std::move(driver)); |
1859 | | return true; |
1860 | | } |
1861 | | |
1862 | | void PDFDataset::PDFiumRenderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, |
1863 | | int start_x, int start_y, int size_x, |
1864 | | int size_y, |
1865 | | const char *pszRenderingOptions) |
1866 | | { |
1867 | | const int rotate = 0; |
1868 | | const int flags = 0; |
1869 | | |
1870 | | if (!bitmap) |
1871 | | return; |
1872 | | |
1873 | | CPDF_Page *pPage = CPDFPageFromFPDFPage(page); |
1874 | | if (!pPage) |
1875 | | return; |
1876 | | |
1877 | | auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>(); |
1878 | | CPDF_PageRenderContext *pContext = pOwnedContext.get(); |
1879 | | CPDF_Page::RenderContextClearer clearer(pPage); |
1880 | | pPage->SetRenderContext(std::move(pOwnedContext)); |
1881 | | |
1882 | | auto pOwnedDevice = std::make_unique<MyRenderDevice>(); |
1883 | | auto pDevice = pOwnedDevice.get(); |
1884 | | pContext->device_ = std::move(pOwnedDevice); |
1885 | | |
1886 | | RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); |
1887 | | |
1888 | | pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, |
1889 | | false, pszRenderingOptions); |
1890 | | |
1891 | | myRenderPageWithContext(this, pContext, page, start_x, start_y, size_x, |
1892 | | size_y, rotate, flags, |
1893 | | /*color_scheme=*/nullptr, |
1894 | | /*need_to_restore=*/true, /*pause=*/nullptr); |
1895 | | |
1896 | | #ifdef _SKIA_SUPPORT_PATHS_ |
1897 | | pDevice->Flush(true); |
1898 | | pBitmap->UnPreMultiply(); |
1899 | | #endif |
1900 | | } |
1901 | | |
1902 | | #endif /* HAVE_PDFIUM */ |
1903 | | |
1904 | | /************************************************************************/ |
1905 | | /* ReadPixels() */ |
1906 | | /************************************************************************/ |
1907 | | |
1908 | | CPLErr PDFDataset::ReadPixels(int nReqXOff, int nReqYOff, int nReqXSize, |
1909 | | int nReqYSize, GSpacing nPixelSpace, |
1910 | | GSpacing nLineSpace, GSpacing nBandSpace, |
1911 | | GByte *pabyData) |
1912 | 7.20k | { |
1913 | 7.20k | CPLErr eErr = CE_None; |
1914 | 7.20k | const char *pszRenderingOptions = |
1915 | 7.20k | GetOption(papszOpenOptions, "RENDERING_OPTIONS", nullptr); |
1916 | | |
1917 | 7.20k | #ifdef HAVE_POPPLER |
1918 | 7.20k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
1919 | 7.20k | { |
1920 | 7.20k | SplashColor sColor; |
1921 | 7.20k | sColor[0] = 255; |
1922 | 7.20k | sColor[1] = 255; |
1923 | 7.20k | sColor[2] = 255; |
1924 | 7.20k | GDALPDFOutputDev *poSplashOut = new GDALPDFOutputDev( |
1925 | 7.20k | (nBands < 4) ? splashModeRGB8 : splashModeXBGR8, 4, false, |
1926 | 7.20k | (nBands < 4) ? sColor : nullptr); |
1927 | | |
1928 | 7.20k | if (pszRenderingOptions != nullptr) |
1929 | 7.20k | { |
1930 | 7.20k | poSplashOut->SetEnableVector(FALSE); |
1931 | 7.20k | poSplashOut->SetEnableText(FALSE); |
1932 | 7.20k | poSplashOut->SetEnableBitmap(FALSE); |
1933 | | |
1934 | 7.20k | char **papszTokens = |
1935 | 7.20k | CSLTokenizeString2(pszRenderingOptions, " ,", 0); |
1936 | 21.6k | for (int i = 0; papszTokens[i] != nullptr; i++) |
1937 | 14.4k | { |
1938 | 14.4k | if (EQUAL(papszTokens[i], "VECTOR")) |
1939 | 7.20k | poSplashOut->SetEnableVector(TRUE); |
1940 | 7.20k | else if (EQUAL(papszTokens[i], "TEXT")) |
1941 | 0 | poSplashOut->SetEnableText(TRUE); |
1942 | 7.20k | else if (EQUAL(papszTokens[i], "RASTER") || |
1943 | 0 | EQUAL(papszTokens[i], "BITMAP")) |
1944 | 7.20k | poSplashOut->SetEnableBitmap(TRUE); |
1945 | 0 | else |
1946 | 0 | { |
1947 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1948 | 0 | "Value %s is not a valid value for " |
1949 | 0 | "GDAL_PDF_RENDERING_OPTIONS", |
1950 | 0 | papszTokens[i]); |
1951 | 0 | } |
1952 | 14.4k | } |
1953 | 7.20k | CSLDestroy(papszTokens); |
1954 | 7.20k | } |
1955 | | |
1956 | 7.20k | PDFDoc *poDoc = m_poDocPoppler; |
1957 | 7.20k | poSplashOut->startDoc(poDoc); |
1958 | | |
1959 | | // Note: Poppler 25.2 is certainly not the lowest version where we can |
1960 | | // avoid the hack. |
1961 | 7.20k | #if !(POPPLER_MAJOR_VERSION > 25 || \ |
1962 | 7.20k | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)) |
1963 | 7.20k | #define USE_OPTCONTENT_HACK |
1964 | 7.20k | #endif |
1965 | | |
1966 | 7.20k | #ifdef USE_OPTCONTENT_HACK |
1967 | | /* EVIL: we modify a private member... */ |
1968 | | /* poppler (at least 0.12 and 0.14 versions) don't render correctly */ |
1969 | | /* some PDFs and display an error message 'Could not find a OCG with |
1970 | | * Ref' */ |
1971 | | /* in those cases. This processing of optional content is an addition of |
1972 | | */ |
1973 | | /* poppler in comparison to original xpdf, which hasn't the issue. All |
1974 | | * in */ |
1975 | | /* all, nullifying optContent removes the error message and improves the |
1976 | | * rendering */ |
1977 | 7.20k | Catalog *poCatalog = poDoc->getCatalog(); |
1978 | 7.20k | OCGs *poOldOCGs = poCatalog->optContent; |
1979 | 7.20k | if (!m_bUseOCG) |
1980 | 7.20k | poCatalog->optContent = nullptr; |
1981 | 7.20k | #endif |
1982 | 7.20k | try |
1983 | 7.20k | { |
1984 | 7.20k | poDoc->displayPageSlice(poSplashOut, m_iPage, m_dfDPI, m_dfDPI, 0, |
1985 | 7.20k | TRUE, false, false, nReqXOff, nReqYOff, |
1986 | 7.20k | nReqXSize, nReqYSize); |
1987 | 7.20k | } |
1988 | 7.20k | catch (const std::exception &e) |
1989 | 7.20k | { |
1990 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1991 | 0 | "PDFDoc::displayPageSlice() failed with %s", e.what()); |
1992 | |
|
1993 | 0 | #ifdef USE_OPTCONTENT_HACK |
1994 | | /* Restore back */ |
1995 | 0 | poCatalog->optContent = poOldOCGs; |
1996 | 0 | #endif |
1997 | 0 | delete poSplashOut; |
1998 | 0 | return CE_Failure; |
1999 | 0 | } |
2000 | | |
2001 | 0 | #ifdef USE_OPTCONTENT_HACK |
2002 | | /* Restore back */ |
2003 | 7.20k | poCatalog->optContent = poOldOCGs; |
2004 | 7.20k | #endif |
2005 | | |
2006 | 7.20k | SplashBitmap *poBitmap = poSplashOut->getBitmap(); |
2007 | 7.20k | if (poBitmap->getWidth() != nReqXSize || |
2008 | 7.20k | poBitmap->getHeight() != nReqYSize) |
2009 | 0 | { |
2010 | 0 | CPLError( |
2011 | 0 | CE_Failure, CPLE_AppDefined, |
2012 | 0 | "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)", |
2013 | 0 | poBitmap->getWidth(), poBitmap->getHeight(), nReqXSize, |
2014 | 0 | nReqYSize); |
2015 | 0 | delete poSplashOut; |
2016 | 0 | return CE_Failure; |
2017 | 0 | } |
2018 | | |
2019 | 7.20k | GByte *pabyDataR = pabyData; |
2020 | 7.20k | GByte *pabyDataG = pabyData + nBandSpace; |
2021 | 7.20k | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2022 | 7.20k | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2023 | 7.20k | GByte *pabySrc = poBitmap->getDataPtr(); |
2024 | 7.20k | GByte *pabyAlphaSrc = |
2025 | 7.20k | reinterpret_cast<GByte *>(poBitmap->getAlphaPtr()); |
2026 | 7.20k | int i, j; |
2027 | 9.31M | for (j = 0; j < nReqYSize; j++) |
2028 | 9.30M | { |
2029 | 11.9G | for (i = 0; i < nReqXSize; i++) |
2030 | 11.9G | { |
2031 | 11.9G | if (nBands < 4) |
2032 | 11.9G | { |
2033 | 11.9G | pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0]; |
2034 | 11.9G | pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1]; |
2035 | 11.9G | pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2]; |
2036 | 11.9G | } |
2037 | 0 | else |
2038 | 0 | { |
2039 | 0 | pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2]; |
2040 | 0 | pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1]; |
2041 | 0 | pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0]; |
2042 | 0 | pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i]; |
2043 | 0 | } |
2044 | 11.9G | } |
2045 | 9.30M | pabyDataR += nLineSpace; |
2046 | 9.30M | pabyDataG += nLineSpace; |
2047 | 9.30M | pabyDataB += nLineSpace; |
2048 | 9.30M | pabyDataA += nLineSpace; |
2049 | 9.30M | pabyAlphaSrc += poBitmap->getAlphaRowSize(); |
2050 | 9.30M | pabySrc += poBitmap->getRowSize(); |
2051 | 9.30M | } |
2052 | 7.20k | delete poSplashOut; |
2053 | 7.20k | } |
2054 | 7.20k | #endif // HAVE_POPPLER |
2055 | | |
2056 | | #ifdef HAVE_PODOFO |
2057 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2058 | | { |
2059 | | if (m_bPdfToPpmFailed) |
2060 | | return CE_Failure; |
2061 | | |
2062 | | if (pszRenderingOptions != nullptr && |
2063 | | !EQUAL(pszRenderingOptions, "RASTER,VECTOR,TEXT")) |
2064 | | { |
2065 | | CPLError(CE_Warning, CPLE_NotSupported, |
2066 | | "GDAL_PDF_RENDERING_OPTIONS only supported " |
2067 | | "when PDF lib is Poppler."); |
2068 | | } |
2069 | | |
2070 | | CPLString osTmpFilename; |
2071 | | int nRet; |
2072 | | |
2073 | | #ifdef notdef |
2074 | | int bUseSpawn = |
2075 | | CPLTestBool(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES")); |
2076 | | if (!bUseSpawn) |
2077 | | { |
2078 | | CPLString osCmd = CPLSPrintf( |
2079 | | "pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"", |
2080 | | dfDPI, nReqXOff, nReqYOff, nReqXSize, nReqYSize, iPage, iPage, |
2081 | | osFilename.c_str()); |
2082 | | |
2083 | | if (!osUserPwd.empty()) |
2084 | | { |
2085 | | osCmd += " -upw \""; |
2086 | | osCmd += osUserPwd; |
2087 | | osCmd += "\""; |
2088 | | } |
2089 | | |
2090 | | CPLString osTmpFilenamePrefix = CPLGenerateTempFilenameSafe("pdf"); |
2091 | | osTmpFilename = |
2092 | | CPLSPrintf("%s-%d.ppm", osTmpFilenamePrefix.c_str(), iPage); |
2093 | | osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str()); |
2094 | | |
2095 | | CPLDebug("PDF", "Running '%s'", osCmd.c_str()); |
2096 | | nRet = CPLSystem(nullptr, osCmd.c_str()); |
2097 | | } |
2098 | | else |
2099 | | #endif // notdef |
2100 | | { |
2101 | | char **papszArgs = nullptr; |
2102 | | papszArgs = CSLAddString(papszArgs, "pdftoppm"); |
2103 | | papszArgs = CSLAddString(papszArgs, "-r"); |
2104 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", m_dfDPI)); |
2105 | | papszArgs = CSLAddString(papszArgs, "-x"); |
2106 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff)); |
2107 | | papszArgs = CSLAddString(papszArgs, "-y"); |
2108 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff)); |
2109 | | papszArgs = CSLAddString(papszArgs, "-W"); |
2110 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize)); |
2111 | | papszArgs = CSLAddString(papszArgs, "-H"); |
2112 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize)); |
2113 | | papszArgs = CSLAddString(papszArgs, "-f"); |
2114 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2115 | | papszArgs = CSLAddString(papszArgs, "-l"); |
2116 | | papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage)); |
2117 | | if (!m_osUserPwd.empty()) |
2118 | | { |
2119 | | papszArgs = CSLAddString(papszArgs, "-upw"); |
2120 | | papszArgs = CSLAddString(papszArgs, m_osUserPwd.c_str()); |
2121 | | } |
2122 | | papszArgs = CSLAddString(papszArgs, m_osFilename.c_str()); |
2123 | | |
2124 | | osTmpFilename = VSIMemGenerateHiddenFilename("pdf_temp.ppm"); |
2125 | | VSILFILE *fpOut = VSIFOpenL(osTmpFilename, "wb"); |
2126 | | if (fpOut != nullptr) |
2127 | | { |
2128 | | nRet = CPLSpawn(papszArgs, nullptr, fpOut, FALSE); |
2129 | | VSIFCloseL(fpOut); |
2130 | | } |
2131 | | else |
2132 | | nRet = -1; |
2133 | | |
2134 | | CSLDestroy(papszArgs); |
2135 | | } |
2136 | | |
2137 | | if (nRet == 0) |
2138 | | { |
2139 | | auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
2140 | | osTmpFilename, GDAL_OF_RASTER, nullptr, nullptr, nullptr)); |
2141 | | if (poDS) |
2142 | | { |
2143 | | if (poDS->GetRasterCount() == 3) |
2144 | | { |
2145 | | eErr = poDS->RasterIO(GF_Read, 0, 0, nReqXSize, nReqYSize, |
2146 | | pabyData, nReqXSize, nReqYSize, |
2147 | | GDT_UInt8, 3, nullptr, nPixelSpace, |
2148 | | nLineSpace, nBandSpace, nullptr); |
2149 | | } |
2150 | | } |
2151 | | } |
2152 | | else |
2153 | | { |
2154 | | CPLDebug("PDF", "Ret code = %d", nRet); |
2155 | | m_bPdfToPpmFailed = true; |
2156 | | eErr = CE_Failure; |
2157 | | } |
2158 | | VSIUnlink(osTmpFilename); |
2159 | | } |
2160 | | #endif // HAVE_PODOFO |
2161 | | #ifdef HAVE_PDFIUM |
2162 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2163 | | { |
2164 | | if (!m_poPagePdfium) |
2165 | | { |
2166 | | return CE_Failure; |
2167 | | } |
2168 | | |
2169 | | // Pdfium does not support multithreading |
2170 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, PDFIUM_MUTEX_TIMEOUT); |
2171 | | |
2172 | | CPLCreateOrAcquireMutex(&(m_poPagePdfium->readMutex), |
2173 | | PDFIUM_MUTEX_TIMEOUT); |
2174 | | |
2175 | | // Parsing content required before rastering |
2176 | | // can takes too long for PDF with large number of objects/layers |
2177 | | m_poPagePdfium->page->ParseContent(); |
2178 | | |
2179 | | FPDF_BITMAP bitmap = |
2180 | | FPDFBitmap_Create(nReqXSize, nReqYSize, nBands == 4 /*alpha*/); |
2181 | | // As coded now, FPDFBitmap_Create cannot allocate more than 1 GB |
2182 | | if (bitmap == nullptr) |
2183 | | { |
2184 | | // Release mutex - following code is thread-safe |
2185 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2186 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2187 | | |
2188 | | #ifdef notdef |
2189 | | // If the requested area is not too small, then try subdividing |
2190 | | if ((GIntBig)nReqXSize * nReqYSize * 4 > 1024 * 1024) |
2191 | | { |
2192 | | #ifdef DEBUG |
2193 | | CPLDebug( |
2194 | | "PDF", |
2195 | | "Subdividing PDFDataset::ReadPixels(%d, %d, %d, %d, " |
2196 | | "scaleFactor=%d)", |
2197 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2198 | | 1 << ((PDFRasterBand *)GetRasterBand(1))->nResolutionLevel); |
2199 | | #endif |
2200 | | if (nReqXSize >= nReqYSize) |
2201 | | { |
2202 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize / 2, |
2203 | | nReqYSize, nPixelSpace, nLineSpace, |
2204 | | nBandSpace, pabyData); |
2205 | | if (eErr == CE_None) |
2206 | | { |
2207 | | eErr = ReadPixels( |
2208 | | nReqXSize / 2, nReqYOff, nReqXSize - nReqXSize / 2, |
2209 | | nReqYSize, nPixelSpace, nLineSpace, nBandSpace, |
2210 | | pabyData + nPixelSpace * (nReqXSize / 2)); |
2211 | | } |
2212 | | } |
2213 | | else |
2214 | | { |
2215 | | eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize, |
2216 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2217 | | nLineSpace, nBandSpace, pabyData); |
2218 | | if (eErr == CE_None) |
2219 | | { |
2220 | | eErr = |
2221 | | ReadPixels(nReqXOff, nReqYSize / 2, nReqXSize, |
2222 | | nReqYSize - nReqYSize / 2, nPixelSpace, |
2223 | | nLineSpace, nBandSpace, |
2224 | | pabyData + nLineSpace * (nReqYSize / 2)); |
2225 | | } |
2226 | | } |
2227 | | return eErr; |
2228 | | } |
2229 | | #endif |
2230 | | |
2231 | | CPLError(CE_Failure, CPLE_AppDefined, |
2232 | | "FPDFBitmap_Create(%d,%d) failed", nReqXSize, nReqYSize); |
2233 | | |
2234 | | return CE_Failure; |
2235 | | } |
2236 | | // alpha is 0% which is transported to FF if not alpha |
2237 | | // Default background color is white |
2238 | | FPDF_DWORD color = 0x00FFFFFF; // A,R,G,B |
2239 | | FPDFBitmap_FillRect(bitmap, 0, 0, nReqXSize, nReqYSize, color); |
2240 | | |
2241 | | #ifdef DEBUG |
2242 | | // start_x, start_y, size_x, size_y, rotate, flags |
2243 | | CPLDebug("PDF", |
2244 | | "PDFDataset::ReadPixels(%d, %d, %d, %d, scaleFactor=%d)", |
2245 | | nReqXOff, nReqYOff, nReqXSize, nReqYSize, |
2246 | | 1 << cpl::down_cast<PDFRasterBand *>(GetRasterBand(1)) |
2247 | | ->nResolutionLevel); |
2248 | | |
2249 | | CPLDebug("PDF", "FPDF_RenderPageBitmap(%d, %d, %d, %d)", -nReqXOff, |
2250 | | -nReqYOff, nRasterXSize, nRasterYSize); |
2251 | | #endif |
2252 | | |
2253 | | // Part of PDF is render with -x, -y, page_width, page_height |
2254 | | // (not requested size!) |
2255 | | PDFiumRenderPageBitmap( |
2256 | | bitmap, FPDFPageFromIPDFPage(m_poPagePdfium->page), -nReqXOff, |
2257 | | -nReqYOff, nRasterXSize, nRasterYSize, pszRenderingOptions); |
2258 | | |
2259 | | int stride = FPDFBitmap_GetStride(bitmap); |
2260 | | const GByte *buffer = |
2261 | | reinterpret_cast<const GByte *>(FPDFBitmap_GetBuffer(bitmap)); |
2262 | | |
2263 | | // Release mutex - following code is thread-safe |
2264 | | CPLReleaseMutex(m_poPagePdfium->readMutex); |
2265 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
2266 | | |
2267 | | // Source data is B, G, R, unused. |
2268 | | // Destination data is R, G, B (,A if is alpha) |
2269 | | GByte *pabyDataR = pabyData; |
2270 | | GByte *pabyDataG = pabyData + 1 * nBandSpace; |
2271 | | GByte *pabyDataB = pabyData + 2 * nBandSpace; |
2272 | | GByte *pabyDataA = pabyData + 3 * nBandSpace; |
2273 | | // Copied from Poppler |
2274 | | int i, j; |
2275 | | for (j = 0; j < nReqYSize; j++) |
2276 | | { |
2277 | | for (i = 0; i < nReqXSize; i++) |
2278 | | { |
2279 | | pabyDataR[i * nPixelSpace] = buffer[(i * 4) + 2]; |
2280 | | pabyDataG[i * nPixelSpace] = buffer[(i * 4) + 1]; |
2281 | | pabyDataB[i * nPixelSpace] = buffer[(i * 4) + 0]; |
2282 | | if (nBands == 4) |
2283 | | { |
2284 | | pabyDataA[i * nPixelSpace] = buffer[(i * 4) + 3]; |
2285 | | } |
2286 | | } |
2287 | | pabyDataR += nLineSpace; |
2288 | | pabyDataG += nLineSpace; |
2289 | | pabyDataB += nLineSpace; |
2290 | | pabyDataA += nLineSpace; |
2291 | | buffer += stride; |
2292 | | } |
2293 | | FPDFBitmap_Destroy(bitmap); |
2294 | | } |
2295 | | #endif // ~ HAVE_PDFIUM |
2296 | | |
2297 | 7.20k | return eErr; |
2298 | 7.20k | } |
2299 | | |
2300 | | /************************************************************************/ |
2301 | | /* ==================================================================== */ |
2302 | | /* PDFImageRasterBand */ |
2303 | | /* ==================================================================== */ |
2304 | | /************************************************************************/ |
2305 | | |
2306 | | class PDFImageRasterBand final : public PDFRasterBand |
2307 | | { |
2308 | | friend class PDFDataset; |
2309 | | |
2310 | | public: |
2311 | | PDFImageRasterBand(PDFDataset *, int); |
2312 | | |
2313 | | CPLErr IReadBlock(int, int, void *) override; |
2314 | | }; |
2315 | | |
2316 | | /************************************************************************/ |
2317 | | /* PDFImageRasterBand() */ |
2318 | | /************************************************************************/ |
2319 | | |
2320 | | PDFImageRasterBand::PDFImageRasterBand(PDFDataset *poDSIn, int nBandIn) |
2321 | 0 | : PDFRasterBand(poDSIn, nBandIn, 0) |
2322 | 0 | { |
2323 | 0 | } |
2324 | | |
2325 | | /************************************************************************/ |
2326 | | /* IReadBlock() */ |
2327 | | /************************************************************************/ |
2328 | | |
2329 | | CPLErr PDFImageRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
2330 | | void *pImage) |
2331 | 0 | { |
2332 | 0 | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2333 | 0 | CPLAssert(poGDS->m_poImageObj != nullptr); |
2334 | |
|
2335 | 0 | if (!poGDS->m_bTried) |
2336 | 0 | { |
2337 | 0 | int nBands = (poGDS->nBands == 1) ? 1 : 3; |
2338 | 0 | poGDS->m_bTried = true; |
2339 | 0 | if (nBands == 3) |
2340 | 0 | { |
2341 | 0 | poGDS->m_pabyCachedData = static_cast<GByte *>( |
2342 | 0 | VSIMalloc3(nBands, nRasterXSize, nRasterYSize)); |
2343 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2344 | 0 | return CE_Failure; |
2345 | 0 | } |
2346 | | |
2347 | 0 | GDALPDFStream *poStream = poGDS->m_poImageObj->GetStream(); |
2348 | 0 | GByte *pabyStream = nullptr; |
2349 | |
|
2350 | 0 | if (poStream == nullptr || |
2351 | 0 | static_cast<size_t>(poStream->GetLength()) != |
2352 | 0 | static_cast<size_t>(nBands) * nRasterXSize * nRasterYSize || |
2353 | 0 | (pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes())) == |
2354 | 0 | nullptr) |
2355 | 0 | { |
2356 | 0 | VSIFree(poGDS->m_pabyCachedData); |
2357 | 0 | poGDS->m_pabyCachedData = nullptr; |
2358 | 0 | return CE_Failure; |
2359 | 0 | } |
2360 | | |
2361 | 0 | if (nBands == 3) |
2362 | 0 | { |
2363 | | /* pixel interleaved to band interleaved */ |
2364 | 0 | for (size_t i = 0; |
2365 | 0 | i < static_cast<size_t>(nRasterXSize) * nRasterYSize; i++) |
2366 | 0 | { |
2367 | 0 | poGDS->m_pabyCachedData[0 * static_cast<size_t>(nRasterXSize) * |
2368 | 0 | nRasterYSize + |
2369 | 0 | i] = pabyStream[3 * i + 0]; |
2370 | 0 | poGDS->m_pabyCachedData[1 * static_cast<size_t>(nRasterXSize) * |
2371 | 0 | nRasterYSize + |
2372 | 0 | i] = pabyStream[3 * i + 1]; |
2373 | 0 | poGDS->m_pabyCachedData[2 * static_cast<size_t>(nRasterXSize) * |
2374 | 0 | nRasterYSize + |
2375 | 0 | i] = pabyStream[3 * i + 2]; |
2376 | 0 | } |
2377 | 0 | VSIFree(pabyStream); |
2378 | 0 | } |
2379 | 0 | else |
2380 | 0 | poGDS->m_pabyCachedData = pabyStream; |
2381 | 0 | } |
2382 | | |
2383 | 0 | if (poGDS->m_pabyCachedData == nullptr) |
2384 | 0 | return CE_Failure; |
2385 | | |
2386 | 0 | if (nBand == 4) |
2387 | 0 | memset(pImage, 255, nRasterXSize); |
2388 | 0 | else |
2389 | 0 | memcpy(pImage, |
2390 | 0 | poGDS->m_pabyCachedData + |
2391 | 0 | static_cast<size_t>(nBand - 1) * nRasterXSize * |
2392 | 0 | nRasterYSize + |
2393 | 0 | static_cast<size_t>(nBlockYOff) * nRasterXSize, |
2394 | 0 | nRasterXSize); |
2395 | |
|
2396 | 0 | return CE_None; |
2397 | 0 | } |
2398 | | |
2399 | | /************************************************************************/ |
2400 | | /* PDFDataset() */ |
2401 | | /************************************************************************/ |
2402 | | |
2403 | | PDFDataset::PDFDataset(PDFDataset *poParentDSIn, int nXSize, int nYSize) |
2404 | 31.9k | : m_bIsOvrDS(poParentDSIn != nullptr), |
2405 | | #ifdef HAVE_PDFIUM |
2406 | | m_poDocPdfium(poParentDSIn ? poParentDSIn->m_poDocPdfium : nullptr), |
2407 | | m_poPagePdfium(poParentDSIn ? poParentDSIn->m_poPagePdfium : nullptr), |
2408 | | #endif |
2409 | 31.9k | m_bSetStyle(CPLTestBool(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES"))) |
2410 | 31.9k | { |
2411 | 31.9k | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
2412 | 31.9k | nRasterXSize = nXSize; |
2413 | 31.9k | nRasterYSize = nYSize; |
2414 | 31.9k | if (poParentDSIn) |
2415 | 0 | m_bUseLib = poParentDSIn->m_bUseLib; |
2416 | | |
2417 | 31.9k | InitMapOperators(); |
2418 | 31.9k | } |
2419 | | |
2420 | | /************************************************************************/ |
2421 | | /* IBuildOverviews() */ |
2422 | | /************************************************************************/ |
2423 | | |
2424 | | CPLErr PDFDataset::IBuildOverviews(const char *pszResampling, int nOverviews, |
2425 | | const int *panOverviewList, int nListBands, |
2426 | | const int *panBandList, |
2427 | | GDALProgressFunc pfnProgress, |
2428 | | void *pProgressData, |
2429 | | CSLConstList papszOptions) |
2430 | | |
2431 | 0 | { |
2432 | | /* -------------------------------------------------------------------- */ |
2433 | | /* In order for building external overviews to work properly we */ |
2434 | | /* discard any concept of internal overviews when the user */ |
2435 | | /* first requests to build external overviews. */ |
2436 | | /* -------------------------------------------------------------------- */ |
2437 | 0 | if (!m_apoOvrDS.empty()) |
2438 | 0 | { |
2439 | 0 | m_apoOvrDSBackup = std::move(m_apoOvrDS); |
2440 | 0 | m_apoOvrDS.clear(); |
2441 | 0 | } |
2442 | | |
2443 | | // Prevents InitOverviews() to run |
2444 | 0 | m_apoOvrDSBackup.emplace_back(nullptr); |
2445 | 0 | const CPLErr eErr = GDALPamDataset::IBuildOverviews( |
2446 | 0 | pszResampling, nOverviews, panOverviewList, nListBands, panBandList, |
2447 | 0 | pfnProgress, pProgressData, papszOptions); |
2448 | 0 | m_apoOvrDSBackup.pop_back(); |
2449 | 0 | return eErr; |
2450 | 0 | } |
2451 | | |
2452 | | /************************************************************************/ |
2453 | | /* PDFFreeDoc() */ |
2454 | | /************************************************************************/ |
2455 | | |
2456 | | #ifdef HAVE_POPPLER |
2457 | | static void PDFFreeDoc(PDFDoc *poDoc) |
2458 | 42.9k | { |
2459 | 42.9k | if (poDoc) |
2460 | 42.9k | { |
2461 | | /* hack to avoid potential cross heap issues on Win32 */ |
2462 | | /* str is the VSIPDFFileStream object passed in the constructor of |
2463 | | * PDFDoc */ |
2464 | | // NOTE: This is potentially very dangerous. See comment in |
2465 | | // VSIPDFFileStream::FillBuffer() */ |
2466 | 42.9k | delete poDoc->str; |
2467 | 42.9k | poDoc->str = nullptr; |
2468 | | |
2469 | 42.9k | delete poDoc; |
2470 | 42.9k | } |
2471 | 42.9k | } |
2472 | | #endif |
2473 | | |
2474 | | /************************************************************************/ |
2475 | | /* GetCatalog() */ |
2476 | | /************************************************************************/ |
2477 | | |
2478 | | GDALPDFObject *PDFDataset::GetCatalog() |
2479 | 77.0k | { |
2480 | 77.0k | if (m_poCatalogObject) |
2481 | 45.0k | return m_poCatalogObject; |
2482 | | |
2483 | 31.9k | #ifdef HAVE_POPPLER |
2484 | 31.9k | if (m_bUseLib.test(PDFLIB_POPPLER) && m_poDocPoppler) |
2485 | 31.9k | { |
2486 | 31.9k | m_poCatalogObjectPoppler = |
2487 | 31.9k | std::make_unique<Object>(m_poDocPoppler->getXRef()->getCatalog()); |
2488 | 31.9k | if (!m_poCatalogObjectPoppler->isNull()) |
2489 | 31.9k | m_poCatalogObject = |
2490 | 31.9k | new GDALPDFObjectPoppler(m_poCatalogObjectPoppler.get(), FALSE); |
2491 | 31.9k | } |
2492 | 31.9k | #endif |
2493 | | |
2494 | | #ifdef HAVE_PODOFO |
2495 | | if (m_bUseLib.test(PDFLIB_PODOFO) && m_poDocPodofo) |
2496 | | { |
2497 | | int nCatalogNum = 0; |
2498 | | int nCatalogGen = 0; |
2499 | | VSILFILE *fp = VSIFOpenL(m_osFilename.c_str(), "rb"); |
2500 | | if (fp != nullptr) |
2501 | | { |
2502 | | GDALPDFUpdateWriter oWriter(fp); |
2503 | | if (oWriter.ParseTrailerAndXRef()) |
2504 | | { |
2505 | | nCatalogNum = oWriter.GetCatalogNum().toInt(); |
2506 | | nCatalogGen = oWriter.GetCatalogGen(); |
2507 | | } |
2508 | | oWriter.Close(); |
2509 | | } |
2510 | | |
2511 | | PoDoFo::PdfObject *poCatalogPodofo = |
2512 | | m_poDocPodofo->GetObjects().GetObject( |
2513 | | PoDoFo::PdfReference(nCatalogNum, nCatalogGen)); |
2514 | | if (poCatalogPodofo) |
2515 | | m_poCatalogObject = new GDALPDFObjectPodofo( |
2516 | | poCatalogPodofo, m_poDocPodofo->GetObjects()); |
2517 | | } |
2518 | | #endif |
2519 | | |
2520 | | #ifdef HAVE_PDFIUM |
2521 | | if (m_bUseLib.test(PDFLIB_PDFIUM) && m_poDocPdfium) |
2522 | | { |
2523 | | RetainPtr<CPDF_Dictionary> catalog = |
2524 | | m_poDocPdfium->doc->GetMutableRoot(); |
2525 | | if (catalog) |
2526 | | m_poCatalogObject = GDALPDFObjectPdfium::Build(catalog); |
2527 | | } |
2528 | | #endif // ~ HAVE_PDFIUM |
2529 | | |
2530 | 31.9k | return m_poCatalogObject; |
2531 | 77.0k | } |
2532 | | |
2533 | | /************************************************************************/ |
2534 | | /* ~PDFDataset() */ |
2535 | | /************************************************************************/ |
2536 | | |
2537 | | PDFDataset::~PDFDataset() |
2538 | 31.9k | { |
2539 | | #ifdef HAVE_PDFIUM |
2540 | | m_apoOvrDS.clear(); |
2541 | | m_apoOvrDSBackup.clear(); |
2542 | | #endif |
2543 | | |
2544 | 31.9k | CPLFree(m_pabyCachedData); |
2545 | 31.9k | m_pabyCachedData = nullptr; |
2546 | | |
2547 | 31.9k | delete m_poNeatLine; |
2548 | 31.9k | m_poNeatLine = nullptr; |
2549 | | |
2550 | | /* Collect data necessary to update */ |
2551 | 31.9k | int nNum = 0; |
2552 | 31.9k | int nGen = 0; |
2553 | 31.9k | GDALPDFDictionaryRW *poPageDictCopy = nullptr; |
2554 | 31.9k | GDALPDFDictionaryRW *poCatalogDictCopy = nullptr; |
2555 | 31.9k | if (m_poPageObj) |
2556 | 31.9k | { |
2557 | 31.9k | nNum = m_poPageObj->GetRefNum().toInt(); |
2558 | 31.9k | nGen = m_poPageObj->GetRefGen(); |
2559 | 31.9k | if (eAccess == GA_Update && |
2560 | 0 | (m_bProjDirty || m_bNeatLineDirty || m_bInfoDirty || m_bXMPDirty) && |
2561 | 0 | nNum != 0 && m_poPageObj != nullptr && |
2562 | 0 | m_poPageObj->GetType() == PDFObjectType_Dictionary) |
2563 | 0 | { |
2564 | 0 | poPageDictCopy = m_poPageObj->GetDictionary()->Clone(); |
2565 | |
|
2566 | 0 | if (m_bXMPDirty) |
2567 | 0 | { |
2568 | | /* We need the catalog because it points to the XMP Metadata |
2569 | | * object */ |
2570 | 0 | GetCatalog(); |
2571 | 0 | if (m_poCatalogObject && |
2572 | 0 | m_poCatalogObject->GetType() == PDFObjectType_Dictionary) |
2573 | 0 | poCatalogDictCopy = |
2574 | 0 | m_poCatalogObject->GetDictionary()->Clone(); |
2575 | 0 | } |
2576 | 0 | } |
2577 | 31.9k | } |
2578 | | |
2579 | | /* Close document (and file descriptor) to be able to open it */ |
2580 | | /* in read-write mode afterwards */ |
2581 | 31.9k | delete m_poPageObj; |
2582 | 31.9k | m_poPageObj = nullptr; |
2583 | 31.9k | delete m_poCatalogObject; |
2584 | 31.9k | m_poCatalogObject = nullptr; |
2585 | 31.9k | #ifdef HAVE_POPPLER |
2586 | 31.9k | if (m_bUseLib.test(PDFLIB_POPPLER)) |
2587 | 31.9k | { |
2588 | 31.9k | m_poCatalogObjectPoppler.reset(); |
2589 | 31.9k | PDFFreeDoc(m_poDocPoppler); |
2590 | 31.9k | } |
2591 | 31.9k | m_poDocPoppler = nullptr; |
2592 | 31.9k | #endif |
2593 | | #ifdef HAVE_PODOFO |
2594 | | if (m_bUseLib.test(PDFLIB_PODOFO)) |
2595 | | { |
2596 | | delete m_poDocPodofo; |
2597 | | } |
2598 | | m_poDocPodofo = nullptr; |
2599 | | #endif |
2600 | | #ifdef HAVE_PDFIUM |
2601 | | if (!m_bIsOvrDS) |
2602 | | { |
2603 | | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
2604 | | { |
2605 | | UnloadPdfiumDocumentPage(&m_poDocPdfium, &m_poPagePdfium); |
2606 | | } |
2607 | | } |
2608 | | m_poDocPdfium = nullptr; |
2609 | | m_poPagePdfium = nullptr; |
2610 | | #endif // ~ HAVE_PDFIUM |
2611 | | |
2612 | 31.9k | m_bHasLoadedLayers = true; |
2613 | 31.9k | m_apoLayers.clear(); |
2614 | | |
2615 | | /* Now do the update */ |
2616 | 31.9k | if (poPageDictCopy) |
2617 | 0 | { |
2618 | 0 | VSILFILE *fp = VSIFOpenL(m_osFilename, "rb+"); |
2619 | 0 | if (fp != nullptr) |
2620 | 0 | { |
2621 | 0 | GDALPDFUpdateWriter oWriter(fp); |
2622 | 0 | if (oWriter.ParseTrailerAndXRef()) |
2623 | 0 | { |
2624 | 0 | if ((m_bProjDirty || m_bNeatLineDirty) && |
2625 | 0 | poPageDictCopy != nullptr) |
2626 | 0 | oWriter.UpdateProj(this, m_dfDPI, poPageDictCopy, |
2627 | 0 | GDALPDFObjectNum(nNum), nGen); |
2628 | |
|
2629 | 0 | if (m_bInfoDirty) |
2630 | 0 | oWriter.UpdateInfo(this); |
2631 | |
|
2632 | 0 | if (m_bXMPDirty && poCatalogDictCopy != nullptr) |
2633 | 0 | oWriter.UpdateXMP(this, poCatalogDictCopy); |
2634 | 0 | } |
2635 | 0 | oWriter.Close(); |
2636 | 0 | } |
2637 | 0 | else |
2638 | 0 | { |
2639 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2640 | 0 | "Cannot open %s in update mode", m_osFilename.c_str()); |
2641 | 0 | } |
2642 | 0 | } |
2643 | 31.9k | delete poPageDictCopy; |
2644 | 31.9k | poPageDictCopy = nullptr; |
2645 | 31.9k | delete poCatalogDictCopy; |
2646 | 31.9k | poCatalogDictCopy = nullptr; |
2647 | | |
2648 | 31.9k | if (m_nGCPCount > 0) |
2649 | 21 | { |
2650 | 21 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
2651 | 21 | CPLFree(m_pasGCPList); |
2652 | 21 | m_pasGCPList = nullptr; |
2653 | 21 | m_nGCPCount = 0; |
2654 | 21 | } |
2655 | | |
2656 | 31.9k | CleanupIntermediateResources(); |
2657 | | |
2658 | | // Do that only after having destroyed Poppler objects |
2659 | 31.9k | m_fp.reset(); |
2660 | 31.9k | } |
2661 | | |
2662 | | /************************************************************************/ |
2663 | | /* IRasterIO() */ |
2664 | | /************************************************************************/ |
2665 | | |
2666 | | CPLErr PDFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2667 | | int nXSize, int nYSize, void *pData, int nBufXSize, |
2668 | | int nBufYSize, GDALDataType eBufType, |
2669 | | int nBandCount, BANDMAP_TYPE panBandMap, |
2670 | | GSpacing nPixelSpace, GSpacing nLineSpace, |
2671 | | GSpacing nBandSpace, |
2672 | | GDALRasterIOExtraArg *psExtraArg) |
2673 | 0 | { |
2674 | | // Try to pass the request to the most appropriate overview dataset. |
2675 | 0 | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2676 | 0 | { |
2677 | 0 | int bTried = FALSE; |
2678 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2679 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2680 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, |
2681 | 0 | nBandSpace, psExtraArg, &bTried); |
2682 | 0 | if (bTried) |
2683 | 0 | return eErr; |
2684 | 0 | } |
2685 | | |
2686 | 0 | int nBandBlockXSize, nBandBlockYSize; |
2687 | 0 | int bReadPixels = FALSE; |
2688 | 0 | GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize); |
2689 | 0 | if (m_aiTiles.empty() && eRWFlag == GF_Read && nXSize == nBufXSize && |
2690 | 0 | nYSize == nBufYSize && |
2691 | 0 | (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) && |
2692 | 0 | eBufType == GDT_UInt8 && nBandCount == nBands && |
2693 | 0 | IsAllBands(nBandCount, panBandMap)) |
2694 | 0 | { |
2695 | 0 | bReadPixels = TRUE; |
2696 | | #ifdef HAVE_PODOFO |
2697 | | if (m_bUseLib.test(PDFLIB_PODOFO) && nBands == 4) |
2698 | | { |
2699 | | bReadPixels = FALSE; |
2700 | | } |
2701 | | #endif |
2702 | 0 | } |
2703 | |
|
2704 | 0 | if (bReadPixels) |
2705 | 0 | return ReadPixels(nXOff, nYOff, nXSize, nYSize, nPixelSpace, nLineSpace, |
2706 | 0 | nBandSpace, static_cast<GByte *>(pData)); |
2707 | | |
2708 | 0 | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2709 | 0 | { |
2710 | 0 | m_bCacheBlocksForOtherBands = true; |
2711 | 0 | } |
2712 | 0 | CPLErr eErr = GDALPamDataset::IRasterIO( |
2713 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2714 | 0 | eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, |
2715 | 0 | psExtraArg); |
2716 | 0 | m_bCacheBlocksForOtherBands = false; |
2717 | 0 | return eErr; |
2718 | 0 | } |
2719 | | |
2720 | | /************************************************************************/ |
2721 | | /* IRasterIO() */ |
2722 | | /************************************************************************/ |
2723 | | |
2724 | | CPLErr PDFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
2725 | | int nXSize, int nYSize, void *pData, |
2726 | | int nBufXSize, int nBufYSize, |
2727 | | GDALDataType eBufType, GSpacing nPixelSpace, |
2728 | | GSpacing nLineSpace, |
2729 | | GDALRasterIOExtraArg *psExtraArg) |
2730 | 9.23M | { |
2731 | 9.23M | PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS); |
2732 | | |
2733 | | // Try to pass the request to the most appropriate overview dataset. |
2734 | 9.23M | if (nBufXSize < nXSize && nBufYSize < nYSize) |
2735 | 0 | { |
2736 | 0 | int bTried = FALSE; |
2737 | 0 | const CPLErr eErr = TryOverviewRasterIO( |
2738 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2739 | 0 | eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried); |
2740 | 0 | if (bTried) |
2741 | 0 | return eErr; |
2742 | 0 | } |
2743 | | |
2744 | 9.23M | if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8) |
2745 | 9.23M | { |
2746 | 9.23M | poGDS->m_bCacheBlocksForOtherBands = true; |
2747 | 9.23M | } |
2748 | 9.23M | CPLErr eErr = GDALPamRasterBand::IRasterIO( |
2749 | 9.23M | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
2750 | 9.23M | eBufType, nPixelSpace, nLineSpace, psExtraArg); |
2751 | 9.23M | poGDS->m_bCacheBlocksForOtherBands = false; |
2752 | 9.23M | return eErr; |
2753 | 9.23M | } |
2754 | | |
2755 | | /************************************************************************/ |
2756 | | /* PDFDatasetErrorFunction() */ |
2757 | | /************************************************************************/ |
2758 | | |
2759 | | #ifdef HAVE_POPPLER |
2760 | | |
2761 | | static void PDFDatasetErrorFunctionCommon(const CPLString &osError) |
2762 | 14.6M | { |
2763 | 14.6M | if (strcmp(osError.c_str(), "Incorrect password") == 0) |
2764 | 71 | return; |
2765 | | /* Reported on newer USGS GeoPDF */ |
2766 | 14.6M | if (strcmp(osError.c_str(), |
2767 | 14.6M | "Couldn't find group for reference to set OFF") == 0) |
2768 | 543 | { |
2769 | 543 | CPLDebug("PDF", "%s", osError.c_str()); |
2770 | 543 | return; |
2771 | 543 | } |
2772 | | |
2773 | 14.6M | CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str()); |
2774 | 14.6M | } |
2775 | | |
2776 | | static int g_nPopplerErrors = 0; |
2777 | | constexpr int MAX_POPPLER_ERRORS = 1000; |
2778 | | |
2779 | | static void PDFDatasetErrorFunction(ErrorCategory /* eErrCategory */, |
2780 | | Goffset nPos, const char *pszMsg) |
2781 | 14.6M | { |
2782 | 14.6M | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
2783 | 9.76k | { |
2784 | | // If there are too many errors, then unregister ourselves and turn |
2785 | | // quiet error mode, as the error() function in poppler can spend |
2786 | | // significant time formatting an error message we won't emit... |
2787 | 9.76k | setErrorCallback(nullptr); |
2788 | 9.76k | globalParams->setErrQuiet(true); |
2789 | 9.76k | return; |
2790 | 9.76k | } |
2791 | | |
2792 | 14.6M | g_nPopplerErrors++; |
2793 | 14.6M | CPLString osError; |
2794 | | |
2795 | 14.6M | if (nPos >= 0) |
2796 | 13.8M | osError.Printf("Pos = " CPL_FRMT_GUIB ", ", |
2797 | 13.8M | static_cast<GUIntBig>(nPos)); |
2798 | 14.6M | osError += pszMsg; |
2799 | 14.6M | PDFDatasetErrorFunctionCommon(osError); |
2800 | 14.6M | } |
2801 | | #endif |
2802 | | |
2803 | | /************************************************************************/ |
2804 | | /* GDALPDFParseStreamContentOnlyDrawForm() */ |
2805 | | /************************************************************************/ |
2806 | | |
2807 | | static CPLString GDALPDFParseStreamContentOnlyDrawForm(const char *pszContent) |
2808 | 3.68k | { |
2809 | 3.68k | CPLString osToken; |
2810 | 3.68k | char ch; |
2811 | 3.68k | int nCurIdx = 0; |
2812 | 3.68k | CPLString osCurrentForm; |
2813 | | |
2814 | | // CPLDebug("PDF", "content = %s", pszContent); |
2815 | | |
2816 | 482k | while ((ch = *pszContent) != '\0') |
2817 | 481k | { |
2818 | 481k | if (ch == '%') |
2819 | 1.89k | { |
2820 | | /* Skip comments until end-of-line */ |
2821 | 24.4k | while ((ch = *pszContent) != '\0') |
2822 | 24.4k | { |
2823 | 24.4k | if (ch == '\r' || ch == '\n') |
2824 | 1.88k | break; |
2825 | 22.5k | pszContent++; |
2826 | 22.5k | } |
2827 | 1.89k | if (ch == 0) |
2828 | 6 | break; |
2829 | 1.89k | } |
2830 | 479k | else if (ch == ' ' || ch == '\r' || ch == '\n') |
2831 | 9.79k | { |
2832 | 9.79k | if (!osToken.empty()) |
2833 | 5.52k | { |
2834 | 5.52k | if (nCurIdx == 0 && osToken[0] == '/') |
2835 | 2.08k | { |
2836 | 2.08k | osCurrentForm = osToken.substr(1); |
2837 | 2.08k | nCurIdx++; |
2838 | 2.08k | } |
2839 | 3.43k | else if (nCurIdx == 1 && osToken == "Do") |
2840 | 36 | { |
2841 | 36 | nCurIdx++; |
2842 | 36 | } |
2843 | 3.40k | else |
2844 | 3.40k | { |
2845 | 3.40k | return ""; |
2846 | 3.40k | } |
2847 | 5.52k | } |
2848 | 6.39k | osToken = ""; |
2849 | 6.39k | } |
2850 | 470k | else |
2851 | 470k | osToken += ch; |
2852 | 478k | pszContent++; |
2853 | 478k | } |
2854 | | |
2855 | 282 | return osCurrentForm; |
2856 | 3.68k | } |
2857 | | |
2858 | | /************************************************************************/ |
2859 | | /* GDALPDFParseStreamContent() */ |
2860 | | /************************************************************************/ |
2861 | | |
2862 | | typedef enum |
2863 | | { |
2864 | | STATE_INIT, |
2865 | | STATE_AFTER_q, |
2866 | | STATE_AFTER_cm, |
2867 | | STATE_AFTER_Do |
2868 | | } PDFStreamState; |
2869 | | |
2870 | | /* This parser is reduced to understanding sequences that draw rasters, such as |
2871 | | : |
2872 | | q |
2873 | | scaleX 0 0 scaleY translateX translateY cm |
2874 | | /ImXXX Do |
2875 | | Q |
2876 | | |
2877 | | All other sequences will abort the parsing. |
2878 | | |
2879 | | Returns TRUE if the stream only contains images. |
2880 | | */ |
2881 | | |
2882 | | static int GDALPDFParseStreamContent(const char *pszContent, |
2883 | | GDALPDFDictionary *poXObjectDict, |
2884 | | double *pdfDPI, int *pbDPISet, |
2885 | | int *pnBands, |
2886 | | std::vector<GDALPDFTileDesc> &asTiles, |
2887 | | int bAcceptRotationTerms) |
2888 | 3.63k | { |
2889 | 3.63k | CPLString osToken; |
2890 | 3.63k | char ch; |
2891 | 3.63k | PDFStreamState nState = STATE_INIT; |
2892 | 3.63k | int nCurIdx = 0; |
2893 | 3.63k | double adfVals[6]; |
2894 | 3.63k | CPLString osCurrentImage; |
2895 | | |
2896 | 3.63k | double dfDPI = DEFAULT_DPI; |
2897 | 3.63k | *pbDPISet = FALSE; |
2898 | | |
2899 | 432k | while ((ch = *pszContent) != '\0') |
2900 | 432k | { |
2901 | 432k | if (ch == '%') |
2902 | 1.89k | { |
2903 | | /* Skip comments until end-of-line */ |
2904 | 24.4k | while ((ch = *pszContent) != '\0') |
2905 | 24.3k | { |
2906 | 24.3k | if (ch == '\r' || ch == '\n') |
2907 | 1.89k | break; |
2908 | 22.5k | pszContent++; |
2909 | 22.5k | } |
2910 | 1.89k | if (ch == 0) |
2911 | 6 | break; |
2912 | 1.89k | } |
2913 | 430k | else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') |
2914 | 17.8k | { |
2915 | 17.8k | if (!osToken.empty()) |
2916 | 11.4k | { |
2917 | 11.4k | if (nState == STATE_INIT) |
2918 | 3.64k | { |
2919 | 3.64k | if (osToken == "q") |
2920 | 954 | { |
2921 | 954 | nState = STATE_AFTER_q; |
2922 | 954 | nCurIdx = 0; |
2923 | 954 | } |
2924 | 2.68k | else if (osToken != "Q") |
2925 | 2.63k | return FALSE; |
2926 | 3.64k | } |
2927 | 7.83k | else if (nState == STATE_AFTER_q) |
2928 | 6.65k | { |
2929 | 6.65k | if (osToken == "q") |
2930 | 258 | { |
2931 | | // ignore |
2932 | 258 | } |
2933 | 6.39k | else if (nCurIdx < 6) |
2934 | 5.53k | { |
2935 | 5.53k | adfVals[nCurIdx++] = CPLAtof(osToken); |
2936 | 5.53k | } |
2937 | 866 | else if (nCurIdx == 6 && osToken == "cm") |
2938 | 504 | { |
2939 | 504 | nState = STATE_AFTER_cm; |
2940 | 504 | nCurIdx = 0; |
2941 | 504 | } |
2942 | 362 | else |
2943 | 362 | return FALSE; |
2944 | 6.65k | } |
2945 | 1.17k | else if (nState == STATE_AFTER_cm) |
2946 | 883 | { |
2947 | 883 | if (nCurIdx == 0 && osToken[0] == '/') |
2948 | 386 | { |
2949 | 386 | osCurrentImage = osToken.substr(1); |
2950 | 386 | } |
2951 | 497 | else if (osToken == "Do") |
2952 | 319 | { |
2953 | 319 | nState = STATE_AFTER_Do; |
2954 | 319 | } |
2955 | 178 | else |
2956 | 178 | return FALSE; |
2957 | 883 | } |
2958 | 292 | else if (nState == STATE_AFTER_Do) |
2959 | 292 | { |
2960 | 292 | if (osToken == "Q") |
2961 | 285 | { |
2962 | 285 | GDALPDFObject *poImage = |
2963 | 285 | poXObjectDict->Get(osCurrentImage); |
2964 | 285 | if (poImage != nullptr && |
2965 | 262 | poImage->GetType() == PDFObjectType_Dictionary) |
2966 | 256 | { |
2967 | 256 | GDALPDFTileDesc sTile; |
2968 | 256 | GDALPDFDictionary *poImageDict = |
2969 | 256 | poImage->GetDictionary(); |
2970 | 256 | GDALPDFObject *poWidth = poImageDict->Get("Width"); |
2971 | 256 | GDALPDFObject *poHeight = |
2972 | 256 | poImageDict->Get("Height"); |
2973 | 256 | GDALPDFObject *poColorSpace = |
2974 | 256 | poImageDict->Get("ColorSpace"); |
2975 | 256 | GDALPDFObject *poSMask = poImageDict->Get("SMask"); |
2976 | 256 | if (poColorSpace && |
2977 | 244 | poColorSpace->GetType() == PDFObjectType_Name) |
2978 | 154 | { |
2979 | 154 | if (poColorSpace->GetName() == "DeviceRGB") |
2980 | 80 | { |
2981 | 80 | sTile.nBands = 3; |
2982 | 80 | if (*pnBands < 3) |
2983 | 74 | *pnBands = 3; |
2984 | 80 | } |
2985 | 74 | else if (poColorSpace->GetName() == |
2986 | 74 | "DeviceGray") |
2987 | 69 | { |
2988 | 69 | sTile.nBands = 1; |
2989 | 69 | if (*pnBands < 1) |
2990 | 61 | *pnBands = 1; |
2991 | 69 | } |
2992 | 5 | else |
2993 | 5 | sTile.nBands = 0; |
2994 | 154 | } |
2995 | 256 | if (poSMask != nullptr) |
2996 | 4 | *pnBands = 4; |
2997 | | |
2998 | 256 | if (poWidth && poHeight && |
2999 | 241 | ((bAcceptRotationTerms && |
3000 | 0 | adfVals[1] == -adfVals[2]) || |
3001 | 241 | (!bAcceptRotationTerms && adfVals[1] == 0.0 && |
3002 | 240 | adfVals[2] == 0.0))) |
3003 | 238 | { |
3004 | 238 | double dfWidth = Get(poWidth); |
3005 | 238 | double dfHeight = Get(poHeight); |
3006 | 238 | double dfScaleX = adfVals[0]; |
3007 | 238 | double dfScaleY = adfVals[3]; |
3008 | 238 | if (dfWidth > 0 && dfHeight > 0 && |
3009 | 236 | dfScaleX > 0 && dfScaleY > 0 && |
3010 | 235 | dfWidth / dfScaleX * DEFAULT_DPI < |
3011 | 235 | INT_MAX && |
3012 | 235 | dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX) |
3013 | 235 | { |
3014 | 235 | double dfDPI_X = ROUND_IF_CLOSE( |
3015 | 235 | dfWidth / dfScaleX * DEFAULT_DPI, 1e-3); |
3016 | 235 | double dfDPI_Y = ROUND_IF_CLOSE( |
3017 | 235 | dfHeight / dfScaleY * DEFAULT_DPI, |
3018 | 235 | 1e-3); |
3019 | | // CPLDebug("PDF", "Image %s, width = %.16g, |
3020 | | // height = %.16g, scaleX = %.16g, scaleY = |
3021 | | // %.16g --> DPI_X = %.16g, DPI_Y = %.16g", |
3022 | | // osCurrentImage.c_str(), |
3023 | | // dfWidth, dfHeight, |
3024 | | // dfScaleX, dfScaleY, |
3025 | | // dfDPI_X, dfDPI_Y); |
3026 | 235 | if (dfDPI_X > dfDPI) |
3027 | 68 | dfDPI = dfDPI_X; |
3028 | 235 | if (dfDPI_Y > dfDPI) |
3029 | 12 | dfDPI = dfDPI_Y; |
3030 | | |
3031 | 235 | memcpy(&(sTile.adfCM), adfVals, |
3032 | 235 | 6 * sizeof(double)); |
3033 | 235 | sTile.poImage = poImage; |
3034 | 235 | sTile.dfWidth = dfWidth; |
3035 | 235 | sTile.dfHeight = dfHeight; |
3036 | 235 | asTiles.push_back(sTile); |
3037 | | |
3038 | 235 | *pbDPISet = TRUE; |
3039 | 235 | *pdfDPI = dfDPI; |
3040 | 235 | } |
3041 | 238 | } |
3042 | 256 | } |
3043 | 285 | nState = STATE_INIT; |
3044 | 285 | } |
3045 | 7 | else |
3046 | 7 | return FALSE; |
3047 | 292 | } |
3048 | 11.4k | } |
3049 | 14.6k | osToken = ""; |
3050 | 14.6k | } |
3051 | 412k | else |
3052 | 412k | osToken += ch; |
3053 | 429k | pszContent++; |
3054 | 429k | } |
3055 | | |
3056 | 455 | return TRUE; |
3057 | 3.63k | } |
3058 | | |
3059 | | /************************************************************************/ |
3060 | | /* CheckTiledRaster() */ |
3061 | | /************************************************************************/ |
3062 | | |
3063 | | int PDFDataset::CheckTiledRaster() |
3064 | 217 | { |
3065 | 217 | size_t i; |
3066 | 217 | int l_nBlockXSize = 0; |
3067 | 217 | int l_nBlockYSize = 0; |
3068 | 217 | const double dfUserUnit = m_dfDPI * USER_UNIT_IN_INCH; |
3069 | | |
3070 | | /* First pass : check that all tiles have same DPI, */ |
3071 | | /* are contained entirely in the raster size, */ |
3072 | | /* and determine the block size */ |
3073 | 282 | for (i = 0; i < m_asTiles.size(); i++) |
3074 | 219 | { |
3075 | 219 | double dfDrawWidth = m_asTiles[i].adfCM[0] * dfUserUnit; |
3076 | 219 | double dfDrawHeight = m_asTiles[i].adfCM[3] * dfUserUnit; |
3077 | 219 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3078 | 219 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3079 | 219 | int nX = static_cast<int>(dfX + 0.1); |
3080 | 219 | int nY = static_cast<int>(dfY + 0.1); |
3081 | 219 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3082 | 219 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3083 | | |
3084 | 219 | GDALPDFDictionary *poImageDict = m_asTiles[i].poImage->GetDictionary(); |
3085 | 219 | GDALPDFObject *poBitsPerComponent = |
3086 | 219 | poImageDict->Get("BitsPerComponent"); |
3087 | 219 | GDALPDFObject *poColorSpace = poImageDict->Get("ColorSpace"); |
3088 | 219 | GDALPDFObject *poFilter = poImageDict->Get("Filter"); |
3089 | | |
3090 | | /* Podofo cannot uncompress JPEG2000 streams */ |
3091 | 219 | if (m_bUseLib.test(PDFLIB_PODOFO) && poFilter != nullptr && |
3092 | 0 | poFilter->GetType() == PDFObjectType_Name && |
3093 | 0 | poFilter->GetName() == "JPXDecode") |
3094 | 0 | { |
3095 | 0 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3096 | 0 | static_cast<int>(i)); |
3097 | 0 | return FALSE; |
3098 | 0 | } |
3099 | | |
3100 | 219 | if (poBitsPerComponent == nullptr || Get(poBitsPerComponent) != 8 || |
3101 | 154 | poColorSpace == nullptr || |
3102 | 153 | poColorSpace->GetType() != PDFObjectType_Name || |
3103 | 112 | (poColorSpace->GetName() != "DeviceRGB" && |
3104 | 42 | poColorSpace->GetName() != "DeviceGray")) |
3105 | 110 | { |
3106 | 110 | CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", |
3107 | 110 | static_cast<int>(i)); |
3108 | 110 | return FALSE; |
3109 | 110 | } |
3110 | | |
3111 | 109 | if (fabs(dfDrawWidth - m_asTiles[i].dfWidth) > 1e-2 || |
3112 | 89 | fabs(dfDrawHeight - m_asTiles[i].dfHeight) > 1e-2 || |
3113 | 85 | fabs(nWidth - m_asTiles[i].dfWidth) > 1e-8 || |
3114 | 85 | fabs(nHeight - m_asTiles[i].dfHeight) > 1e-8 || |
3115 | 85 | fabs(nX - dfX) > 1e-1 || fabs(nY - dfY) > 1e-1 || nX < 0 || |
3116 | 67 | nY < 0 || nX + nWidth > nRasterXSize || nY >= nRasterYSize) |
3117 | 44 | { |
3118 | 44 | CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", static_cast<int>(i), |
3119 | 44 | dfX, dfY, dfDrawWidth, dfDrawHeight, m_asTiles[i].dfWidth, |
3120 | 44 | m_asTiles[i].dfHeight); |
3121 | 44 | return FALSE; |
3122 | 44 | } |
3123 | 65 | if (l_nBlockXSize == 0 && l_nBlockYSize == 0 && nX == 0 && nY != 0) |
3124 | 0 | { |
3125 | 0 | l_nBlockXSize = nWidth; |
3126 | 0 | l_nBlockYSize = nHeight; |
3127 | 0 | } |
3128 | 65 | } |
3129 | 63 | if (l_nBlockXSize <= 0 || l_nBlockYSize <= 0 || l_nBlockXSize > 2048 || |
3130 | 0 | l_nBlockYSize > 2048) |
3131 | 63 | return FALSE; |
3132 | | |
3133 | 0 | int nXBlocks = DIV_ROUND_UP(nRasterXSize, l_nBlockXSize); |
3134 | 0 | int nYBlocks = DIV_ROUND_UP(nRasterYSize, l_nBlockYSize); |
3135 | | |
3136 | | /* Second pass to determine that all tiles are properly aligned on block |
3137 | | * size */ |
3138 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3139 | 0 | { |
3140 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3141 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3142 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3143 | 0 | int nY = static_cast<int>(dfY + 0.1); |
3144 | 0 | int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8); |
3145 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3146 | 0 | int bOK = TRUE; |
3147 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3148 | 0 | if ((nX % l_nBlockXSize) != 0) |
3149 | 0 | bOK = FALSE; |
3150 | 0 | if (nBlockXOff < nXBlocks - 1 && nWidth != l_nBlockXSize) |
3151 | 0 | bOK = FALSE; |
3152 | 0 | if (nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize) |
3153 | 0 | bOK = FALSE; |
3154 | |
|
3155 | 0 | if (nY > 0 && nHeight != l_nBlockYSize) |
3156 | 0 | bOK = FALSE; |
3157 | 0 | if (nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * l_nBlockYSize) |
3158 | 0 | bOK = FALSE; |
3159 | |
|
3160 | 0 | if (!bOK) |
3161 | 0 | { |
3162 | 0 | CPLDebug("PDF", "Tile %d : %d %d %d %d", static_cast<int>(i), nX, |
3163 | 0 | nY, nWidth, nHeight); |
3164 | 0 | return FALSE; |
3165 | 0 | } |
3166 | 0 | } |
3167 | | |
3168 | | /* Third pass to set the aiTiles array */ |
3169 | 0 | m_aiTiles.resize(static_cast<size_t>(nXBlocks) * nYBlocks, -1); |
3170 | 0 | for (i = 0; i < m_asTiles.size(); i++) |
3171 | 0 | { |
3172 | 0 | double dfX = m_asTiles[i].adfCM[4] * dfUserUnit; |
3173 | 0 | double dfY = m_asTiles[i].adfCM[5] * dfUserUnit; |
3174 | 0 | int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8); |
3175 | 0 | int nX = static_cast<int>(dfX + 0.1); |
3176 | 0 | int nY = nRasterYSize - (static_cast<int>(dfY + 0.1) + nHeight); |
3177 | 0 | int nBlockXOff = nX / l_nBlockXSize; |
3178 | 0 | int nBlockYOff = nY / l_nBlockYSize; |
3179 | 0 | m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff] = static_cast<int>(i); |
3180 | 0 | } |
3181 | |
|
3182 | 0 | this->m_nBlockXSize = l_nBlockXSize; |
3183 | 0 | this->m_nBlockYSize = l_nBlockYSize; |
3184 | |
|
3185 | 0 | return TRUE; |
3186 | 0 | } |
3187 | | |
3188 | | /************************************************************************/ |
3189 | | /* GuessDPI() */ |
3190 | | /************************************************************************/ |
3191 | | |
3192 | | void PDFDataset::GuessDPI(GDALPDFDictionary *poPageDict, int *pnBands) |
3193 | 31.9k | { |
3194 | 31.9k | const char *pszDPI = GetOption(papszOpenOptions, "DPI", nullptr); |
3195 | 31.9k | if (pszDPI != nullptr) |
3196 | 18.4k | { |
3197 | | // coverity[tainted_data] |
3198 | 18.4k | m_dfDPI = CPLAtof(pszDPI); |
3199 | 18.4k | } |
3200 | 13.5k | else |
3201 | 13.5k | { |
3202 | | /* Try to get a better value from the images that are drawn */ |
3203 | | /* Very simplistic logic. Will only work for raster only PDF */ |
3204 | | |
3205 | 13.5k | GDALPDFObject *poContents = poPageDict->Get("Contents"); |
3206 | 13.5k | if (poContents != nullptr && |
3207 | 10.3k | poContents->GetType() == PDFObjectType_Array) |
3208 | 695 | { |
3209 | 695 | GDALPDFArray *poContentsArray = poContents->GetArray(); |
3210 | 695 | if (poContentsArray->GetLength() == 1) |
3211 | 87 | { |
3212 | 87 | poContents = poContentsArray->Get(0); |
3213 | 87 | } |
3214 | 695 | } |
3215 | | |
3216 | 13.5k | GDALPDFObject *poXObject = |
3217 | 13.5k | poPageDict->LookupObject("Resources.XObject"); |
3218 | 13.5k | if (poContents != nullptr && |
3219 | 10.3k | poContents->GetType() == PDFObjectType_Dictionary && |
3220 | 9.65k | poXObject != nullptr && |
3221 | 4.06k | poXObject->GetType() == PDFObjectType_Dictionary) |
3222 | 3.94k | { |
3223 | 3.94k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
3224 | 3.94k | GDALPDFDictionary *poContentDict = poXObjectDict; |
3225 | 3.94k | GDALPDFStream *poPageStream = poContents->GetStream(); |
3226 | 3.94k | if (poPageStream != nullptr) |
3227 | 3.73k | { |
3228 | 3.73k | char *pszContent = nullptr; |
3229 | 3.73k | const int64_t MAX_LENGTH = 10 * 1000 * 1000; |
3230 | 3.73k | int64_t nLength = poPageStream->GetLength(MAX_LENGTH); |
3231 | 3.73k | int bResetTiles = FALSE; |
3232 | 3.73k | double dfScaleDPI = 1.0; |
3233 | | |
3234 | 3.73k | if (nLength < MAX_LENGTH) |
3235 | 3.73k | { |
3236 | 3.73k | CPLString osForm; |
3237 | 3.73k | pszContent = poPageStream->GetBytes(); |
3238 | 3.73k | if (pszContent != nullptr) |
3239 | 3.68k | { |
3240 | | #ifdef DEBUG |
3241 | | const char *pszDumpStream = |
3242 | | CPLGetConfigOption("PDF_DUMP_STREAM", nullptr); |
3243 | | if (pszDumpStream != nullptr) |
3244 | | { |
3245 | | VSILFILE *fpDump = VSIFOpenL(pszDumpStream, "wb"); |
3246 | | if (fpDump) |
3247 | | { |
3248 | | VSIFWriteL(pszContent, 1, |
3249 | | static_cast<int>(nLength), fpDump); |
3250 | | VSIFCloseL(fpDump); |
3251 | | } |
3252 | | } |
3253 | | #endif // DEBUG |
3254 | 3.68k | osForm = |
3255 | 3.68k | GDALPDFParseStreamContentOnlyDrawForm(pszContent); |
3256 | 3.68k | if (osForm.empty()) |
3257 | 3.60k | { |
3258 | | /* Special case for USGS Topo PDF, like |
3259 | | * CA_Hollywood_20090811_OM_geo.pdf */ |
3260 | 3.60k | const char *pszOGCDo = |
3261 | 3.60k | strstr(pszContent, " /XO1 Do"); |
3262 | 3.60k | if (pszOGCDo) |
3263 | 13 | { |
3264 | 13 | const char *pszcm = strstr(pszContent, " cm "); |
3265 | 13 | if (pszcm != nullptr && pszcm < pszOGCDo) |
3266 | 0 | { |
3267 | 0 | const char *pszNextcm = |
3268 | 0 | strstr(pszcm + 2, "cm"); |
3269 | 0 | if (pszNextcm == nullptr || |
3270 | 0 | pszNextcm > pszOGCDo) |
3271 | 0 | { |
3272 | 0 | const char *pszIter = pszcm; |
3273 | 0 | while (pszIter > pszContent) |
3274 | 0 | { |
3275 | 0 | if ((*pszIter >= '0' && |
3276 | 0 | *pszIter <= '9') || |
3277 | 0 | *pszIter == '-' || |
3278 | 0 | *pszIter == '.' || |
3279 | 0 | *pszIter == ' ') |
3280 | 0 | pszIter--; |
3281 | 0 | else |
3282 | 0 | { |
3283 | 0 | pszIter++; |
3284 | 0 | break; |
3285 | 0 | } |
3286 | 0 | } |
3287 | 0 | CPLString oscm(pszIter); |
3288 | 0 | oscm.resize(pszcm - pszIter); |
3289 | 0 | char **papszTokens = |
3290 | 0 | CSLTokenizeString(oscm); |
3291 | 0 | double dfScaleX = -1.0; |
3292 | 0 | double dfScaleY = -2.0; |
3293 | 0 | if (CSLCount(papszTokens) == 6) |
3294 | 0 | { |
3295 | 0 | dfScaleX = CPLAtof(papszTokens[0]); |
3296 | 0 | dfScaleY = CPLAtof(papszTokens[3]); |
3297 | 0 | } |
3298 | 0 | CSLDestroy(papszTokens); |
3299 | 0 | if (dfScaleX == dfScaleY && |
3300 | 0 | dfScaleX > 0.0) |
3301 | 0 | { |
3302 | 0 | osForm = "XO1"; |
3303 | 0 | bResetTiles = TRUE; |
3304 | 0 | dfScaleDPI = 1.0 / dfScaleX; |
3305 | 0 | } |
3306 | 0 | } |
3307 | 0 | } |
3308 | 13 | else |
3309 | 13 | { |
3310 | 13 | osForm = "XO1"; |
3311 | 13 | bResetTiles = TRUE; |
3312 | 13 | } |
3313 | 13 | } |
3314 | | /* Special case for USGS Topo PDF, like |
3315 | | * CA_Sacramento_East_20120308_TM_geo.pdf */ |
3316 | 3.58k | else |
3317 | 3.58k | { |
3318 | 3.58k | CPLString osOCG = |
3319 | 3.58k | FindLayerOCG(poPageDict, "Orthoimage"); |
3320 | 3.58k | if (!osOCG.empty()) |
3321 | 0 | { |
3322 | 0 | const char *pszBDCLookup = CPLSPrintf( |
3323 | 0 | "/OC /%s BDC", osOCG.c_str()); |
3324 | 0 | const char *pszBDC = |
3325 | 0 | strstr(pszContent, pszBDCLookup); |
3326 | 0 | if (pszBDC != nullptr) |
3327 | 0 | { |
3328 | 0 | const char *pszIter = |
3329 | 0 | pszBDC + strlen(pszBDCLookup); |
3330 | 0 | while (*pszIter != '\0') |
3331 | 0 | { |
3332 | 0 | if (*pszIter == 13 || |
3333 | 0 | *pszIter == 10 || |
3334 | 0 | *pszIter == ' ' || |
3335 | 0 | *pszIter == 'q') |
3336 | 0 | pszIter++; |
3337 | 0 | else |
3338 | 0 | break; |
3339 | 0 | } |
3340 | 0 | if (STARTS_WITH(pszIter, |
3341 | 0 | "1 0 0 1 0 0 cm\n")) |
3342 | 0 | pszIter += |
3343 | 0 | strlen("1 0 0 1 0 0 cm\n"); |
3344 | 0 | if (*pszIter == '/') |
3345 | 0 | { |
3346 | 0 | pszIter++; |
3347 | 0 | const char *pszDo = |
3348 | 0 | strstr(pszIter, " Do"); |
3349 | 0 | if (pszDo != nullptr) |
3350 | 0 | { |
3351 | 0 | osForm = pszIter; |
3352 | 0 | osForm.resize(pszDo - pszIter); |
3353 | 0 | bResetTiles = TRUE; |
3354 | 0 | } |
3355 | 0 | } |
3356 | 0 | } |
3357 | 0 | } |
3358 | 3.58k | } |
3359 | 3.60k | } |
3360 | 3.68k | } |
3361 | | |
3362 | 3.73k | if (!osForm.empty()) |
3363 | 97 | { |
3364 | 97 | CPLFree(pszContent); |
3365 | 97 | pszContent = nullptr; |
3366 | | |
3367 | 97 | GDALPDFObject *poObjForm = poXObjectDict->Get(osForm); |
3368 | 97 | if (poObjForm != nullptr && |
3369 | 48 | poObjForm->GetType() == PDFObjectType_Dictionary && |
3370 | 48 | (poPageStream = poObjForm->GetStream()) != nullptr) |
3371 | 48 | { |
3372 | 48 | GDALPDFDictionary *poObjFormDict = |
3373 | 48 | poObjForm->GetDictionary(); |
3374 | 48 | GDALPDFObject *poSubtype = |
3375 | 48 | poObjFormDict->Get("Subtype"); |
3376 | 48 | if (poSubtype != nullptr && |
3377 | 48 | poSubtype->GetType() == PDFObjectType_Name && |
3378 | 48 | poSubtype->GetName() == "Form") |
3379 | 48 | { |
3380 | 48 | nLength = poPageStream->GetLength(MAX_LENGTH); |
3381 | 48 | if (nLength < MAX_LENGTH) |
3382 | 48 | { |
3383 | 48 | pszContent = poPageStream->GetBytes(); |
3384 | | |
3385 | 48 | GDALPDFObject *poXObject2 = |
3386 | 48 | poObjFormDict->LookupObject( |
3387 | 48 | "Resources.XObject"); |
3388 | 48 | if (poXObject2 != nullptr && |
3389 | 1 | poXObject2->GetType() == |
3390 | 1 | PDFObjectType_Dictionary) |
3391 | 1 | poContentDict = |
3392 | 1 | poXObject2->GetDictionary(); |
3393 | 48 | } |
3394 | 48 | } |
3395 | 48 | } |
3396 | 97 | } |
3397 | 3.73k | } |
3398 | | |
3399 | 3.73k | if (pszContent != nullptr) |
3400 | 3.63k | { |
3401 | 3.63k | int bDPISet = FALSE; |
3402 | | |
3403 | 3.63k | const char *pszContentToParse = pszContent; |
3404 | 3.63k | if (bResetTiles) |
3405 | 0 | { |
3406 | 0 | while (*pszContentToParse != '\0') |
3407 | 0 | { |
3408 | 0 | if (*pszContentToParse == 13 || |
3409 | 0 | *pszContentToParse == 10 || |
3410 | 0 | *pszContentToParse == ' ' || |
3411 | 0 | (*pszContentToParse >= '0' && |
3412 | 0 | *pszContentToParse <= '9') || |
3413 | 0 | *pszContentToParse == '.' || |
3414 | 0 | *pszContentToParse == '-' || |
3415 | 0 | *pszContentToParse == 'l' || |
3416 | 0 | *pszContentToParse == 'm' || |
3417 | 0 | *pszContentToParse == 'n' || |
3418 | 0 | *pszContentToParse == 'W') |
3419 | 0 | pszContentToParse++; |
3420 | 0 | else |
3421 | 0 | break; |
3422 | 0 | } |
3423 | 0 | } |
3424 | | |
3425 | 3.63k | GDALPDFParseStreamContent(pszContentToParse, poContentDict, |
3426 | 3.63k | &(m_dfDPI), &bDPISet, pnBands, |
3427 | 3.63k | m_asTiles, bResetTiles); |
3428 | 3.63k | CPLFree(pszContent); |
3429 | 3.63k | if (bDPISet) |
3430 | 219 | { |
3431 | 219 | m_dfDPI *= dfScaleDPI; |
3432 | | |
3433 | 219 | CPLDebug("PDF", |
3434 | 219 | "DPI guessed from contents stream = %.16g", |
3435 | 219 | m_dfDPI); |
3436 | 219 | SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI)); |
3437 | 219 | if (bResetTiles) |
3438 | 0 | m_asTiles.resize(0); |
3439 | 219 | } |
3440 | 3.41k | else |
3441 | 3.41k | m_asTiles.resize(0); |
3442 | 3.63k | } |
3443 | 3.73k | } |
3444 | 3.94k | } |
3445 | | |
3446 | 13.5k | GDALPDFObject *poUserUnit = nullptr; |
3447 | 13.5k | if ((poUserUnit = poPageDict->Get("UserUnit")) != nullptr && |
3448 | 3.37k | (poUserUnit->GetType() == PDFObjectType_Int || |
3449 | 32 | poUserUnit->GetType() == PDFObjectType_Real)) |
3450 | 3.35k | { |
3451 | 3.35k | m_dfDPI = ROUND_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI, 1e-5); |
3452 | 3.35k | CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", m_dfDPI); |
3453 | 3.35k | SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI)); |
3454 | 3.35k | } |
3455 | 13.5k | } |
3456 | | |
3457 | 31.9k | if (m_dfDPI < 1e-2 || m_dfDPI > 7200) |
3458 | 28 | { |
3459 | 28 | CPLError(CE_Warning, CPLE_AppDefined, |
3460 | 28 | "Invalid value for GDAL_PDF_DPI. Using default value instead"); |
3461 | 28 | m_dfDPI = GDAL_DEFAULT_DPI; |
3462 | 28 | } |
3463 | 31.9k | } |
3464 | | |
3465 | | /************************************************************************/ |
3466 | | /* FindXMP() */ |
3467 | | /************************************************************************/ |
3468 | | |
3469 | | void PDFDataset::FindXMP(GDALPDFObject *poObj) |
3470 | 0 | { |
3471 | 0 | if (poObj->GetType() != PDFObjectType_Dictionary) |
3472 | 0 | return; |
3473 | | |
3474 | 0 | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3475 | 0 | GDALPDFObject *poType = poDict->Get("Type"); |
3476 | 0 | GDALPDFObject *poSubtype = poDict->Get("Subtype"); |
3477 | 0 | if (poType == nullptr || poType->GetType() != PDFObjectType_Name || |
3478 | 0 | poType->GetName() != "Metadata" || poSubtype == nullptr || |
3479 | 0 | poSubtype->GetType() != PDFObjectType_Name || |
3480 | 0 | poSubtype->GetName() != "XML") |
3481 | 0 | { |
3482 | 0 | return; |
3483 | 0 | } |
3484 | | |
3485 | 0 | GDALPDFStream *poStream = poObj->GetStream(); |
3486 | 0 | if (poStream == nullptr) |
3487 | 0 | return; |
3488 | | |
3489 | 0 | char *pszContent = poStream->GetBytes(); |
3490 | 0 | const auto nLength = poStream->GetLength(); |
3491 | 0 | if (pszContent != nullptr && nLength > 15 && |
3492 | 0 | STARTS_WITH(pszContent, "<?xpacket begin=")) |
3493 | 0 | { |
3494 | 0 | char *apszMDList[2]; |
3495 | 0 | apszMDList[0] = pszContent; |
3496 | 0 | apszMDList[1] = nullptr; |
3497 | 0 | SetMetadata(apszMDList, "xml:XMP"); |
3498 | 0 | } |
3499 | 0 | CPLFree(pszContent); |
3500 | 0 | } |
3501 | | |
3502 | | /************************************************************************/ |
3503 | | /* ParseInfo() */ |
3504 | | /************************************************************************/ |
3505 | | |
3506 | | void PDFDataset::ParseInfo(GDALPDFObject *poInfoObj) |
3507 | 31.9k | { |
3508 | 31.9k | if (poInfoObj->GetType() != PDFObjectType_Dictionary) |
3509 | 23.1k | return; |
3510 | | |
3511 | 8.76k | GDALPDFDictionary *poInfoObjDict = poInfoObj->GetDictionary(); |
3512 | 8.76k | GDALPDFObject *poItem = nullptr; |
3513 | 8.76k | int bOneMDISet = FALSE; |
3514 | 8.76k | if ((poItem = poInfoObjDict->Get("Author")) != nullptr && |
3515 | 2.11k | poItem->GetType() == PDFObjectType_String) |
3516 | 2.10k | { |
3517 | 2.10k | SetMetadataItem("AUTHOR", poItem->GetString().c_str()); |
3518 | 2.10k | bOneMDISet = TRUE; |
3519 | 2.10k | } |
3520 | 8.76k | if ((poItem = poInfoObjDict->Get("Creator")) != nullptr && |
3521 | 5.84k | poItem->GetType() == PDFObjectType_String) |
3522 | 5.83k | { |
3523 | 5.83k | SetMetadataItem("CREATOR", poItem->GetString().c_str()); |
3524 | 5.83k | bOneMDISet = TRUE; |
3525 | 5.83k | } |
3526 | 8.76k | if ((poItem = poInfoObjDict->Get("Keywords")) != nullptr && |
3527 | 931 | poItem->GetType() == PDFObjectType_String) |
3528 | 931 | { |
3529 | 931 | SetMetadataItem("KEYWORDS", poItem->GetString().c_str()); |
3530 | 931 | bOneMDISet = TRUE; |
3531 | 931 | } |
3532 | 8.76k | if ((poItem = poInfoObjDict->Get("Subject")) != nullptr && |
3533 | 1.43k | poItem->GetType() == PDFObjectType_String) |
3534 | 1.43k | { |
3535 | 1.43k | SetMetadataItem("SUBJECT", poItem->GetString().c_str()); |
3536 | 1.43k | bOneMDISet = TRUE; |
3537 | 1.43k | } |
3538 | 8.76k | if ((poItem = poInfoObjDict->Get("Title")) != nullptr && |
3539 | 2.67k | poItem->GetType() == PDFObjectType_String) |
3540 | 2.62k | { |
3541 | 2.62k | SetMetadataItem("TITLE", poItem->GetString().c_str()); |
3542 | 2.62k | bOneMDISet = TRUE; |
3543 | 2.62k | } |
3544 | 8.76k | if ((poItem = poInfoObjDict->Get("Producer")) != nullptr && |
3545 | 7.18k | poItem->GetType() == PDFObjectType_String) |
3546 | 6.55k | { |
3547 | 6.55k | if (bOneMDISet || |
3548 | 1.27k | poItem->GetString() != "PoDoFo - http://podofo.sf.net") |
3549 | 6.54k | { |
3550 | 6.54k | SetMetadataItem("PRODUCER", poItem->GetString().c_str()); |
3551 | 6.54k | bOneMDISet = TRUE; |
3552 | 6.54k | } |
3553 | 6.55k | } |
3554 | 8.76k | if ((poItem = poInfoObjDict->Get("CreationDate")) != nullptr && |
3555 | 7.28k | poItem->GetType() == PDFObjectType_String) |
3556 | 7.24k | { |
3557 | 7.24k | if (bOneMDISet) |
3558 | 6.46k | SetMetadataItem("CREATION_DATE", poItem->GetString().c_str()); |
3559 | 7.24k | } |
3560 | 8.76k | } |
3561 | | |
3562 | | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3563 | | |
3564 | | /************************************************************************/ |
3565 | | /* AddLayer() */ |
3566 | | /************************************************************************/ |
3567 | | |
3568 | | void PDFDataset::AddLayer(const std::string &osName, int iPage) |
3569 | 429k | { |
3570 | 429k | LayerStruct layerStruct; |
3571 | 429k | layerStruct.osName = osName; |
3572 | 429k | layerStruct.nInsertIdx = static_cast<int>(m_oLayerNameSet.size()); |
3573 | 429k | layerStruct.iPage = iPage; |
3574 | 429k | m_oLayerNameSet.emplace_back(std::move(layerStruct)); |
3575 | 429k | } |
3576 | | |
3577 | | /************************************************************************/ |
3578 | | /* SortLayerList() */ |
3579 | | /************************************************************************/ |
3580 | | |
3581 | | // recent libc++ std::sort() involve unsigned integer overflow in some |
3582 | | // situation |
3583 | | CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW |
3584 | | void PDFDataset::SortLayerList() |
3585 | 6.79k | { |
3586 | | // Sort layers by prioritizing page number and then insertion index |
3587 | 6.79k | std::sort(m_oLayerNameSet.begin(), m_oLayerNameSet.end(), |
3588 | 6.79k | [](const LayerStruct &a, const LayerStruct &b) |
3589 | 841k | { |
3590 | 841k | if (a.iPage < b.iPage) |
3591 | 1.23k | return true; |
3592 | 840k | if (a.iPage > b.iPage) |
3593 | 7 | return false; |
3594 | 840k | return a.nInsertIdx < b.nInsertIdx; |
3595 | 840k | }); |
3596 | 6.79k | } |
3597 | | |
3598 | | /************************************************************************/ |
3599 | | /* CreateLayerList() */ |
3600 | | /************************************************************************/ |
3601 | | |
3602 | | void PDFDataset::CreateLayerList() |
3603 | 6.79k | { |
3604 | 6.79k | SortLayerList(); |
3605 | | |
3606 | 6.79k | if (m_oLayerNameSet.size() >= 100) |
3607 | 108 | { |
3608 | 108 | for (const auto &oLayerStruct : m_oLayerNameSet) |
3609 | 401k | { |
3610 | 401k | m_aosLayerNames.AddNameValue( |
3611 | 401k | CPLSPrintf("LAYER_%03d_NAME", m_aosLayerNames.size()), |
3612 | 401k | oLayerStruct.osName.c_str()); |
3613 | 401k | } |
3614 | 108 | } |
3615 | 6.68k | else |
3616 | 6.68k | { |
3617 | 6.68k | for (const auto &oLayerStruct : m_oLayerNameSet) |
3618 | 28.7k | { |
3619 | 28.7k | m_aosLayerNames.AddNameValue( |
3620 | 28.7k | CPLSPrintf("LAYER_%02d_NAME", m_aosLayerNames.size()), |
3621 | 28.7k | oLayerStruct.osName.c_str()); |
3622 | 28.7k | } |
3623 | 6.68k | } |
3624 | 6.79k | } |
3625 | | |
3626 | | /************************************************************************/ |
3627 | | /* BuildPostfixedLayerNameAndAddLayer() */ |
3628 | | /************************************************************************/ |
3629 | | |
3630 | | /** Append a suffix with the page number(s) to the provided layer name, if |
3631 | | * it makes sense (that is if it is a multiple page PDF and we haven't selected |
3632 | | * a specific name). And also call AddLayer() on it if successful. |
3633 | | * If may return an empty string if the layer isn't used by the page of interest |
3634 | | */ |
3635 | | std::string PDFDataset::BuildPostfixedLayerNameAndAddLayer( |
3636 | | const std::string &osName, const std::pair<int, int> &oOCGRef, |
3637 | | int iPageOfInterest, int nPageCount) |
3638 | 426k | { |
3639 | 426k | std::string osPostfixedName = osName; |
3640 | 426k | int iLayerPage = 0; |
3641 | 426k | if (nPageCount > 1 && !m_oMapOCGNumGenToPages.empty()) |
3642 | 336 | { |
3643 | 336 | const auto oIterToPages = m_oMapOCGNumGenToPages.find(oOCGRef); |
3644 | 336 | if (oIterToPages != m_oMapOCGNumGenToPages.end()) |
3645 | 279 | { |
3646 | 279 | const auto &anPages = oIterToPages->second; |
3647 | 279 | if (iPageOfInterest > 0) |
3648 | 0 | { |
3649 | 0 | if (std::find(anPages.begin(), anPages.end(), |
3650 | 0 | iPageOfInterest) == anPages.end()) |
3651 | 0 | { |
3652 | 0 | return std::string(); |
3653 | 0 | } |
3654 | 0 | } |
3655 | 279 | else if (anPages.size() == 1) |
3656 | 276 | { |
3657 | 276 | iLayerPage = anPages.front(); |
3658 | 276 | osPostfixedName += CPLSPrintf(" (page %d)", anPages.front()); |
3659 | 276 | } |
3660 | 3 | else |
3661 | 3 | { |
3662 | 3 | osPostfixedName += " (pages "; |
3663 | 12 | for (size_t j = 0; j < anPages.size(); ++j) |
3664 | 9 | { |
3665 | 9 | if (j > 0) |
3666 | 6 | osPostfixedName += ", "; |
3667 | 9 | osPostfixedName += CPLSPrintf("%d", anPages[j]); |
3668 | 9 | } |
3669 | 3 | osPostfixedName += ')'; |
3670 | 3 | } |
3671 | 279 | } |
3672 | 336 | } |
3673 | | |
3674 | 426k | AddLayer(osPostfixedName, iLayerPage); |
3675 | | |
3676 | 426k | return osPostfixedName; |
3677 | 426k | } |
3678 | | |
3679 | | #endif // defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
3680 | | |
3681 | | #ifdef HAVE_POPPLER |
3682 | | |
3683 | | /************************************************************************/ |
3684 | | /* ExploreLayersPoppler() */ |
3685 | | /************************************************************************/ |
3686 | | |
3687 | | void PDFDataset::ExploreLayersPoppler(GDALPDFArray *poArray, |
3688 | | int iPageOfInterest, int nPageCount, |
3689 | | CPLString osTopLayer, int nRecLevel, |
3690 | | int &nVisited, bool &bStop) |
3691 | 31.1k | { |
3692 | 31.1k | if (nRecLevel == 16 || nVisited == 1000) |
3693 | 114 | { |
3694 | 114 | CPLError( |
3695 | 114 | CE_Failure, CPLE_AppDefined, |
3696 | 114 | "ExploreLayersPoppler(): too deep exploration or too many items"); |
3697 | 114 | bStop = true; |
3698 | 114 | return; |
3699 | 114 | } |
3700 | 31.0k | if (bStop) |
3701 | 0 | return; |
3702 | | |
3703 | 31.0k | int nLength = poArray->GetLength(); |
3704 | 31.0k | CPLString osCurLayer; |
3705 | 1.45M | for (int i = 0; i < nLength; i++) |
3706 | 1.42M | { |
3707 | 1.42M | nVisited++; |
3708 | 1.42M | GDALPDFObject *poObj = poArray->Get(i); |
3709 | 1.42M | if (poObj == nullptr) |
3710 | 58.5k | continue; |
3711 | 1.36M | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
3712 | 1.30k | { |
3713 | 1.30k | std::string osName = |
3714 | 1.30k | PDFSanitizeLayerName(poObj->GetString().c_str()); |
3715 | 1.30k | if (!osTopLayer.empty()) |
3716 | 789 | { |
3717 | 789 | osTopLayer += '.'; |
3718 | 789 | osTopLayer += osName; |
3719 | 789 | } |
3720 | 520 | else |
3721 | 520 | osTopLayer = std::move(osName); |
3722 | 1.30k | AddLayer(osTopLayer, 0); |
3723 | 1.30k | m_oLayerOCGListPoppler.push_back(std::pair(osTopLayer, nullptr)); |
3724 | 1.30k | } |
3725 | 1.36M | else if (poObj->GetType() == PDFObjectType_Array) |
3726 | 25.3k | { |
3727 | 25.3k | ExploreLayersPoppler(poObj->GetArray(), iPageOfInterest, nPageCount, |
3728 | 25.3k | osCurLayer, nRecLevel + 1, nVisited, bStop); |
3729 | 25.3k | if (bStop) |
3730 | 1.70k | return; |
3731 | 23.6k | osCurLayer = ""; |
3732 | 23.6k | } |
3733 | 1.34M | else if (poObj->GetType() == PDFObjectType_Dictionary) |
3734 | 641k | { |
3735 | 641k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
3736 | 641k | GDALPDFObject *poName = poDict->Get("Name"); |
3737 | 641k | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
3738 | 581k | { |
3739 | 581k | std::string osName = |
3740 | 581k | PDFSanitizeLayerName(poName->GetString().c_str()); |
3741 | | /* coverity[copy_paste_error] */ |
3742 | 581k | if (!osTopLayer.empty()) |
3743 | 522k | { |
3744 | 522k | osCurLayer = osTopLayer; |
3745 | 522k | osCurLayer += '.'; |
3746 | 522k | osCurLayer += osName; |
3747 | 522k | } |
3748 | 58.7k | else |
3749 | 58.7k | osCurLayer = std::move(osName); |
3750 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
3751 | | |
3752 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3753 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3754 | | const |
3755 | | #endif |
3756 | 581k | OCGs *optContentConfig = |
3757 | 581k | m_poDocPoppler->getOptContentConfig(); |
3758 | 581k | struct Ref r; |
3759 | 581k | r.num = poObj->GetRefNum().toInt(); |
3760 | 581k | r.gen = poObj->GetRefGen(); |
3761 | 581k | OptionalContentGroup *ocg = optContentConfig->findOcgByRef(r); |
3762 | 581k | if (ocg) |
3763 | 426k | { |
3764 | 426k | const auto oRefPair = std::pair(poObj->GetRefNum().toInt(), |
3765 | 426k | poObj->GetRefGen()); |
3766 | 426k | const std::string osPostfixedName = |
3767 | 426k | BuildPostfixedLayerNameAndAddLayer( |
3768 | 426k | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
3769 | 426k | if (osPostfixedName.empty()) |
3770 | 0 | continue; |
3771 | | |
3772 | 426k | m_oLayerOCGListPoppler.push_back( |
3773 | 426k | std::make_pair(osPostfixedName, ocg)); |
3774 | 426k | m_aoLayerWithRef.emplace_back(osPostfixedName.c_str(), |
3775 | 426k | poObj->GetRefNum(), r.gen); |
3776 | 426k | } |
3777 | 581k | } |
3778 | 641k | } |
3779 | 1.36M | } |
3780 | 31.0k | } |
3781 | | |
3782 | | /************************************************************************/ |
3783 | | /* FindLayersPoppler() */ |
3784 | | /************************************************************************/ |
3785 | | |
3786 | | void PDFDataset::FindLayersPoppler(int iPageOfInterest) |
3787 | 31.9k | { |
3788 | 31.9k | int nPageCount = 0; |
3789 | 31.9k | const auto poPages = GetPagesKids(); |
3790 | 31.9k | if (poPages) |
3791 | 30.9k | nPageCount = poPages->GetLength(); |
3792 | | |
3793 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3794 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3795 | | const |
3796 | | #endif |
3797 | 31.9k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3798 | 31.9k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3799 | 25.1k | return; |
3800 | | |
3801 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3802 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3803 | | const |
3804 | | #endif |
3805 | 6.79k | Array *array = optContentConfig->getOrderArray(); |
3806 | 6.79k | if (array) |
3807 | 5.87k | { |
3808 | 5.87k | GDALPDFArray *poArray = GDALPDFCreateArray(array); |
3809 | 5.87k | int nVisited = 0; |
3810 | 5.87k | bool bStop = false; |
3811 | 5.87k | ExploreLayersPoppler(poArray, iPageOfInterest, nPageCount, CPLString(), |
3812 | 5.87k | 0, nVisited, bStop); |
3813 | 5.87k | delete poArray; |
3814 | 5.87k | } |
3815 | 922 | else |
3816 | 922 | { |
3817 | 922 | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3818 | 1.88k | { |
3819 | 1.88k | auto ocg = refOCGPair.second.get(); |
3820 | 1.88k | if (ocg != nullptr && ocg->getName() != nullptr) |
3821 | 1.69k | { |
3822 | 1.69k | const char *pszLayerName = |
3823 | 1.69k | reinterpret_cast<const char *>(ocg->getName()->c_str()); |
3824 | 1.69k | AddLayer(pszLayerName, 0); |
3825 | 1.69k | m_oLayerOCGListPoppler.push_back( |
3826 | 1.69k | std::make_pair(CPLString(pszLayerName), ocg)); |
3827 | 1.69k | } |
3828 | 1.88k | } |
3829 | 922 | } |
3830 | | |
3831 | 6.79k | CreateLayerList(); |
3832 | 6.79k | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
3833 | 6.79k | } |
3834 | | |
3835 | | /************************************************************************/ |
3836 | | /* TurnLayersOnOffPoppler() */ |
3837 | | /************************************************************************/ |
3838 | | |
3839 | | void PDFDataset::TurnLayersOnOffPoppler() |
3840 | 31.9k | { |
3841 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
3842 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2) |
3843 | | const |
3844 | | #endif |
3845 | 31.9k | OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig(); |
3846 | 31.9k | if (optContentConfig == nullptr || !optContentConfig->isOk()) |
3847 | 25.1k | return; |
3848 | | |
3849 | | // Which layers to turn ON ? |
3850 | 6.79k | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
3851 | 6.79k | if (pszLayers) |
3852 | 0 | { |
3853 | 0 | int i; |
3854 | 0 | int bAll = EQUAL(pszLayers, "ALL"); |
3855 | 0 | for (const auto &refOCGPair : optContentConfig->getOCGs()) |
3856 | 0 | { |
3857 | 0 | auto ocg = refOCGPair.second.get(); |
3858 | 0 | ocg->setState((bAll) ? OptionalContentGroup::On |
3859 | 0 | : OptionalContentGroup::Off); |
3860 | 0 | } |
3861 | |
|
3862 | 0 | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
3863 | 0 | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
3864 | 0 | { |
3865 | 0 | bool isFound = false; |
3866 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
3867 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
3868 | 0 | { |
3869 | 0 | if (oIter2->first != papszLayers[i]) |
3870 | 0 | continue; |
3871 | | |
3872 | 0 | isFound = true; |
3873 | 0 | auto oIter = oIter2; |
3874 | 0 | if (oIter->second) |
3875 | 0 | { |
3876 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
3877 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3878 | 0 | } |
3879 | | |
3880 | | // Turn child layers on, unless there's one of them explicitly |
3881 | | // listed in the list. |
3882 | 0 | size_t nLen = strlen(papszLayers[i]); |
3883 | 0 | int bFoundChildLayer = FALSE; |
3884 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3885 | 0 | for (; |
3886 | 0 | oIter != m_oLayerOCGListPoppler.end() && !bFoundChildLayer; |
3887 | 0 | ++oIter) |
3888 | 0 | { |
3889 | 0 | if (oIter->first.size() > nLen && |
3890 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
3891 | 0 | 0 && |
3892 | 0 | oIter->first[nLen] == '.') |
3893 | 0 | { |
3894 | 0 | for (int j = 0; papszLayers[j] != nullptr; j++) |
3895 | 0 | { |
3896 | 0 | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
3897 | 0 | 0) |
3898 | 0 | { |
3899 | 0 | bFoundChildLayer = TRUE; |
3900 | 0 | break; |
3901 | 0 | } |
3902 | 0 | } |
3903 | 0 | } |
3904 | 0 | } |
3905 | |
|
3906 | 0 | if (!bFoundChildLayer) |
3907 | 0 | { |
3908 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3909 | 0 | for (; oIter != m_oLayerOCGListPoppler.end() && |
3910 | 0 | !bFoundChildLayer; |
3911 | 0 | ++oIter) |
3912 | 0 | { |
3913 | 0 | if (oIter->first.size() > nLen && |
3914 | 0 | strncmp(oIter->first.c_str(), papszLayers[i], |
3915 | 0 | nLen) == 0 && |
3916 | 0 | oIter->first[nLen] == '.') |
3917 | 0 | { |
3918 | 0 | if (oIter->second) |
3919 | 0 | { |
3920 | | // CPLDebug("PDF", "Turn '%s' on too", |
3921 | | // oIter->first.c_str()); |
3922 | 0 | oIter->second->setState( |
3923 | 0 | OptionalContentGroup::On); |
3924 | 0 | } |
3925 | 0 | } |
3926 | 0 | } |
3927 | 0 | } |
3928 | | |
3929 | | // Turn parent layers on too |
3930 | 0 | std::string layer(papszLayers[i]); |
3931 | 0 | std::string::size_type j; |
3932 | 0 | while ((j = layer.find_last_of('.')) != std::string::npos) |
3933 | 0 | { |
3934 | 0 | layer.resize(j); |
3935 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3936 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
3937 | 0 | { |
3938 | 0 | if (oIter->first == layer && oIter->second) |
3939 | 0 | { |
3940 | | // CPLDebug("PDF", "Turn '%s' on too", |
3941 | | // layer.c_str()); |
3942 | 0 | oIter->second->setState(OptionalContentGroup::On); |
3943 | 0 | } |
3944 | 0 | } |
3945 | 0 | } |
3946 | 0 | } |
3947 | 0 | if (!isFound) |
3948 | 0 | { |
3949 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
3950 | 0 | papszLayers[i]); |
3951 | 0 | } |
3952 | 0 | } |
3953 | 0 | CSLDestroy(papszLayers); |
3954 | |
|
3955 | 0 | m_bUseOCG = true; |
3956 | 0 | } |
3957 | | |
3958 | | // Which layers to turn OFF ? |
3959 | 6.79k | const char *pszLayersOFF = |
3960 | 6.79k | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
3961 | 6.79k | if (pszLayersOFF) |
3962 | 0 | { |
3963 | 0 | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
3964 | 0 | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
3965 | 0 | { |
3966 | 0 | bool isFound = false; |
3967 | 0 | for (auto oIter2 = m_oLayerOCGListPoppler.begin(); |
3968 | 0 | oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2) |
3969 | 0 | { |
3970 | 0 | if (oIter2->first != papszLayersOFF[i]) |
3971 | 0 | continue; |
3972 | | |
3973 | 0 | isFound = true; |
3974 | 0 | auto oIter = oIter2; |
3975 | 0 | if (oIter->second) |
3976 | 0 | { |
3977 | | // CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]); |
3978 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
3979 | 0 | } |
3980 | | |
3981 | | // Turn child layers off too |
3982 | 0 | size_t nLen = strlen(papszLayersOFF[i]); |
3983 | 0 | oIter = m_oLayerOCGListPoppler.begin(); |
3984 | 0 | for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter) |
3985 | 0 | { |
3986 | 0 | if (oIter->first.size() > nLen && |
3987 | 0 | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
3988 | 0 | nLen) == 0 && |
3989 | 0 | oIter->first[nLen] == '.') |
3990 | 0 | { |
3991 | 0 | if (oIter->second) |
3992 | 0 | { |
3993 | | // CPLDebug("PDF", "Turn '%s' off too", |
3994 | | // oIter->first.c_str()); |
3995 | 0 | oIter->second->setState(OptionalContentGroup::Off); |
3996 | 0 | } |
3997 | 0 | } |
3998 | 0 | } |
3999 | 0 | } |
4000 | 0 | if (!isFound) |
4001 | 0 | { |
4002 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4003 | 0 | papszLayersOFF[i]); |
4004 | 0 | } |
4005 | 0 | } |
4006 | 0 | CSLDestroy(papszLayersOFF); |
4007 | |
|
4008 | 0 | m_bUseOCG = true; |
4009 | 0 | } |
4010 | 6.79k | } |
4011 | | |
4012 | | #endif |
4013 | | |
4014 | | #ifdef HAVE_PDFIUM |
4015 | | |
4016 | | /************************************************************************/ |
4017 | | /* ExploreLayersPdfium() */ |
4018 | | /************************************************************************/ |
4019 | | |
4020 | | void PDFDataset::ExploreLayersPdfium(GDALPDFArray *poArray, int iPageOfInterest, |
4021 | | int nPageCount, int nRecLevel, |
4022 | | CPLString osTopLayer) |
4023 | | { |
4024 | | if (nRecLevel == 16) |
4025 | | return; |
4026 | | |
4027 | | const int nLength = poArray->GetLength(); |
4028 | | std::string osCurLayer; |
4029 | | for (int i = 0; i < nLength; i++) |
4030 | | { |
4031 | | GDALPDFObject *poObj = poArray->Get(i); |
4032 | | if (poObj == nullptr) |
4033 | | continue; |
4034 | | if (i == 0 && poObj->GetType() == PDFObjectType_String) |
4035 | | { |
4036 | | const std::string osName = |
4037 | | PDFSanitizeLayerName(poObj->GetString().c_str()); |
4038 | | if (!osTopLayer.empty()) |
4039 | | osTopLayer = std::string(osTopLayer).append(".").append(osName); |
4040 | | else |
4041 | | osTopLayer = osName; |
4042 | | AddLayer(osTopLayer, 0); |
4043 | | m_oMapLayerNameToOCGNumGenPdfium[osTopLayer] = std::pair(-1, -1); |
4044 | | } |
4045 | | else if (poObj->GetType() == PDFObjectType_Array) |
4046 | | { |
4047 | | ExploreLayersPdfium(poObj->GetArray(), iPageOfInterest, nPageCount, |
4048 | | nRecLevel + 1, osCurLayer); |
4049 | | osCurLayer.clear(); |
4050 | | } |
4051 | | else if (poObj->GetType() == PDFObjectType_Dictionary) |
4052 | | { |
4053 | | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
4054 | | GDALPDFObject *poName = poDict->Get("Name"); |
4055 | | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
4056 | | { |
4057 | | std::string osName = |
4058 | | PDFSanitizeLayerName(poName->GetString().c_str()); |
4059 | | // coverity[copy_paste_error] |
4060 | | if (!osTopLayer.empty()) |
4061 | | { |
4062 | | osCurLayer = |
4063 | | std::string(osTopLayer).append(".").append(osName); |
4064 | | } |
4065 | | else |
4066 | | osCurLayer = std::move(osName); |
4067 | | // CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); |
4068 | | |
4069 | | const auto oRefPair = |
4070 | | std::pair(poObj->GetRefNum().toInt(), poObj->GetRefGen()); |
4071 | | const std::string osPostfixedName = |
4072 | | BuildPostfixedLayerNameAndAddLayer( |
4073 | | osCurLayer, oRefPair, iPageOfInterest, nPageCount); |
4074 | | if (osPostfixedName.empty()) |
4075 | | continue; |
4076 | | |
4077 | | m_aoLayerWithRef.emplace_back( |
4078 | | osPostfixedName, poObj->GetRefNum(), poObj->GetRefGen()); |
4079 | | m_oMapLayerNameToOCGNumGenPdfium[osPostfixedName] = oRefPair; |
4080 | | } |
4081 | | } |
4082 | | } |
4083 | | } |
4084 | | |
4085 | | /************************************************************************/ |
4086 | | /* FindLayersPdfium() */ |
4087 | | /************************************************************************/ |
4088 | | |
4089 | | void PDFDataset::FindLayersPdfium(int iPageOfInterest) |
4090 | | { |
4091 | | int nPageCount = 0; |
4092 | | const auto poPages = GetPagesKids(); |
4093 | | if (poPages) |
4094 | | nPageCount = poPages->GetLength(); |
4095 | | |
4096 | | GDALPDFObject *poCatalog = GetCatalog(); |
4097 | | if (poCatalog == nullptr || |
4098 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4099 | | return; |
4100 | | GDALPDFObject *poOrder = poCatalog->LookupObject("OCProperties.D.Order"); |
4101 | | if (poOrder != nullptr && poOrder->GetType() == PDFObjectType_Array) |
4102 | | { |
4103 | | ExploreLayersPdfium(poOrder->GetArray(), iPageOfInterest, nPageCount, |
4104 | | 0); |
4105 | | } |
4106 | | #if 0 |
4107 | | else |
4108 | | { |
4109 | | GDALPDFObject* poOCGs = poD->GetDictionary()->Get("OCGs"); |
4110 | | if( poOCGs != nullptr && poOCGs->GetType() == PDFObjectType_Array ) |
4111 | | { |
4112 | | GDALPDFArray* poArray = poOCGs->GetArray(); |
4113 | | int nLength = poArray->GetLength(); |
4114 | | for(int i=0;i<nLength;i++) |
4115 | | { |
4116 | | GDALPDFObject* poObj = poArray->Get(i); |
4117 | | if( poObj != nullptr ) |
4118 | | { |
4119 | | // TODO ? |
4120 | | } |
4121 | | } |
4122 | | } |
4123 | | } |
4124 | | #endif |
4125 | | |
4126 | | CreateLayerList(); |
4127 | | m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS"); |
4128 | | } |
4129 | | |
4130 | | /************************************************************************/ |
4131 | | /* TurnLayersOnOffPdfium() */ |
4132 | | /************************************************************************/ |
4133 | | |
4134 | | void PDFDataset::TurnLayersOnOffPdfium() |
4135 | | { |
4136 | | GDALPDFObject *poCatalog = GetCatalog(); |
4137 | | if (poCatalog == nullptr || |
4138 | | poCatalog->GetType() != PDFObjectType_Dictionary) |
4139 | | return; |
4140 | | GDALPDFObject *poOCGs = poCatalog->LookupObject("OCProperties.OCGs"); |
4141 | | if (poOCGs == nullptr || poOCGs->GetType() != PDFObjectType_Array) |
4142 | | return; |
4143 | | |
4144 | | // Which layers to turn ON ? |
4145 | | const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr); |
4146 | | if (pszLayers) |
4147 | | { |
4148 | | int i; |
4149 | | int bAll = EQUAL(pszLayers, "ALL"); |
4150 | | |
4151 | | GDALPDFArray *poOCGsArray = poOCGs->GetArray(); |
4152 | | int nLength = poOCGsArray->GetLength(); |
4153 | | for (i = 0; i < nLength; i++) |
4154 | | { |
4155 | | GDALPDFObject *poOCG = poOCGsArray->Get(i); |
4156 | | m_oMapOCGNumGenToVisibilityStatePdfium[std::pair( |
4157 | | poOCG->GetRefNum().toInt(), poOCG->GetRefGen())] = |
4158 | | (bAll) ? VISIBILITY_ON : VISIBILITY_OFF; |
4159 | | } |
4160 | | |
4161 | | char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0); |
4162 | | for (i = 0; !bAll && papszLayers[i] != nullptr; i++) |
4163 | | { |
4164 | | auto oIter = m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4165 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4166 | | { |
4167 | | if (oIter->second.first >= 0) |
4168 | | { |
4169 | | // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); |
4170 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4171 | | VISIBILITY_ON; |
4172 | | } |
4173 | | |
4174 | | // Turn child layers on, unless there's one of them explicitly |
4175 | | // listed in the list. |
4176 | | size_t nLen = strlen(papszLayers[i]); |
4177 | | int bFoundChildLayer = FALSE; |
4178 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4179 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4180 | | !bFoundChildLayer; |
4181 | | oIter++) |
4182 | | { |
4183 | | if (oIter->first.size() > nLen && |
4184 | | strncmp(oIter->first.c_str(), papszLayers[i], nLen) == |
4185 | | 0 && |
4186 | | oIter->first[nLen] == '.') |
4187 | | { |
4188 | | for (int j = 0; papszLayers[j] != nullptr; j++) |
4189 | | { |
4190 | | if (strcmp(papszLayers[j], oIter->first.c_str()) == |
4191 | | 0) |
4192 | | bFoundChildLayer = TRUE; |
4193 | | } |
4194 | | } |
4195 | | } |
4196 | | |
4197 | | if (!bFoundChildLayer) |
4198 | | { |
4199 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4200 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() && |
4201 | | !bFoundChildLayer; |
4202 | | oIter++) |
4203 | | { |
4204 | | if (oIter->first.size() > nLen && |
4205 | | strncmp(oIter->first.c_str(), papszLayers[i], |
4206 | | nLen) == 0 && |
4207 | | oIter->first[nLen] == '.') |
4208 | | { |
4209 | | if (oIter->second.first >= 0) |
4210 | | { |
4211 | | // CPLDebug("PDF", "Turn '%s' on too", |
4212 | | // oIter->first.c_str()); |
4213 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4214 | | [oIter->second] = VISIBILITY_ON; |
4215 | | } |
4216 | | } |
4217 | | } |
4218 | | } |
4219 | | |
4220 | | // Turn parent layers on too |
4221 | | char *pszLastDot = nullptr; |
4222 | | while ((pszLastDot = strrchr(papszLayers[i], '.')) != nullptr) |
4223 | | { |
4224 | | *pszLastDot = '\0'; |
4225 | | oIter = |
4226 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]); |
4227 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4228 | | { |
4229 | | if (oIter->second.first >= 0) |
4230 | | { |
4231 | | // CPLDebug("PDF", "Turn '%s' on too", |
4232 | | // papszLayers[i]); |
4233 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4234 | | [oIter->second] = VISIBILITY_ON; |
4235 | | } |
4236 | | } |
4237 | | } |
4238 | | } |
4239 | | else |
4240 | | { |
4241 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4242 | | papszLayers[i]); |
4243 | | } |
4244 | | } |
4245 | | CSLDestroy(papszLayers); |
4246 | | |
4247 | | m_bUseOCG = true; |
4248 | | } |
4249 | | |
4250 | | // Which layers to turn OFF ? |
4251 | | const char *pszLayersOFF = |
4252 | | GetOption(papszOpenOptions, "LAYERS_OFF", nullptr); |
4253 | | if (pszLayersOFF) |
4254 | | { |
4255 | | char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); |
4256 | | for (int i = 0; papszLayersOFF[i] != nullptr; i++) |
4257 | | { |
4258 | | auto oIter = |
4259 | | m_oMapLayerNameToOCGNumGenPdfium.find(papszLayersOFF[i]); |
4260 | | if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end()) |
4261 | | { |
4262 | | if (oIter->second.first >= 0) |
4263 | | { |
4264 | | // CPLDebug("PDF", "Turn '%s' (%d,%d) off", |
4265 | | // papszLayersOFF[i], oIter->second.first, |
4266 | | // oIter->second.second); |
4267 | | m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] = |
4268 | | VISIBILITY_OFF; |
4269 | | } |
4270 | | |
4271 | | // Turn child layers off too |
4272 | | size_t nLen = strlen(papszLayersOFF[i]); |
4273 | | oIter = m_oMapLayerNameToOCGNumGenPdfium.begin(); |
4274 | | for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end(); oIter++) |
4275 | | { |
4276 | | if (oIter->first.size() > nLen && |
4277 | | strncmp(oIter->first.c_str(), papszLayersOFF[i], |
4278 | | nLen) == 0 && |
4279 | | oIter->first[nLen] == '.') |
4280 | | { |
4281 | | if (oIter->second.first >= 0) |
4282 | | { |
4283 | | // CPLDebug("PDF", "Turn '%s' off too", |
4284 | | // oIter->first.c_str()); |
4285 | | m_oMapOCGNumGenToVisibilityStatePdfium |
4286 | | [oIter->second] = VISIBILITY_OFF; |
4287 | | } |
4288 | | } |
4289 | | } |
4290 | | } |
4291 | | else |
4292 | | { |
4293 | | CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", |
4294 | | papszLayersOFF[i]); |
4295 | | } |
4296 | | } |
4297 | | CSLDestroy(papszLayersOFF); |
4298 | | |
4299 | | m_bUseOCG = true; |
4300 | | } |
4301 | | } |
4302 | | |
4303 | | /************************************************************************/ |
4304 | | /* GetVisibilityStateForOGCPdfium() */ |
4305 | | /************************************************************************/ |
4306 | | |
4307 | | PDFDataset::VisibilityState PDFDataset::GetVisibilityStateForOGCPdfium(int nNum, |
4308 | | int nGen) |
4309 | | { |
4310 | | auto oIter = |
4311 | | m_oMapOCGNumGenToVisibilityStatePdfium.find(std::pair(nNum, nGen)); |
4312 | | if (oIter == m_oMapOCGNumGenToVisibilityStatePdfium.end()) |
4313 | | return VISIBILITY_DEFAULT; |
4314 | | return oIter->second; |
4315 | | } |
4316 | | |
4317 | | #endif /* HAVE_PDFIUM */ |
4318 | | |
4319 | | /************************************************************************/ |
4320 | | /* GetPagesKids() */ |
4321 | | /************************************************************************/ |
4322 | | |
4323 | | GDALPDFArray *PDFDataset::GetPagesKids() |
4324 | 63.9k | { |
4325 | 63.9k | const auto poCatalog = GetCatalog(); |
4326 | 63.9k | if (!poCatalog || poCatalog->GetType() != PDFObjectType_Dictionary) |
4327 | 0 | { |
4328 | 0 | return nullptr; |
4329 | 0 | } |
4330 | 63.9k | const auto poKids = poCatalog->LookupObject("Pages.Kids"); |
4331 | 63.9k | if (!poKids || poKids->GetType() != PDFObjectType_Array) |
4332 | 2.03k | { |
4333 | 2.03k | return nullptr; |
4334 | 2.03k | } |
4335 | 61.9k | return poKids->GetArray(); |
4336 | 63.9k | } |
4337 | | |
4338 | | /************************************************************************/ |
4339 | | /* MapOCGsToPages() */ |
4340 | | /************************************************************************/ |
4341 | | |
4342 | | void PDFDataset::MapOCGsToPages() |
4343 | 31.9k | { |
4344 | 31.9k | const auto poKidsArray = GetPagesKids(); |
4345 | 31.9k | if (!poKidsArray) |
4346 | 1.01k | { |
4347 | 1.01k | return; |
4348 | 1.01k | } |
4349 | 30.9k | const int nKidsArrayLength = poKidsArray->GetLength(); |
4350 | 94.5k | for (int iPage = 0; iPage < nKidsArrayLength; ++iPage) |
4351 | 63.6k | { |
4352 | 63.6k | const auto poPage = poKidsArray->Get(iPage); |
4353 | 63.6k | if (poPage && poPage->GetType() == PDFObjectType_Dictionary) |
4354 | 37.9k | { |
4355 | 37.9k | const auto poXObject = poPage->LookupObject("Resources.XObject"); |
4356 | 37.9k | if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary) |
4357 | 10.2k | { |
4358 | 10.2k | for (const auto &oNameObjectPair : |
4359 | 10.2k | poXObject->GetDictionary()->GetValues()) |
4360 | 69.3k | { |
4361 | 69.3k | const auto poProperties = |
4362 | 69.3k | oNameObjectPair.second->LookupObject( |
4363 | 69.3k | "Resources.Properties"); |
4364 | 69.3k | if (poProperties && |
4365 | 245 | poProperties->GetType() == PDFObjectType_Dictionary) |
4366 | 243 | { |
4367 | 243 | const auto &oMap = |
4368 | 243 | poProperties->GetDictionary()->GetValues(); |
4369 | 243 | for (const auto &[osKey, poObj] : oMap) |
4370 | 451 | { |
4371 | 451 | if (poObj->GetRefNum().toBool() && |
4372 | 412 | poObj->GetType() == PDFObjectType_Dictionary) |
4373 | 404 | { |
4374 | 404 | GDALPDFObject *poType = |
4375 | 404 | poObj->GetDictionary()->Get("Type"); |
4376 | 404 | GDALPDFObject *poName = |
4377 | 404 | poObj->GetDictionary()->Get("Name"); |
4378 | 404 | if (poType && |
4379 | 360 | poType->GetType() == PDFObjectType_Name && |
4380 | 360 | poType->GetName() == "OCG" && poName && |
4381 | 337 | poName->GetType() == PDFObjectType_String) |
4382 | 337 | { |
4383 | 337 | m_oMapOCGNumGenToPages |
4384 | 337 | [std::pair(poObj->GetRefNum().toInt(), |
4385 | 337 | poObj->GetRefGen())] |
4386 | 337 | .push_back(iPage + 1); |
4387 | 337 | } |
4388 | 404 | } |
4389 | 451 | } |
4390 | 243 | } |
4391 | 69.3k | } |
4392 | 10.2k | } |
4393 | 37.9k | } |
4394 | 63.6k | } |
4395 | 30.9k | } |
4396 | | |
4397 | | /************************************************************************/ |
4398 | | /* FindLayerOCG() */ |
4399 | | /************************************************************************/ |
4400 | | |
4401 | | CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary *poPageDict, |
4402 | | const char *pszLayerName) |
4403 | 3.58k | { |
4404 | 3.58k | GDALPDFObject *poProperties = |
4405 | 3.58k | poPageDict->LookupObject("Resources.Properties"); |
4406 | 3.58k | if (poProperties != nullptr && |
4407 | 1.91k | poProperties->GetType() == PDFObjectType_Dictionary) |
4408 | 1.90k | { |
4409 | 1.90k | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4410 | 1.90k | for (const auto &[osKey, poObj] : oMap) |
4411 | 13.1k | { |
4412 | 13.1k | if (poObj->GetRefNum().toBool() && |
4413 | 12.9k | poObj->GetType() == PDFObjectType_Dictionary) |
4414 | 12.8k | { |
4415 | 12.8k | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4416 | 12.8k | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4417 | 12.8k | if (poType != nullptr && |
4418 | 12.8k | poType->GetType() == PDFObjectType_Name && |
4419 | 12.8k | poType->GetName() == "OCG" && poName != nullptr && |
4420 | 12.7k | poName->GetType() == PDFObjectType_String) |
4421 | 12.7k | { |
4422 | 12.7k | if (poName->GetString() == pszLayerName) |
4423 | 0 | return osKey; |
4424 | 12.7k | } |
4425 | 12.8k | } |
4426 | 13.1k | } |
4427 | 1.90k | } |
4428 | 3.58k | return ""; |
4429 | 3.58k | } |
4430 | | |
4431 | | /************************************************************************/ |
4432 | | /* FindLayersGeneric() */ |
4433 | | /************************************************************************/ |
4434 | | |
4435 | | void PDFDataset::FindLayersGeneric(GDALPDFDictionary *poPageDict) |
4436 | 0 | { |
4437 | 0 | GDALPDFObject *poProperties = |
4438 | 0 | poPageDict->LookupObject("Resources.Properties"); |
4439 | 0 | if (poProperties != nullptr && |
4440 | 0 | poProperties->GetType() == PDFObjectType_Dictionary) |
4441 | 0 | { |
4442 | 0 | const auto &oMap = poProperties->GetDictionary()->GetValues(); |
4443 | 0 | for (const auto &[osKey, poObj] : oMap) |
4444 | 0 | { |
4445 | 0 | if (poObj->GetRefNum().toBool() && |
4446 | 0 | poObj->GetType() == PDFObjectType_Dictionary) |
4447 | 0 | { |
4448 | 0 | GDALPDFObject *poType = poObj->GetDictionary()->Get("Type"); |
4449 | 0 | GDALPDFObject *poName = poObj->GetDictionary()->Get("Name"); |
4450 | 0 | if (poType != nullptr && |
4451 | 0 | poType->GetType() == PDFObjectType_Name && |
4452 | 0 | poType->GetName() == "OCG" && poName != nullptr && |
4453 | 0 | poName->GetType() == PDFObjectType_String) |
4454 | 0 | { |
4455 | 0 | m_aoLayerWithRef.emplace_back( |
4456 | 0 | PDFSanitizeLayerName(poName->GetString().c_str()) |
4457 | 0 | .c_str(), |
4458 | 0 | poObj->GetRefNum(), poObj->GetRefGen()); |
4459 | 0 | } |
4460 | 0 | } |
4461 | 0 | } |
4462 | 0 | } |
4463 | 0 | } |
4464 | | |
4465 | | /************************************************************************/ |
4466 | | /* Open() */ |
4467 | | /************************************************************************/ |
4468 | | |
4469 | | PDFDataset *PDFDataset::Open(GDALOpenInfo *poOpenInfo) |
4470 | | |
4471 | 42.9k | { |
4472 | 42.9k | if (!PDFDatasetIdentify(poOpenInfo)) |
4473 | 0 | return nullptr; |
4474 | | |
4475 | 42.9k | const char *pszUserPwd = |
4476 | 42.9k | GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", nullptr); |
4477 | | |
4478 | 42.9k | const bool bOpenSubdataset = STARTS_WITH(poOpenInfo->pszFilename, "PDF:"); |
4479 | 42.9k | const bool bOpenSubdatasetImage = |
4480 | 42.9k | STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:"); |
4481 | 42.9k | int iPage = -1; |
4482 | 42.9k | int nImageNum = -1; |
4483 | 42.9k | std::string osSubdatasetName; |
4484 | 42.9k | const char *pszFilename = poOpenInfo->pszFilename; |
4485 | | |
4486 | 42.9k | if (bOpenSubdataset) |
4487 | 0 | { |
4488 | 0 | iPage = atoi(pszFilename + 4); |
4489 | 0 | if (iPage <= 0) |
4490 | 0 | return nullptr; |
4491 | 0 | pszFilename = strchr(pszFilename + 4, ':'); |
4492 | 0 | if (pszFilename == nullptr) |
4493 | 0 | return nullptr; |
4494 | 0 | pszFilename++; |
4495 | 0 | osSubdatasetName = CPLSPrintf("Page %d", iPage); |
4496 | 0 | } |
4497 | 42.9k | else if (bOpenSubdatasetImage) |
4498 | 0 | { |
4499 | 0 | iPage = atoi(pszFilename + 10); |
4500 | 0 | if (iPage <= 0) |
4501 | 0 | return nullptr; |
4502 | 0 | const char *pszNext = strchr(pszFilename + 10, ':'); |
4503 | 0 | if (pszNext == nullptr) |
4504 | 0 | return nullptr; |
4505 | 0 | nImageNum = atoi(pszNext + 1); |
4506 | 0 | if (nImageNum <= 0) |
4507 | 0 | return nullptr; |
4508 | 0 | pszFilename = strchr(pszNext + 1, ':'); |
4509 | 0 | if (pszFilename == nullptr) |
4510 | 0 | return nullptr; |
4511 | 0 | pszFilename++; |
4512 | 0 | osSubdatasetName = CPLSPrintf("Image %d", nImageNum); |
4513 | 0 | } |
4514 | 42.9k | else |
4515 | 42.9k | iPage = 1; |
4516 | | |
4517 | 42.9k | std::bitset<PDFLIB_COUNT> bHasLib; |
4518 | 42.9k | bHasLib.reset(); |
4519 | | // Each library set their flag |
4520 | 42.9k | #if defined(HAVE_POPPLER) |
4521 | 42.9k | bHasLib.set(PDFLIB_POPPLER); |
4522 | 42.9k | #endif // HAVE_POPPLER |
4523 | | #if defined(HAVE_PODOFO) |
4524 | | bHasLib.set(PDFLIB_PODOFO); |
4525 | | #endif // HAVE_PODOFO |
4526 | | #if defined(HAVE_PDFIUM) |
4527 | | bHasLib.set(PDFLIB_PDFIUM); |
4528 | | #endif // HAVE_PDFIUM |
4529 | | |
4530 | 42.9k | std::bitset<PDFLIB_COUNT> bUseLib; |
4531 | | |
4532 | | // More than one library available |
4533 | | // Detect which one |
4534 | 42.9k | if (bHasLib.count() != 1) |
4535 | 0 | { |
4536 | 0 | const char *pszDefaultLib = bHasLib.test(PDFLIB_PDFIUM) ? "PDFIUM" |
4537 | 0 | : bHasLib.test(PDFLIB_POPPLER) ? "POPPLER" |
4538 | 0 | : "PODOFO"; |
4539 | 0 | const char *pszPDFLib = |
4540 | 0 | GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", pszDefaultLib); |
4541 | 0 | while (true) |
4542 | 0 | { |
4543 | 0 | if (EQUAL(pszPDFLib, "POPPLER")) |
4544 | 0 | bUseLib.set(PDFLIB_POPPLER); |
4545 | 0 | else if (EQUAL(pszPDFLib, "PODOFO")) |
4546 | 0 | bUseLib.set(PDFLIB_PODOFO); |
4547 | 0 | else if (EQUAL(pszPDFLib, "PDFIUM")) |
4548 | 0 | bUseLib.set(PDFLIB_PDFIUM); |
4549 | |
|
4550 | 0 | if (bUseLib.count() != 1 || (bHasLib & bUseLib) == 0) |
4551 | 0 | { |
4552 | 0 | CPLDebug("PDF", |
4553 | 0 | "Invalid value for GDAL_PDF_LIB config option: %s. " |
4554 | 0 | "Fallback to %s", |
4555 | 0 | pszPDFLib, pszDefaultLib); |
4556 | 0 | pszPDFLib = pszDefaultLib; |
4557 | 0 | bUseLib.reset(); |
4558 | 0 | } |
4559 | 0 | else |
4560 | 0 | break; |
4561 | 0 | } |
4562 | 0 | } |
4563 | 42.9k | else |
4564 | 42.9k | bUseLib = bHasLib; |
4565 | | |
4566 | 42.9k | GDALPDFObject *poPageObj = nullptr; |
4567 | 42.9k | #ifdef HAVE_POPPLER |
4568 | 42.9k | PDFDoc *poDocPoppler = nullptr; |
4569 | 42.9k | Page *poPagePoppler = nullptr; |
4570 | 42.9k | Catalog *poCatalogPoppler = nullptr; |
4571 | 42.9k | #endif |
4572 | | #ifdef HAVE_PODOFO |
4573 | | std::unique_ptr<PoDoFo::PdfMemDocument> poDocPodofo; |
4574 | | PoDoFo::PdfPage *poPagePodofo = nullptr; |
4575 | | #endif |
4576 | | #ifdef HAVE_PDFIUM |
4577 | | TPdfiumDocumentStruct *poDocPdfium = nullptr; |
4578 | | TPdfiumPageStruct *poPagePdfium = nullptr; |
4579 | | #endif |
4580 | 42.9k | int nPages = 0; |
4581 | 42.9k | VSIVirtualHandleUniquePtr fp; |
4582 | | |
4583 | 42.9k | #ifdef HAVE_POPPLER |
4584 | 42.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
4585 | 42.9k | { |
4586 | 42.9k | static bool globalParamsCreatedByGDAL = false; |
4587 | 42.9k | { |
4588 | 42.9k | CPLMutexHolderD(&hGlobalParamsMutex); |
4589 | | /* poppler global variable */ |
4590 | 42.9k | if (globalParams == nullptr) |
4591 | 11 | { |
4592 | 11 | globalParamsCreatedByGDAL = true; |
4593 | 11 | globalParams.reset(new GlobalParams()); |
4594 | 11 | } |
4595 | | |
4596 | 42.9k | globalParams->setPrintCommands(CPLTestBool( |
4597 | 42.9k | CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE"))); |
4598 | 42.9k | } |
4599 | | |
4600 | 42.9k | const auto registerErrorCallback = []() |
4601 | 85.8k | { |
4602 | | /* Set custom error handler for poppler errors */ |
4603 | 85.8k | setErrorCallback(PDFDatasetErrorFunction); |
4604 | 85.8k | assert(globalParams); // avoid CSA false positive |
4605 | 85.8k | globalParams->setErrQuiet(false); |
4606 | 85.8k | }; |
4607 | | |
4608 | 42.9k | fp.reset(VSIFOpenL(pszFilename, "rb")); |
4609 | 42.9k | if (!fp) |
4610 | 0 | return nullptr; |
4611 | | |
4612 | 42.9k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
4613 | 42.9k | { |
4614 | | // Workaround for ossfuzz only due to |
4615 | | // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37584 |
4616 | | // https://gitlab.freedesktop.org/poppler/poppler/-/issues/1137 |
4617 | 42.9k | GByte *pabyRet = nullptr; |
4618 | 42.9k | vsi_l_offset nSize = 0; |
4619 | 42.9k | if (VSIIngestFile(fp.get(), pszFilename, &pabyRet, &nSize, |
4620 | 42.9k | 10 * 1024 * 1024)) |
4621 | 42.9k | { |
4622 | | // Replace nul byte by something else so that strstr() works |
4623 | 1.49G | for (size_t i = 0; i < nSize; i++) |
4624 | 1.49G | { |
4625 | 1.49G | if (pabyRet[i] == 0) |
4626 | 63.4M | pabyRet[i] = ' '; |
4627 | 1.49G | } |
4628 | 42.9k | if (strstr(reinterpret_cast<const char *>(pabyRet), |
4629 | 42.9k | "/JBIG2Decode")) |
4630 | 22 | { |
4631 | 22 | CPLError(CE_Failure, CPLE_AppDefined, |
4632 | 22 | "/JBIG2Decode found. Giving up due to potential " |
4633 | 22 | "very long processing time."); |
4634 | 22 | CPLFree(pabyRet); |
4635 | 22 | return nullptr; |
4636 | 22 | } |
4637 | 42.9k | } |
4638 | 42.9k | CPLFree(pabyRet); |
4639 | 42.9k | } |
4640 | 0 | #endif |
4641 | | |
4642 | 0 | fp.reset(VSICreateBufferedReaderHandle(fp.release())); |
4643 | 42.9k | while (true) |
4644 | 42.9k | { |
4645 | 42.9k | fp->Seek(0, SEEK_SET); |
4646 | 42.9k | g_nPopplerErrors = 0; |
4647 | 42.9k | if (globalParamsCreatedByGDAL) |
4648 | 42.9k | registerErrorCallback(); |
4649 | 42.9k | Object oObj; |
4650 | 42.9k | auto poStream = |
4651 | 42.9k | new VSIPDFFileStream(fp.get(), pszFilename, std::move(oObj)); |
4652 | 42.9k | #if POPPLER_MAJOR_VERSION > 22 || \ |
4653 | 42.9k | (POPPLER_MAJOR_VERSION == 22 && POPPLER_MINOR_VERSION > 2) |
4654 | 42.9k | std::optional<GooString> osUserPwd; |
4655 | 42.9k | if (pszUserPwd) |
4656 | 0 | osUserPwd = std::optional<GooString>(pszUserPwd); |
4657 | 42.9k | try |
4658 | 42.9k | { |
4659 | 42.9k | poDocPoppler = |
4660 | 42.9k | new PDFDoc(poStream, std::optional<GooString>(), osUserPwd); |
4661 | 42.9k | } |
4662 | 42.9k | catch (const std::exception &e) |
4663 | 42.9k | { |
4664 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4665 | 0 | "PDFDoc::PDFDoc() failed with %s", e.what()); |
4666 | 0 | return nullptr; |
4667 | 0 | } |
4668 | | #else |
4669 | | GooString *poUserPwd = nullptr; |
4670 | | if (pszUserPwd) |
4671 | | poUserPwd = new GooString(pszUserPwd); |
4672 | | poDocPoppler = new PDFDoc(poStream, nullptr, poUserPwd); |
4673 | | delete poUserPwd; |
4674 | | #endif |
4675 | 42.9k | if (globalParamsCreatedByGDAL) |
4676 | 42.9k | registerErrorCallback(); |
4677 | 42.9k | if (g_nPopplerErrors >= MAX_POPPLER_ERRORS) |
4678 | 5.11k | { |
4679 | 5.11k | PDFFreeDoc(poDocPoppler); |
4680 | 5.11k | return nullptr; |
4681 | 5.11k | } |
4682 | | |
4683 | 37.8k | if (!poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0) |
4684 | 5.67k | { |
4685 | 5.67k | if (poDocPoppler->getErrorCode() == errEncrypted) |
4686 | 79 | { |
4687 | 79 | if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE")) |
4688 | 0 | { |
4689 | 0 | pszUserPwd = |
4690 | 0 | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4691 | 0 | PDFFreeDoc(poDocPoppler); |
4692 | | |
4693 | | /* Reset errors that could have been issued during |
4694 | | * opening and that */ |
4695 | | /* did not result in an invalid document */ |
4696 | 0 | CPLErrorReset(); |
4697 | |
|
4698 | 0 | continue; |
4699 | 0 | } |
4700 | 79 | else if (pszUserPwd == nullptr) |
4701 | 79 | { |
4702 | 79 | CPLError(CE_Failure, CPLE_AppDefined, |
4703 | 79 | "A password is needed. You can specify it " |
4704 | 79 | "through the PDF_USER_PWD " |
4705 | 79 | "configuration option / USER_PWD open option " |
4706 | 79 | "(that can be set to ASK_INTERACTIVE)"); |
4707 | 79 | } |
4708 | 0 | else |
4709 | 0 | { |
4710 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4711 | 0 | "Invalid password"); |
4712 | 0 | } |
4713 | 79 | } |
4714 | 5.59k | else |
4715 | 5.59k | { |
4716 | 5.59k | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4717 | 5.59k | } |
4718 | | |
4719 | 5.67k | PDFFreeDoc(poDocPoppler); |
4720 | 5.67k | return nullptr; |
4721 | 5.67k | } |
4722 | 32.1k | else if (poDocPoppler->isLinearized() && |
4723 | 4 | !poStream->FoundLinearizedHint()) |
4724 | 0 | { |
4725 | | // This is a likely defect of poppler Linearization.cc file that |
4726 | | // recognizes a file as linearized if the /Linearized hint is |
4727 | | // missing, but the content of this dictionary are present. But |
4728 | | // given the hacks of PDFFreeDoc() and |
4729 | | // VSIPDFFileStream::FillBuffer() opening such a file will |
4730 | | // result in a null-ptr deref at closing if we try to access a |
4731 | | // page and build the page cache, so just exit now |
4732 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4733 | |
|
4734 | 0 | PDFFreeDoc(poDocPoppler); |
4735 | 0 | return nullptr; |
4736 | 0 | } |
4737 | 32.1k | else |
4738 | 32.1k | { |
4739 | 32.1k | break; |
4740 | 32.1k | } |
4741 | 37.8k | } |
4742 | | |
4743 | 32.1k | poCatalogPoppler = poDocPoppler->getCatalog(); |
4744 | 32.1k | if (poCatalogPoppler == nullptr || !poCatalogPoppler->isOk()) |
4745 | 0 | { |
4746 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4747 | 0 | "Invalid PDF : invalid catalog"); |
4748 | 0 | PDFFreeDoc(poDocPoppler); |
4749 | 0 | return nullptr; |
4750 | 0 | } |
4751 | | |
4752 | 32.1k | nPages = poDocPoppler->getNumPages(); |
4753 | | |
4754 | 32.1k | if (iPage == 1 && nPages > 10000 && |
4755 | 0 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_LIMIT_PAGE_COUNT", "YES"))) |
4756 | 0 | { |
4757 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
4758 | 0 | "This PDF document reports %d pages. " |
4759 | 0 | "Limiting count to 10000 for performance reasons. " |
4760 | 0 | "You may remove this limit by setting the " |
4761 | 0 | "GDAL_PDF_LIMIT_PAGE_COUNT configuration option to NO", |
4762 | 0 | nPages); |
4763 | 0 | nPages = 10000; |
4764 | 0 | } |
4765 | | |
4766 | 32.1k | if (iPage < 1 || iPage > nPages) |
4767 | 0 | { |
4768 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4769 | 0 | iPage, nPages); |
4770 | 0 | PDFFreeDoc(poDocPoppler); |
4771 | 0 | return nullptr; |
4772 | 0 | } |
4773 | | |
4774 | | /* Sanity check to validate page count */ |
4775 | 32.1k | if (iPage > 1 && nPages <= 10000 && iPage != nPages) |
4776 | 0 | { |
4777 | 0 | poPagePoppler = poCatalogPoppler->getPage(nPages); |
4778 | 0 | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4779 | 0 | { |
4780 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4781 | 0 | "Invalid PDF : invalid page count"); |
4782 | 0 | PDFFreeDoc(poDocPoppler); |
4783 | 0 | return nullptr; |
4784 | 0 | } |
4785 | 0 | } |
4786 | | |
4787 | 32.1k | poPagePoppler = poCatalogPoppler->getPage(iPage); |
4788 | 32.1k | if (poPagePoppler == nullptr || !poPagePoppler->isOk()) |
4789 | 131 | { |
4790 | 131 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
4791 | 131 | PDFFreeDoc(poDocPoppler); |
4792 | 131 | return nullptr; |
4793 | 131 | } |
4794 | | |
4795 | | #if POPPLER_MAJOR_VERSION > 25 || \ |
4796 | | (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 3) |
4797 | | const Object &oPageObj = poPagePoppler->getPageObj(); |
4798 | | #else |
4799 | | /* Here's the dirty part: this is a private member */ |
4800 | | /* so we had to #define private public to get it ! */ |
4801 | 31.9k | const Object &oPageObj = poPagePoppler->pageObj; |
4802 | 31.9k | #endif |
4803 | 31.9k | if (!oPageObj.isDict()) |
4804 | 0 | { |
4805 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4806 | 0 | "Invalid PDF : !oPageObj.isDict()"); |
4807 | 0 | PDFFreeDoc(poDocPoppler); |
4808 | 0 | return nullptr; |
4809 | 0 | } |
4810 | | |
4811 | 31.9k | poPageObj = new GDALPDFObjectPoppler(&oPageObj); |
4812 | 31.9k | Ref *poPageRef = poCatalogPoppler->getPageRef(iPage); |
4813 | 31.9k | if (poPageRef != nullptr) |
4814 | 31.9k | { |
4815 | 31.9k | cpl::down_cast<GDALPDFObjectPoppler *>(poPageObj)->SetRefNumAndGen( |
4816 | 31.9k | GDALPDFObjectNum(poPageRef->num), poPageRef->gen); |
4817 | 31.9k | } |
4818 | 31.9k | } |
4819 | 31.9k | #endif // ~ HAVE_POPPLER |
4820 | | |
4821 | | #ifdef HAVE_PODOFO |
4822 | | if (bUseLib.test(PDFLIB_PODOFO) && poPageObj == nullptr) |
4823 | | { |
4824 | | #if !(PODOFO_VERSION_MAJOR > 0 || \ |
4825 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)) |
4826 | | PoDoFo::PdfError::EnableDebug(false); |
4827 | | PoDoFo::PdfError::EnableLogging(false); |
4828 | | #endif |
4829 | | |
4830 | | poDocPodofo = std::make_unique<PoDoFo::PdfMemDocument>(); |
4831 | | try |
4832 | | { |
4833 | | poDocPodofo->Load(pszFilename); |
4834 | | } |
4835 | | catch (PoDoFo::PdfError &oError) |
4836 | | { |
4837 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4838 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4839 | | if (oError.GetCode() == PoDoFo::PdfErrorCode::InvalidPassword) |
4840 | | #else |
4841 | | if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword) |
4842 | | #endif |
4843 | | { |
4844 | | if (pszUserPwd) |
4845 | | { |
4846 | | pszUserPwd = |
4847 | | PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd); |
4848 | | |
4849 | | try |
4850 | | { |
4851 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4852 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4853 | | poDocPodofo = |
4854 | | std::make_unique<PoDoFo::PdfMemDocument>(); |
4855 | | poDocPodofo->Load(pszFilename, pszUserPwd); |
4856 | | #else |
4857 | | poDocPodofo->SetPassword(pszUserPwd); |
4858 | | #endif |
4859 | | } |
4860 | | catch (PoDoFo::PdfError &oError2) |
4861 | | { |
4862 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4863 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4864 | | if (oError2.GetCode() == |
4865 | | PoDoFo::PdfErrorCode::InvalidPassword) |
4866 | | #else |
4867 | | if (oError2.GetError() == |
4868 | | PoDoFo::ePdfError_InvalidPassword) |
4869 | | #endif |
4870 | | { |
4871 | | CPLError(CE_Failure, CPLE_AppDefined, |
4872 | | "Invalid password"); |
4873 | | } |
4874 | | else |
4875 | | { |
4876 | | CPLError(CE_Failure, CPLE_AppDefined, |
4877 | | "Invalid PDF : %s", oError2.what()); |
4878 | | } |
4879 | | return nullptr; |
4880 | | } |
4881 | | catch (...) |
4882 | | { |
4883 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4884 | | return nullptr; |
4885 | | } |
4886 | | } |
4887 | | else |
4888 | | { |
4889 | | CPLError(CE_Failure, CPLE_AppDefined, |
4890 | | "A password is needed. You can specify it through " |
4891 | | "the PDF_USER_PWD " |
4892 | | "configuration option / USER_PWD open option " |
4893 | | "(that can be set to ASK_INTERACTIVE)"); |
4894 | | return nullptr; |
4895 | | } |
4896 | | } |
4897 | | else |
4898 | | { |
4899 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4900 | | oError.what()); |
4901 | | return nullptr; |
4902 | | } |
4903 | | } |
4904 | | catch (...) |
4905 | | { |
4906 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4907 | | return nullptr; |
4908 | | } |
4909 | | |
4910 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4911 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4912 | | auto &oPageCollections = poDocPodofo->GetPages(); |
4913 | | nPages = static_cast<int>(oPageCollections.GetCount()); |
4914 | | #else |
4915 | | nPages = poDocPodofo->GetPageCount(); |
4916 | | #endif |
4917 | | if (iPage < 1 || iPage > nPages) |
4918 | | { |
4919 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", |
4920 | | iPage, nPages); |
4921 | | return nullptr; |
4922 | | } |
4923 | | |
4924 | | try |
4925 | | { |
4926 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4927 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4928 | | /* Sanity check to validate page count */ |
4929 | | if (iPage != nPages) |
4930 | | CPL_IGNORE_RET_VAL(oPageCollections.GetPageAt(nPages - 1)); |
4931 | | |
4932 | | poPagePodofo = &oPageCollections.GetPageAt(iPage - 1); |
4933 | | #else |
4934 | | /* Sanity check to validate page count */ |
4935 | | if (iPage != nPages) |
4936 | | CPL_IGNORE_RET_VAL(poDocPodofo->GetPage(nPages - 1)); |
4937 | | |
4938 | | poPagePodofo = poDocPodofo->GetPage(iPage - 1); |
4939 | | #endif |
4940 | | } |
4941 | | catch (PoDoFo::PdfError &oError) |
4942 | | { |
4943 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", |
4944 | | oError.what()); |
4945 | | return nullptr; |
4946 | | } |
4947 | | catch (...) |
4948 | | { |
4949 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); |
4950 | | return nullptr; |
4951 | | } |
4952 | | |
4953 | | if (poPagePodofo == nullptr) |
4954 | | { |
4955 | | CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); |
4956 | | return nullptr; |
4957 | | } |
4958 | | |
4959 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
4960 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
4961 | | const PoDoFo::PdfObject *pObj = &poPagePodofo->GetObject(); |
4962 | | #else |
4963 | | const PoDoFo::PdfObject *pObj = poPagePodofo->GetObject(); |
4964 | | #endif |
4965 | | poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects()); |
4966 | | } |
4967 | | #endif // ~ HAVE_PODOFO |
4968 | | |
4969 | | #ifdef HAVE_PDFIUM |
4970 | | if (bUseLib.test(PDFLIB_PDFIUM) && poPageObj == nullptr) |
4971 | | { |
4972 | | if (!LoadPdfiumDocumentPage(pszFilename, pszUserPwd, iPage, |
4973 | | &poDocPdfium, &poPagePdfium, &nPages)) |
4974 | | { |
4975 | | // CPLError is called inside function |
4976 | | return nullptr; |
4977 | | } |
4978 | | |
4979 | | const auto pageObj = poPagePdfium->page->GetDict(); |
4980 | | if (pageObj == nullptr) |
4981 | | { |
4982 | | CPLError(CE_Failure, CPLE_AppDefined, |
4983 | | "Invalid PDF : invalid page object"); |
4984 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
4985 | | return nullptr; |
4986 | | } |
4987 | | poPageObj = GDALPDFObjectPdfium::Build(pageObj); |
4988 | | } |
4989 | | #endif // ~ HAVE_PDFIUM |
4990 | | |
4991 | 31.9k | if (poPageObj == nullptr) |
4992 | 0 | return nullptr; |
4993 | 31.9k | GDALPDFDictionary *poPageDict = poPageObj->GetDictionary(); |
4994 | 31.9k | if (poPageDict == nullptr) |
4995 | 0 | { |
4996 | 0 | delete poPageObj; |
4997 | |
|
4998 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4999 | 0 | "Invalid PDF : poPageDict == nullptr"); |
5000 | 0 | #ifdef HAVE_POPPLER |
5001 | 0 | if (bUseLib.test(PDFLIB_POPPLER)) |
5002 | 0 | PDFFreeDoc(poDocPoppler); |
5003 | 0 | #endif |
5004 | | #ifdef HAVE_PDFIUM |
5005 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5006 | | { |
5007 | | UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium); |
5008 | | } |
5009 | | #endif |
5010 | 0 | return nullptr; |
5011 | 0 | } |
5012 | | |
5013 | 31.9k | const char *pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", nullptr); |
5014 | 31.9k | if (pszDumpObject != nullptr) |
5015 | 0 | { |
5016 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpObject); |
5017 | 0 | oDumper.Dump(poPageObj); |
5018 | 0 | } |
5019 | | |
5020 | 31.9k | PDFDataset *poDS = new PDFDataset(); |
5021 | 31.9k | poDS->m_fp = std::move(fp); |
5022 | 31.9k | poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions); |
5023 | 31.9k | poDS->m_bUseLib = bUseLib; |
5024 | 31.9k | poDS->m_osFilename = pszFilename; |
5025 | 31.9k | poDS->eAccess = poOpenInfo->eAccess; |
5026 | | |
5027 | 31.9k | if (nPages > 1 && !bOpenSubdataset) |
5028 | 3.51k | { |
5029 | 3.51k | int i; |
5030 | 3.51k | CPLStringList aosList; |
5031 | 85.2k | for (i = 0; i < nPages; i++) |
5032 | 81.7k | { |
5033 | 81.7k | char szKey[32]; |
5034 | 81.7k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_NAME", i + 1); |
5035 | 81.7k | aosList.AddNameValue( |
5036 | 81.7k | szKey, CPLSPrintf("PDF:%d:%s", i + 1, poOpenInfo->pszFilename)); |
5037 | 81.7k | snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_DESC", i + 1); |
5038 | 81.7k | aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i + 1, |
5039 | 81.7k | poOpenInfo->pszFilename)); |
5040 | 81.7k | } |
5041 | 3.51k | poDS->SetMetadata(aosList.List(), "SUBDATASETS"); |
5042 | 3.51k | } |
5043 | | |
5044 | 31.9k | #ifdef HAVE_POPPLER |
5045 | 31.9k | poDS->m_poDocPoppler = poDocPoppler; |
5046 | 31.9k | #endif |
5047 | | #ifdef HAVE_PODOFO |
5048 | | poDS->m_poDocPodofo = poDocPodofo.release(); |
5049 | | #endif |
5050 | | #ifdef HAVE_PDFIUM |
5051 | | poDS->m_poDocPdfium = poDocPdfium; |
5052 | | poDS->m_poPagePdfium = poPagePdfium; |
5053 | | #endif |
5054 | 31.9k | poDS->m_poPageObj = poPageObj; |
5055 | 31.9k | poDS->m_osUserPwd = pszUserPwd ? pszUserPwd : ""; |
5056 | 31.9k | poDS->m_iPage = iPage; |
5057 | | |
5058 | 31.9k | const char *pszDumpCatalog = |
5059 | 31.9k | CPLGetConfigOption("PDF_DUMP_CATALOG", nullptr); |
5060 | 31.9k | if (pszDumpCatalog != nullptr) |
5061 | 0 | { |
5062 | 0 | GDALPDFDumper oDumper(pszFilename, pszDumpCatalog); |
5063 | 0 | auto poCatalog = poDS->GetCatalog(); |
5064 | 0 | if (poCatalog) |
5065 | 0 | oDumper.Dump(poCatalog); |
5066 | 0 | } |
5067 | | |
5068 | 31.9k | int nBandsGuessed = 0; |
5069 | 31.9k | if (nImageNum < 0) |
5070 | 31.9k | { |
5071 | 31.9k | poDS->GuessDPI(poPageDict, &nBandsGuessed); |
5072 | 31.9k | if (nBandsGuessed < 4) |
5073 | 31.9k | nBandsGuessed = 0; |
5074 | 31.9k | } |
5075 | 0 | else |
5076 | 0 | { |
5077 | 0 | const char *pszDPI = |
5078 | 0 | GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr); |
5079 | 0 | if (pszDPI != nullptr) |
5080 | 0 | { |
5081 | | // coverity[tainted_data] |
5082 | 0 | poDS->m_dfDPI = CPLAtof(pszDPI); |
5083 | 0 | } |
5084 | 0 | } |
5085 | | |
5086 | 31.9k | double dfX1 = 0.0; |
5087 | 31.9k | double dfY1 = 0.0; |
5088 | 31.9k | double dfX2 = 0.0; |
5089 | 31.9k | double dfY2 = 0.0; |
5090 | | |
5091 | 31.9k | #ifdef HAVE_POPPLER |
5092 | 31.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
5093 | 31.9k | { |
5094 | 31.9k | const auto *psMediaBox = poPagePoppler->getMediaBox(); |
5095 | 31.9k | dfX1 = psMediaBox->x1; |
5096 | 31.9k | dfY1 = psMediaBox->y1; |
5097 | 31.9k | dfX2 = psMediaBox->x2; |
5098 | 31.9k | dfY2 = psMediaBox->y2; |
5099 | 31.9k | } |
5100 | 31.9k | #endif |
5101 | | |
5102 | | #ifdef HAVE_PODOFO |
5103 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5104 | | { |
5105 | | CPLAssert(poPagePodofo); |
5106 | | auto oMediaBox = poPagePodofo->GetMediaBox(); |
5107 | | dfX1 = oMediaBox.GetLeft(); |
5108 | | dfY1 = oMediaBox.GetBottom(); |
5109 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5110 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5111 | | dfX2 = dfX1 + oMediaBox.Width; |
5112 | | dfY2 = dfY1 + oMediaBox.Height; |
5113 | | #else |
5114 | | dfX2 = dfX1 + oMediaBox.GetWidth(); |
5115 | | dfY2 = dfY1 + oMediaBox.GetHeight(); |
5116 | | #endif |
5117 | | } |
5118 | | #endif |
5119 | | |
5120 | | #ifdef HAVE_PDFIUM |
5121 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5122 | | { |
5123 | | CPLAssert(poPagePdfium); |
5124 | | CFX_FloatRect rect = poPagePdfium->page->GetBBox(); |
5125 | | dfX1 = rect.left; |
5126 | | dfX2 = rect.right; |
5127 | | dfY1 = rect.bottom; |
5128 | | dfY2 = rect.top; |
5129 | | } |
5130 | | #endif // ~ HAVE_PDFIUM |
5131 | | |
5132 | 31.9k | double dfUserUnit = poDS->m_dfDPI * USER_UNIT_IN_INCH; |
5133 | 31.9k | poDS->m_dfPageWidth = dfX2 - dfX1; |
5134 | 31.9k | poDS->m_dfPageHeight = dfY2 - dfY1; |
5135 | | // CPLDebug("PDF", "left=%f right=%f bottom=%f top=%f", dfX1, dfX2, dfY1, |
5136 | | // dfY2); |
5137 | 31.9k | const double dfXSize = floor((dfX2 - dfX1) * dfUserUnit + 0.5); |
5138 | 31.9k | const double dfYSize = floor((dfY2 - dfY1) * dfUserUnit + 0.5); |
5139 | 31.9k | if (!(dfXSize >= 0 && dfXSize <= INT_MAX && dfYSize >= 0 && |
5140 | 31.9k | dfYSize <= INT_MAX)) |
5141 | 5 | { |
5142 | 5 | delete poDS; |
5143 | 5 | return nullptr; |
5144 | 5 | } |
5145 | 31.9k | poDS->nRasterXSize = static_cast<int>(dfXSize); |
5146 | 31.9k | poDS->nRasterYSize = static_cast<int>(dfYSize); |
5147 | | |
5148 | 31.9k | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) |
5149 | 23 | { |
5150 | 23 | delete poDS; |
5151 | 23 | return nullptr; |
5152 | 23 | } |
5153 | | |
5154 | 31.9k | double dfRotation = 0; |
5155 | 31.9k | #ifdef HAVE_POPPLER |
5156 | 31.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
5157 | 31.9k | dfRotation = poDocPoppler->getPageRotate(iPage); |
5158 | 31.9k | #endif |
5159 | | |
5160 | | #ifdef HAVE_PODOFO |
5161 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5162 | | { |
5163 | | CPLAssert(poPagePodofo); |
5164 | | #if PODOFO_VERSION_MAJOR >= 1 |
5165 | | poPagePodofo->TryGetRotationRaw(dfRotation); |
5166 | | #elif (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5167 | | dfRotation = poPagePodofo->GetRotationRaw(); |
5168 | | #else |
5169 | | dfRotation = poPagePodofo->GetRotation(); |
5170 | | #endif |
5171 | | } |
5172 | | #endif |
5173 | | |
5174 | | #ifdef HAVE_PDFIUM |
5175 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5176 | | { |
5177 | | CPLAssert(poPagePdfium); |
5178 | | dfRotation = poPagePdfium->page->GetPageRotation() * 90; |
5179 | | } |
5180 | | #endif |
5181 | | |
5182 | 31.9k | if (dfRotation == 90 || dfRotation == -90 || dfRotation == 270) |
5183 | 166 | { |
5184 | | /* FIXME: the podofo case should be implemented. This needs to rotate */ |
5185 | | /* the output of pdftoppm */ |
5186 | 166 | #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM) |
5187 | 166 | if (bUseLib.test(PDFLIB_POPPLER) || bUseLib.test(PDFLIB_PDFIUM)) |
5188 | 166 | { |
5189 | 166 | int nTmp = poDS->nRasterXSize; |
5190 | 166 | poDS->nRasterXSize = poDS->nRasterYSize; |
5191 | 166 | poDS->nRasterYSize = nTmp; |
5192 | 166 | } |
5193 | 166 | #endif |
5194 | 166 | } |
5195 | | |
5196 | 31.9k | if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5197 | 18.4k | { |
5198 | 18.4k | poDS->m_nBlockXSize = 512; |
5199 | 18.4k | poDS->m_nBlockYSize = 512; |
5200 | 18.4k | } |
5201 | | /* Check if the PDF is only made of regularly tiled images */ |
5202 | | /* (like some USGS GeoPDF production) */ |
5203 | 13.5k | else if (dfRotation == 0.0 && !poDS->m_asTiles.empty() && |
5204 | 217 | EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"), |
5205 | 13.5k | "ALL")) |
5206 | 217 | { |
5207 | 217 | poDS->CheckTiledRaster(); |
5208 | 217 | if (!poDS->m_aiTiles.empty()) |
5209 | 0 | poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); |
5210 | 217 | } |
5211 | | |
5212 | 31.9k | GDALPDFObject *poLGIDict = nullptr; |
5213 | 31.9k | GDALPDFObject *poVP = nullptr; |
5214 | 31.9k | int bIsOGCBP = FALSE; |
5215 | 31.9k | if ((poLGIDict = poPageDict->Get("LGIDict")) != nullptr && nImageNum < 0) |
5216 | 529 | { |
5217 | | /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */ |
5218 | 529 | CPLDebug("PDF", "OGC Encoding Best Practice style detected"); |
5219 | 529 | if (poDS->ParseLGIDictObject(poLGIDict)) |
5220 | 197 | { |
5221 | 197 | if (poDS->m_bHasCTM) |
5222 | 178 | { |
5223 | 178 | if (dfRotation == 90) |
5224 | 0 | { |
5225 | 0 | poDS->m_gt[0] = poDS->m_adfCTM[4]; |
5226 | 0 | poDS->m_gt[1] = poDS->m_adfCTM[2] / dfUserUnit; |
5227 | 0 | poDS->m_gt[2] = poDS->m_adfCTM[0] / dfUserUnit; |
5228 | 0 | poDS->m_gt[3] = poDS->m_adfCTM[5]; |
5229 | 0 | poDS->m_gt[4] = poDS->m_adfCTM[3] / dfUserUnit; |
5230 | 0 | poDS->m_gt[5] = poDS->m_adfCTM[1] / dfUserUnit; |
5231 | 0 | } |
5232 | 178 | else if (dfRotation == -90 || dfRotation == 270) |
5233 | 0 | { |
5234 | 0 | poDS->m_gt[0] = poDS->m_adfCTM[4] + |
5235 | 0 | poDS->m_adfCTM[2] * poDS->m_dfPageHeight + |
5236 | 0 | poDS->m_adfCTM[0] * poDS->m_dfPageWidth; |
5237 | 0 | poDS->m_gt[1] = -poDS->m_adfCTM[2] / dfUserUnit; |
5238 | 0 | poDS->m_gt[2] = -poDS->m_adfCTM[0] / dfUserUnit; |
5239 | 0 | poDS->m_gt[3] = poDS->m_adfCTM[5] + |
5240 | 0 | poDS->m_adfCTM[3] * poDS->m_dfPageHeight + |
5241 | 0 | poDS->m_adfCTM[1] * poDS->m_dfPageWidth; |
5242 | 0 | poDS->m_gt[4] = -poDS->m_adfCTM[3] / dfUserUnit; |
5243 | 0 | poDS->m_gt[5] = -poDS->m_adfCTM[1] / dfUserUnit; |
5244 | 0 | } |
5245 | 178 | else |
5246 | 178 | { |
5247 | 178 | poDS->m_gt[0] = poDS->m_adfCTM[4] + |
5248 | 178 | poDS->m_adfCTM[2] * dfY2 + |
5249 | 178 | poDS->m_adfCTM[0] * dfX1; |
5250 | 178 | poDS->m_gt[1] = poDS->m_adfCTM[0] / dfUserUnit; |
5251 | 178 | poDS->m_gt[2] = -poDS->m_adfCTM[2] / dfUserUnit; |
5252 | 178 | poDS->m_gt[3] = poDS->m_adfCTM[5] + |
5253 | 178 | poDS->m_adfCTM[3] * dfY2 + |
5254 | 178 | poDS->m_adfCTM[1] * dfX1; |
5255 | 178 | poDS->m_gt[4] = poDS->m_adfCTM[1] / dfUserUnit; |
5256 | 178 | poDS->m_gt[5] = -poDS->m_adfCTM[3] / dfUserUnit; |
5257 | 178 | } |
5258 | | |
5259 | 178 | poDS->m_bGeoTransformValid = true; |
5260 | 178 | } |
5261 | | |
5262 | 197 | bIsOGCBP = TRUE; |
5263 | | |
5264 | 197 | int i; |
5265 | 271 | for (i = 0; i < poDS->m_nGCPCount; i++) |
5266 | 74 | { |
5267 | 74 | if (dfRotation == 90) |
5268 | 0 | { |
5269 | 0 | double dfPixel = |
5270 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5271 | 0 | double dfLine = |
5272 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5273 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = dfLine; |
5274 | 0 | poDS->m_pasGCPList[i].dfGCPLine = dfPixel; |
5275 | 0 | } |
5276 | 74 | else if (dfRotation == -90 || dfRotation == 270) |
5277 | 0 | { |
5278 | 0 | double dfPixel = |
5279 | 0 | poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit; |
5280 | 0 | double dfLine = |
5281 | 0 | poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit; |
5282 | 0 | poDS->m_pasGCPList[i].dfGCPPixel = |
5283 | 0 | poDS->nRasterXSize - dfLine; |
5284 | 0 | poDS->m_pasGCPList[i].dfGCPLine = |
5285 | 0 | poDS->nRasterYSize - dfPixel; |
5286 | 0 | } |
5287 | 74 | else |
5288 | 74 | { |
5289 | 74 | poDS->m_pasGCPList[i].dfGCPPixel = |
5290 | 74 | (-dfX1 + poDS->m_pasGCPList[i].dfGCPPixel) * dfUserUnit; |
5291 | 74 | poDS->m_pasGCPList[i].dfGCPLine = |
5292 | 74 | (dfY2 - poDS->m_pasGCPList[i].dfGCPLine) * dfUserUnit; |
5293 | 74 | } |
5294 | 74 | } |
5295 | 197 | } |
5296 | 529 | } |
5297 | 31.4k | else if ((poVP = poPageDict->Get("VP")) != nullptr && nImageNum < 0) |
5298 | 5.75k | { |
5299 | | /* Cf adobe_supplement_iso32000.pdf */ |
5300 | 5.75k | CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?"); |
5301 | 5.75k | if (dfX1 != 0 || dfY1 != 0) |
5302 | 118 | { |
5303 | 118 | CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case..."); |
5304 | 118 | } |
5305 | 5.75k | poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1); |
5306 | 5.75k | } |
5307 | 25.6k | else |
5308 | 25.6k | { |
5309 | 25.6k | GDALPDFObject *poXObject = |
5310 | 25.6k | poPageDict->LookupObject("Resources.XObject"); |
5311 | | |
5312 | 25.6k | if (poXObject != nullptr && |
5313 | 7.32k | poXObject->GetType() == PDFObjectType_Dictionary) |
5314 | 7.27k | { |
5315 | 7.27k | GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary(); |
5316 | 7.27k | const auto &oMap = poXObjectDict->GetValues(); |
5317 | 7.27k | int nSubDataset = 0; |
5318 | 7.27k | for (const auto &[osKey, poObj] : oMap) |
5319 | 47.7k | { |
5320 | 47.7k | if (poObj->GetType() == PDFObjectType_Dictionary) |
5321 | 44.6k | { |
5322 | 44.6k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
5323 | 44.6k | GDALPDFObject *poSubtype = nullptr; |
5324 | 44.6k | GDALPDFObject *poMeasure = nullptr; |
5325 | 44.6k | GDALPDFObject *poWidth = nullptr; |
5326 | 44.6k | GDALPDFObject *poHeight = nullptr; |
5327 | 44.6k | int nW = 0; |
5328 | 44.6k | int nH = 0; |
5329 | 44.6k | if ((poSubtype = poDict->Get("Subtype")) != nullptr && |
5330 | 43.4k | poSubtype->GetType() == PDFObjectType_Name && |
5331 | 43.3k | poSubtype->GetName() == "Image" && |
5332 | 9.04k | (poMeasure = poDict->Get("Measure")) != nullptr && |
5333 | 1 | poMeasure->GetType() == PDFObjectType_Dictionary && |
5334 | 0 | (poWidth = poDict->Get("Width")) != nullptr && |
5335 | 0 | poWidth->GetType() == PDFObjectType_Int && |
5336 | 0 | (nW = poWidth->GetInt()) > 0 && |
5337 | 0 | (poHeight = poDict->Get("Height")) != nullptr && |
5338 | 0 | poHeight->GetType() == PDFObjectType_Int && |
5339 | 0 | (nH = poHeight->GetInt()) > 0) |
5340 | 0 | { |
5341 | 0 | if (nImageNum < 0) |
5342 | 0 | CPLDebug("PDF", |
5343 | 0 | "Measure found on Image object (%d)", |
5344 | 0 | poObj->GetRefNum().toInt()); |
5345 | |
|
5346 | 0 | GDALPDFObject *poColorSpace = poDict->Get("ColorSpace"); |
5347 | 0 | GDALPDFObject *poBitsPerComponent = |
5348 | 0 | poDict->Get("BitsPerComponent"); |
5349 | 0 | if (poObj->GetRefNum().toBool() && |
5350 | 0 | poObj->GetRefGen() == 0 && |
5351 | 0 | poColorSpace != nullptr && |
5352 | 0 | poColorSpace->GetType() == PDFObjectType_Name && |
5353 | 0 | (poColorSpace->GetName() == "DeviceGray" || |
5354 | 0 | poColorSpace->GetName() == "DeviceRGB") && |
5355 | 0 | (poBitsPerComponent == nullptr || |
5356 | 0 | (poBitsPerComponent->GetType() == |
5357 | 0 | PDFObjectType_Int && |
5358 | 0 | poBitsPerComponent->GetInt() == 8))) |
5359 | 0 | { |
5360 | 0 | if (nImageNum < 0) |
5361 | 0 | { |
5362 | 0 | nSubDataset++; |
5363 | 0 | poDS->SetMetadataItem( |
5364 | 0 | CPLSPrintf("SUBDATASET_%d_NAME", |
5365 | 0 | nSubDataset), |
5366 | 0 | CPLSPrintf("PDF_IMAGE:%d:%d:%s", iPage, |
5367 | 0 | poObj->GetRefNum().toInt(), |
5368 | 0 | pszFilename), |
5369 | 0 | "SUBDATASETS"); |
5370 | 0 | poDS->SetMetadataItem( |
5371 | 0 | CPLSPrintf("SUBDATASET_%d_DESC", |
5372 | 0 | nSubDataset), |
5373 | 0 | CPLSPrintf("Georeferenced image of size " |
5374 | 0 | "%dx%d of page %d of %s", |
5375 | 0 | nW, nH, iPage, pszFilename), |
5376 | 0 | "SUBDATASETS"); |
5377 | 0 | } |
5378 | 0 | else if (poObj->GetRefNum().toInt() == nImageNum) |
5379 | 0 | { |
5380 | 0 | poDS->nRasterXSize = nW; |
5381 | 0 | poDS->nRasterYSize = nH; |
5382 | 0 | poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW, |
5383 | 0 | 0); |
5384 | 0 | poDS->m_poImageObj = poObj; |
5385 | 0 | if (poColorSpace->GetName() == "DeviceGray") |
5386 | 0 | nBandsGuessed = 1; |
5387 | 0 | break; |
5388 | 0 | } |
5389 | 0 | } |
5390 | 0 | } |
5391 | 44.6k | } |
5392 | 47.7k | } |
5393 | 7.27k | } |
5394 | | |
5395 | 25.6k | if (nImageNum >= 0 && poDS->m_poImageObj == nullptr) |
5396 | 0 | { |
5397 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d", |
5398 | 0 | nImageNum); |
5399 | 0 | delete poDS; |
5400 | 0 | return nullptr; |
5401 | 0 | } |
5402 | | |
5403 | | /* Not a geospatial PDF doc */ |
5404 | 25.6k | } |
5405 | | |
5406 | | /* If pixel size or top left coordinates are very close to an int, round |
5407 | | * them to the int */ |
5408 | 31.9k | double dfEps = |
5409 | 31.9k | (fabs(poDS->m_gt[0]) > 1e5 && fabs(poDS->m_gt[3]) > 1e5) ? 1e-5 : 1e-8; |
5410 | 31.9k | poDS->m_gt[0] = ROUND_IF_CLOSE(poDS->m_gt[0], dfEps); |
5411 | 31.9k | poDS->m_gt[1] = ROUND_IF_CLOSE(poDS->m_gt[1]); |
5412 | 31.9k | poDS->m_gt[3] = ROUND_IF_CLOSE(poDS->m_gt[3], dfEps); |
5413 | 31.9k | poDS->m_gt[5] = ROUND_IF_CLOSE(poDS->m_gt[5]); |
5414 | | |
5415 | 31.9k | if (bUseLib.test(PDFLIB_PDFIUM)) |
5416 | 0 | { |
5417 | | // Attempt to "fix" the loss of precision due to the use of float32 for |
5418 | | // numbers by pdfium |
5419 | 0 | if ((fabs(poDS->m_gt[0]) > 1e5 || fabs(poDS->m_gt[3]) > 1e5) && |
5420 | 0 | fabs(poDS->m_gt[0] - std::round(poDS->m_gt[0])) < |
5421 | 0 | 1e-6 * fabs(poDS->m_gt[0]) && |
5422 | 0 | fabs(poDS->m_gt[1] - std::round(poDS->m_gt[1])) < |
5423 | 0 | 1e-3 * fabs(poDS->m_gt[1]) && |
5424 | 0 | fabs(poDS->m_gt[3] - std::round(poDS->m_gt[3])) < |
5425 | 0 | 1e-6 * fabs(poDS->m_gt[3]) && |
5426 | 0 | fabs(poDS->m_gt[5] - std::round(poDS->m_gt[5])) < |
5427 | 0 | 1e-3 * fabs(poDS->m_gt[5])) |
5428 | 0 | { |
5429 | 0 | for (int i = 0; i < 6; i++) |
5430 | 0 | { |
5431 | 0 | poDS->m_gt[i] = std::round(poDS->m_gt[i]); |
5432 | 0 | } |
5433 | 0 | } |
5434 | 0 | } |
5435 | | |
5436 | 31.9k | if (poDS->m_poNeatLine) |
5437 | 2.52k | { |
5438 | 2.52k | char *pszNeatLineWkt = nullptr; |
5439 | 2.52k | OGRLinearRing *poRing = poDS->m_poNeatLine->getExteriorRing(); |
5440 | | /* Adobe style is already in target SRS units */ |
5441 | 2.52k | if (bIsOGCBP) |
5442 | 196 | { |
5443 | 196 | int nPoints = poRing->getNumPoints(); |
5444 | 196 | int i; |
5445 | | |
5446 | 85.3k | for (i = 0; i < nPoints; i++) |
5447 | 85.1k | { |
5448 | 85.1k | double x, y; |
5449 | 85.1k | if (dfRotation == 90.0) |
5450 | 0 | { |
5451 | 0 | x = poRing->getY(i) * dfUserUnit; |
5452 | 0 | y = poRing->getX(i) * dfUserUnit; |
5453 | 0 | } |
5454 | 85.1k | else if (dfRotation == -90.0 || dfRotation == 270.0) |
5455 | 0 | { |
5456 | 0 | x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit; |
5457 | 0 | y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit; |
5458 | 0 | } |
5459 | 85.1k | else |
5460 | 85.1k | { |
5461 | 85.1k | x = (-dfX1 + poRing->getX(i)) * dfUserUnit; |
5462 | 85.1k | y = (dfY2 - poRing->getY(i)) * dfUserUnit; |
5463 | 85.1k | } |
5464 | 85.1k | double X = |
5465 | 85.1k | poDS->m_gt[0] + x * poDS->m_gt[1] + y * poDS->m_gt[2]; |
5466 | 85.1k | double Y = |
5467 | 85.1k | poDS->m_gt[3] + x * poDS->m_gt[4] + y * poDS->m_gt[5]; |
5468 | 85.1k | poRing->setPoint(i, X, Y); |
5469 | 85.1k | } |
5470 | 196 | } |
5471 | 2.52k | poRing->closeRings(); |
5472 | | |
5473 | 2.52k | poDS->m_poNeatLine->exportToWkt(&pszNeatLineWkt); |
5474 | 2.52k | if (nImageNum < 0) |
5475 | 2.52k | poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt); |
5476 | 2.52k | CPLFree(pszNeatLineWkt); |
5477 | 2.52k | } |
5478 | | |
5479 | 31.9k | poDS->MapOCGsToPages(); |
5480 | | |
5481 | 31.9k | #ifdef HAVE_POPPLER |
5482 | 31.9k | if (bUseLib.test(PDFLIB_POPPLER)) |
5483 | 31.9k | { |
5484 | 31.9k | auto poMetadata = poCatalogPoppler->readMetadata(); |
5485 | 31.9k | if (poMetadata) |
5486 | 5.15k | { |
5487 | 5.15k | const char *pszContent = poMetadata->c_str(); |
5488 | 5.15k | if (pszContent != nullptr && |
5489 | 5.15k | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5490 | 4.75k | { |
5491 | 4.75k | const char *const apszMDList[2] = {pszContent, nullptr}; |
5492 | 4.75k | poDS->SetMetadata(const_cast<char **>(apszMDList), "xml:XMP"); |
5493 | 4.75k | } |
5494 | | #if (POPPLER_MAJOR_VERSION < 21 || \ |
5495 | | (POPPLER_MAJOR_VERSION == 21 && POPPLER_MINOR_VERSION < 10)) |
5496 | | delete poMetadata; |
5497 | | #endif |
5498 | 5.15k | } |
5499 | | |
5500 | | /* Read Info object */ |
5501 | | /* The test is necessary since with some corrupted PDFs |
5502 | | * poDocPoppler->getDocInfo() */ |
5503 | | /* might abort() */ |
5504 | 31.9k | if (poDocPoppler->getXRef()->isOk()) |
5505 | 31.9k | { |
5506 | 31.9k | Object oInfo = poDocPoppler->getDocInfo(); |
5507 | 31.9k | GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE); |
5508 | 31.9k | poDS->ParseInfo(&oInfoObjPoppler); |
5509 | 31.9k | } |
5510 | | |
5511 | | /* Find layers */ |
5512 | 31.9k | poDS->FindLayersPoppler( |
5513 | 31.9k | (bOpenSubdataset || bOpenSubdatasetImage) ? iPage : 0); |
5514 | | |
5515 | | /* Turn user specified layers on or off */ |
5516 | 31.9k | poDS->TurnLayersOnOffPoppler(); |
5517 | 31.9k | } |
5518 | 31.9k | #endif |
5519 | | |
5520 | | #ifdef HAVE_PODOFO |
5521 | | if (bUseLib.test(PDFLIB_PODOFO)) |
5522 | | { |
5523 | | for (const auto &obj : poDS->m_poDocPodofo->GetObjects()) |
5524 | | { |
5525 | | GDALPDFObjectPodofo oObjPodofo(obj, |
5526 | | poDS->m_poDocPodofo->GetObjects()); |
5527 | | poDS->FindXMP(&oObjPodofo); |
5528 | | } |
5529 | | |
5530 | | /* Find layers */ |
5531 | | poDS->FindLayersGeneric(poPageDict); |
5532 | | |
5533 | | /* Read Info object */ |
5534 | | const PoDoFo::PdfInfo *poInfo = poDS->m_poDocPodofo->GetInfo(); |
5535 | | if (poInfo != nullptr) |
5536 | | { |
5537 | | GDALPDFObjectPodofo oInfoObjPodofo( |
5538 | | #if PODOFO_VERSION_MAJOR > 0 || \ |
5539 | | (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10) |
5540 | | &(poInfo->GetObject()), |
5541 | | #else |
5542 | | poInfo->GetObject(), |
5543 | | #endif |
5544 | | poDS->m_poDocPodofo->GetObjects()); |
5545 | | poDS->ParseInfo(&oInfoObjPodofo); |
5546 | | } |
5547 | | } |
5548 | | #endif |
5549 | | #ifdef HAVE_PDFIUM |
5550 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5551 | | { |
5552 | | // coverity is confused by WrapRetain(), believing that multiple |
5553 | | // smart pointers manage the same raw pointer. Which is actually |
5554 | | // true, but a RetainPtr holds a reference counted object. It is |
5555 | | // thus safe to have several RetainPtr holding it. |
5556 | | // coverity[multiple_init_smart_ptr] |
5557 | | GDALPDFObjectPdfium *poRoot = GDALPDFObjectPdfium::Build( |
5558 | | pdfium::WrapRetain(poDocPdfium->doc->GetRoot())); |
5559 | | if (poRoot->GetType() == PDFObjectType_Dictionary) |
5560 | | { |
5561 | | GDALPDFDictionary *poDict = poRoot->GetDictionary(); |
5562 | | GDALPDFObject *poMetadata(poDict->Get("Metadata")); |
5563 | | if (poMetadata != nullptr) |
5564 | | { |
5565 | | GDALPDFStream *poStream = poMetadata->GetStream(); |
5566 | | if (poStream != nullptr) |
5567 | | { |
5568 | | char *pszContent = poStream->GetBytes(); |
5569 | | const auto nLength = poStream->GetLength(); |
5570 | | if (pszContent != nullptr && nLength > 15 && |
5571 | | STARTS_WITH(pszContent, "<?xpacket begin=")) |
5572 | | { |
5573 | | char *apszMDList[2]; |
5574 | | apszMDList[0] = pszContent; |
5575 | | apszMDList[1] = nullptr; |
5576 | | poDS->SetMetadata(apszMDList, "xml:XMP"); |
5577 | | } |
5578 | | CPLFree(pszContent); |
5579 | | } |
5580 | | } |
5581 | | } |
5582 | | delete poRoot; |
5583 | | |
5584 | | /* Find layers */ |
5585 | | poDS->FindLayersPdfium((bOpenSubdataset || bOpenSubdatasetImage) ? iPage |
5586 | | : 0); |
5587 | | |
5588 | | /* Turn user specified layers on or off */ |
5589 | | poDS->TurnLayersOnOffPdfium(); |
5590 | | |
5591 | | GDALPDFObjectPdfium *poInfo = |
5592 | | GDALPDFObjectPdfium::Build(poDocPdfium->doc->GetInfo()); |
5593 | | if (poInfo) |
5594 | | { |
5595 | | /* Read Info object */ |
5596 | | poDS->ParseInfo(poInfo); |
5597 | | delete poInfo; |
5598 | | } |
5599 | | } |
5600 | | #endif // ~ HAVE_PDFIUM |
5601 | | |
5602 | 31.9k | int nBands = 3; |
5603 | | #ifdef HAVE_PDFIUM |
5604 | | // Use Alpha channel for PDFIUM as default format RGBA |
5605 | | if (bUseLib.test(PDFLIB_PDFIUM)) |
5606 | | nBands = 4; |
5607 | | #endif |
5608 | 31.9k | if (nBandsGuessed) |
5609 | 4 | nBands = nBandsGuessed; |
5610 | 31.9k | const char *pszPDFBands = |
5611 | 31.9k | GetOption(poOpenInfo->papszOpenOptions, "BANDS", nullptr); |
5612 | 31.9k | if (pszPDFBands) |
5613 | 18.4k | { |
5614 | 18.4k | nBands = atoi(pszPDFBands); |
5615 | 18.4k | if (nBands != 3 && nBands != 4) |
5616 | 0 | { |
5617 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
5618 | 0 | "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback"); |
5619 | 0 | nBands = 3; |
5620 | 0 | } |
5621 | 18.4k | } |
5622 | | #ifdef HAVE_PODOFO |
5623 | | if (bUseLib.test(PDFLIB_PODOFO) && nBands == 4 && poDS->m_aiTiles.empty()) |
5624 | | { |
5625 | | CPLError(CE_Warning, CPLE_NotSupported, |
5626 | | "GDAL_PDF_BANDS=4 not supported when PDF driver is compiled " |
5627 | | "against Podofo. " |
5628 | | "Using 3 as a fallback"); |
5629 | | nBands = 3; |
5630 | | } |
5631 | | #endif |
5632 | | |
5633 | 31.9k | int iBand; |
5634 | 127k | for (iBand = 1; iBand <= nBands; iBand++) |
5635 | 95.9k | { |
5636 | 95.9k | if (poDS->m_poImageObj != nullptr) |
5637 | 0 | poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand)); |
5638 | 95.9k | else |
5639 | 95.9k | poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand, 0)); |
5640 | 95.9k | } |
5641 | | |
5642 | | /* Check if this is a raster-only PDF file and that we are */ |
5643 | | /* opened in vector-only mode */ |
5644 | 31.9k | if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && |
5645 | 23.8k | (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && |
5646 | 5.37k | !poDS->OpenVectorLayers(poPageDict)) |
5647 | 4.28k | { |
5648 | 4.28k | CPLDebug("PDF", "This is a raster-only PDF dataset, " |
5649 | 4.28k | "but it has been opened in vector-only mode"); |
5650 | | /* Clear dirty flag */ |
5651 | 4.28k | poDS->m_bProjDirty = false; |
5652 | 4.28k | poDS->m_bNeatLineDirty = false; |
5653 | 4.28k | poDS->m_bInfoDirty = false; |
5654 | 4.28k | poDS->m_bXMPDirty = false; |
5655 | 4.28k | delete poDS; |
5656 | 4.28k | return nullptr; |
5657 | 4.28k | } |
5658 | | |
5659 | | /* -------------------------------------------------------------------- */ |
5660 | | /* Initialize any PAM information. */ |
5661 | | /* -------------------------------------------------------------------- */ |
5662 | 27.6k | if (bOpenSubdataset || bOpenSubdatasetImage) |
5663 | 0 | { |
5664 | 0 | poDS->SetPhysicalFilename(pszFilename); |
5665 | 0 | poDS->SetSubdatasetName(osSubdatasetName.c_str()); |
5666 | 0 | } |
5667 | 27.6k | else |
5668 | 27.6k | { |
5669 | 27.6k | poDS->SetDescription(poOpenInfo->pszFilename); |
5670 | 27.6k | } |
5671 | | |
5672 | 27.6k | poDS->TryLoadXML(); |
5673 | | |
5674 | | /* -------------------------------------------------------------------- */ |
5675 | | /* Support overviews. */ |
5676 | | /* -------------------------------------------------------------------- */ |
5677 | 27.6k | if (!CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW")) |
5678 | 9.23k | { |
5679 | 9.23k | poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); |
5680 | 9.23k | } |
5681 | | |
5682 | | /* Clear dirty flag */ |
5683 | 27.6k | poDS->m_bProjDirty = false; |
5684 | 27.6k | poDS->m_bNeatLineDirty = false; |
5685 | 27.6k | poDS->m_bInfoDirty = false; |
5686 | 27.6k | poDS->m_bXMPDirty = false; |
5687 | | |
5688 | 27.6k | return (poDS); |
5689 | 31.9k | } |
5690 | | |
5691 | | /************************************************************************/ |
5692 | | /* ParseLGIDictObject() */ |
5693 | | /************************************************************************/ |
5694 | | |
5695 | | int PDFDataset::ParseLGIDictObject(GDALPDFObject *poLGIDict) |
5696 | 529 | { |
5697 | 529 | bool bOK = false; |
5698 | 529 | if (poLGIDict->GetType() == PDFObjectType_Array) |
5699 | 33 | { |
5700 | 33 | GDALPDFArray *poArray = poLGIDict->GetArray(); |
5701 | 33 | int nArrayLength = poArray->GetLength(); |
5702 | 33 | int iMax = -1; |
5703 | 33 | GDALPDFObject *poArrayElt = nullptr; |
5704 | 33 | for (int i = 0; i < nArrayLength; i++) |
5705 | 33 | { |
5706 | 33 | if ((poArrayElt = poArray->Get(i)) == nullptr || |
5707 | 33 | poArrayElt->GetType() != PDFObjectType_Dictionary) |
5708 | 33 | { |
5709 | 33 | CPLError(CE_Failure, CPLE_AppDefined, |
5710 | 33 | "LGIDict[%d] is not a dictionary", i); |
5711 | 33 | return FALSE; |
5712 | 33 | } |
5713 | | |
5714 | 0 | int bIsBestCandidate = FALSE; |
5715 | 0 | if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), |
5716 | 0 | &bIsBestCandidate)) |
5717 | 0 | { |
5718 | 0 | if (bIsBestCandidate || iMax < 0) |
5719 | 0 | iMax = i; |
5720 | 0 | } |
5721 | 0 | } |
5722 | | |
5723 | 0 | if (iMax < 0) |
5724 | 0 | return FALSE; |
5725 | | |
5726 | 0 | poArrayElt = poArray->Get(iMax); |
5727 | 0 | bOK = CPL_TO_BOOL( |
5728 | 0 | ParseLGIDictDictSecondPass(poArrayElt->GetDictionary())); |
5729 | 0 | } |
5730 | 496 | else if (poLGIDict->GetType() == PDFObjectType_Dictionary) |
5731 | 459 | { |
5732 | 459 | bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) && |
5733 | 333 | ParseLGIDictDictSecondPass(poLGIDict->GetDictionary()); |
5734 | 459 | } |
5735 | 37 | else |
5736 | 37 | { |
5737 | 37 | CPLError(CE_Failure, CPLE_AppDefined, "LGIDict is of type %s", |
5738 | 37 | poLGIDict->GetTypeName()); |
5739 | 37 | } |
5740 | | |
5741 | 496 | return bOK; |
5742 | 529 | } |
5743 | | |
5744 | | /************************************************************************/ |
5745 | | /* Get() */ |
5746 | | /************************************************************************/ |
5747 | | |
5748 | | static double Get(GDALPDFObject *poObj, int nIndice) |
5749 | 1.22M | { |
5750 | 1.22M | if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0) |
5751 | 612k | { |
5752 | 612k | poObj = poObj->GetArray()->Get(nIndice); |
5753 | 612k | if (poObj == nullptr) |
5754 | 359 | return 0; |
5755 | 612k | return Get(poObj); |
5756 | 612k | } |
5757 | 616k | else if (poObj->GetType() == PDFObjectType_Int) |
5758 | 198k | return poObj->GetInt(); |
5759 | 418k | else if (poObj->GetType() == PDFObjectType_Real) |
5760 | 107k | return poObj->GetReal(); |
5761 | 310k | else if (poObj->GetType() == PDFObjectType_String) |
5762 | 8.21k | { |
5763 | 8.21k | const char *pszStr = poObj->GetString().c_str(); |
5764 | 8.21k | size_t nLen = strlen(pszStr); |
5765 | 8.21k | if (nLen == 0) |
5766 | 4.94k | return 0; |
5767 | | /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" |
5768 | | */ |
5769 | 3.27k | char chLast = pszStr[nLen - 1]; |
5770 | 3.27k | if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S') |
5771 | 338 | { |
5772 | 338 | double dfDeg = CPLAtof(pszStr); |
5773 | 338 | double dfMin = 0.0; |
5774 | 338 | double dfSec = 0.0; |
5775 | 338 | const char *pszNext = strchr(pszStr, ' '); |
5776 | 338 | if (pszNext) |
5777 | 68 | pszNext++; |
5778 | 338 | if (pszNext) |
5779 | 68 | dfMin = CPLAtof(pszNext); |
5780 | 338 | if (pszNext) |
5781 | 68 | pszNext = strchr(pszNext, ' '); |
5782 | 338 | if (pszNext) |
5783 | 68 | pszNext++; |
5784 | 338 | if (pszNext) |
5785 | 68 | dfSec = CPLAtof(pszNext); |
5786 | 338 | double dfVal = dfDeg + dfMin / 60 + dfSec / 3600; |
5787 | 338 | if (chLast == 'W' || chLast == 'S') |
5788 | 0 | return -dfVal; |
5789 | 338 | else |
5790 | 338 | return dfVal; |
5791 | 338 | } |
5792 | 2.93k | return CPLAtof(pszStr); |
5793 | 3.27k | } |
5794 | 302k | else |
5795 | 302k | { |
5796 | 302k | CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s", |
5797 | 302k | poObj->GetTypeName()); |
5798 | 302k | return 0; |
5799 | 302k | } |
5800 | 1.22M | } |
5801 | | |
5802 | | /************************************************************************/ |
5803 | | /* Get() */ |
5804 | | /************************************************************************/ |
5805 | | |
5806 | | static double Get(GDALPDFDictionary *poDict, const char *pszName) |
5807 | 0 | { |
5808 | 0 | GDALPDFObject *poObj = poDict->Get(pszName); |
5809 | 0 | if (poObj != nullptr) |
5810 | 0 | return Get(poObj); |
5811 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find parameter %s", pszName); |
5812 | 0 | return 0; |
5813 | 0 | } |
5814 | | |
5815 | | /************************************************************************/ |
5816 | | /* ParseLGIDictDictFirstPass() */ |
5817 | | /************************************************************************/ |
5818 | | |
5819 | | int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary *poLGIDict, |
5820 | | int *pbIsBestCandidate) |
5821 | 459 | { |
5822 | 459 | if (pbIsBestCandidate) |
5823 | 0 | *pbIsBestCandidate = FALSE; |
5824 | | |
5825 | 459 | if (poLGIDict == nullptr) |
5826 | 0 | return FALSE; |
5827 | | |
5828 | | /* -------------------------------------------------------------------- */ |
5829 | | /* Extract Type attribute */ |
5830 | | /* -------------------------------------------------------------------- */ |
5831 | 459 | GDALPDFObject *poType = poLGIDict->Get("Type"); |
5832 | 459 | if (poType == nullptr) |
5833 | 42 | { |
5834 | 42 | CPLError(CE_Failure, CPLE_AppDefined, |
5835 | 42 | "Cannot find Type of LGIDict object"); |
5836 | 42 | return FALSE; |
5837 | 42 | } |
5838 | | |
5839 | 417 | if (poType->GetType() != PDFObjectType_Name) |
5840 | 1 | { |
5841 | 1 | CPLError(CE_Failure, CPLE_AppDefined, |
5842 | 1 | "Invalid type for Type of LGIDict object"); |
5843 | 1 | return FALSE; |
5844 | 1 | } |
5845 | | |
5846 | 416 | if (strcmp(poType->GetName().c_str(), "LGIDict") != 0) |
5847 | 79 | { |
5848 | 79 | CPLError(CE_Failure, CPLE_AppDefined, |
5849 | 79 | "Invalid value for Type of LGIDict object : %s", |
5850 | 79 | poType->GetName().c_str()); |
5851 | 79 | return FALSE; |
5852 | 79 | } |
5853 | | |
5854 | | /* -------------------------------------------------------------------- */ |
5855 | | /* Extract Version attribute */ |
5856 | | /* -------------------------------------------------------------------- */ |
5857 | 337 | GDALPDFObject *poVersion = poLGIDict->Get("Version"); |
5858 | 337 | if (poVersion == nullptr) |
5859 | 0 | { |
5860 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5861 | 0 | "Cannot find Version of LGIDict object"); |
5862 | 0 | return FALSE; |
5863 | 0 | } |
5864 | | |
5865 | 337 | if (poVersion->GetType() == PDFObjectType_String) |
5866 | 293 | { |
5867 | | /* OGC best practice is 2.1 */ |
5868 | 293 | CPLDebug("PDF", "LGIDict Version : %s", poVersion->GetString().c_str()); |
5869 | 293 | } |
5870 | 44 | else if (poVersion->GetType() == PDFObjectType_Int) |
5871 | 1 | { |
5872 | | /* Old TerraGo is 2 */ |
5873 | 1 | CPLDebug("PDF", "LGIDict Version : %d", poVersion->GetInt()); |
5874 | 1 | } |
5875 | | |
5876 | | /* USGS PDF maps have several LGIDict. Keep the one whose description */ |
5877 | | /* is "Map Layers" by default */ |
5878 | 337 | const char *pszNeatlineToSelect = |
5879 | 337 | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
5880 | | |
5881 | | /* -------------------------------------------------------------------- */ |
5882 | | /* Extract Neatline attribute */ |
5883 | | /* -------------------------------------------------------------------- */ |
5884 | 337 | GDALPDFObject *poNeatline = poLGIDict->Get("Neatline"); |
5885 | 337 | if (poNeatline != nullptr && poNeatline->GetType() == PDFObjectType_Array) |
5886 | 329 | { |
5887 | 329 | int nLength = poNeatline->GetArray()->GetLength(); |
5888 | 329 | if ((nLength % 2) != 0 || nLength < 4) |
5889 | 4 | { |
5890 | 4 | CPLError(CE_Failure, CPLE_AppDefined, |
5891 | 4 | "Invalid length for Neatline"); |
5892 | 4 | return FALSE; |
5893 | 4 | } |
5894 | | |
5895 | 325 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
5896 | 325 | bool bIsAskedNeatline = false; |
5897 | 325 | if (poDescription != nullptr && |
5898 | 29 | poDescription->GetType() == PDFObjectType_String) |
5899 | 29 | { |
5900 | 29 | CPLDebug("PDF", "Description = %s", |
5901 | 29 | poDescription->GetString().c_str()); |
5902 | | |
5903 | 29 | if (EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect)) |
5904 | 0 | { |
5905 | 0 | m_dfMaxArea = 1e300; |
5906 | 0 | bIsAskedNeatline = true; |
5907 | 0 | } |
5908 | 29 | } |
5909 | | |
5910 | 325 | if (!bIsAskedNeatline) |
5911 | 325 | { |
5912 | 325 | double dfMinX = 0.0; |
5913 | 325 | double dfMinY = 0.0; |
5914 | 325 | double dfMaxX = 0.0; |
5915 | 325 | double dfMaxY = 0.0; |
5916 | 127k | for (int i = 0; i < nLength; i += 2) |
5917 | 126k | { |
5918 | 126k | double dfX = Get(poNeatline, i); |
5919 | 126k | double dfY = Get(poNeatline, i + 1); |
5920 | 126k | if (i == 0 || dfX < dfMinX) |
5921 | 785 | dfMinX = dfX; |
5922 | 126k | if (i == 0 || dfY < dfMinY) |
5923 | 1.39k | dfMinY = dfY; |
5924 | 126k | if (i == 0 || dfX > dfMaxX) |
5925 | 1.49k | dfMaxX = dfX; |
5926 | 126k | if (i == 0 || dfY > dfMaxY) |
5927 | 993 | dfMaxY = dfY; |
5928 | 126k | } |
5929 | 325 | double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY); |
5930 | 325 | if (dfArea < m_dfMaxArea) |
5931 | 0 | { |
5932 | 0 | CPLDebug("PDF", "Not the largest neatline. Skipping it"); |
5933 | 0 | return TRUE; |
5934 | 0 | } |
5935 | | |
5936 | 325 | CPLDebug("PDF", "This is the largest neatline for now"); |
5937 | 325 | m_dfMaxArea = dfArea; |
5938 | 325 | } |
5939 | 0 | else |
5940 | 0 | CPLDebug("PDF", "The \"%s\" registration will be selected", |
5941 | 0 | pszNeatlineToSelect); |
5942 | | |
5943 | 325 | if (pbIsBestCandidate) |
5944 | 0 | *pbIsBestCandidate = TRUE; |
5945 | | |
5946 | 325 | delete m_poNeatLine; |
5947 | 325 | m_poNeatLine = new OGRPolygon(); |
5948 | 325 | OGRLinearRing *poRing = new OGRLinearRing(); |
5949 | 325 | if (nLength == 4) |
5950 | 3 | { |
5951 | | /* 2 points only ? They are the bounding box */ |
5952 | 3 | double dfX1 = Get(poNeatline, 0); |
5953 | 3 | double dfY1 = Get(poNeatline, 1); |
5954 | 3 | double dfX2 = Get(poNeatline, 2); |
5955 | 3 | double dfY2 = Get(poNeatline, 3); |
5956 | 3 | poRing->addPoint(dfX1, dfY1); |
5957 | 3 | poRing->addPoint(dfX2, dfY1); |
5958 | 3 | poRing->addPoint(dfX2, dfY2); |
5959 | 3 | poRing->addPoint(dfX1, dfY2); |
5960 | 3 | } |
5961 | 322 | else |
5962 | 322 | { |
5963 | 127k | for (int i = 0; i < nLength; i += 2) |
5964 | 126k | { |
5965 | 126k | double dfX = Get(poNeatline, i); |
5966 | 126k | double dfY = Get(poNeatline, i + 1); |
5967 | 126k | poRing->addPoint(dfX, dfY); |
5968 | 126k | } |
5969 | 322 | } |
5970 | 325 | poRing->closeRings(); |
5971 | 325 | m_poNeatLine->addRingDirectly(poRing); |
5972 | 325 | } |
5973 | | |
5974 | 333 | return TRUE; |
5975 | 337 | } |
5976 | | |
5977 | | /************************************************************************/ |
5978 | | /* ParseLGIDictDictSecondPass() */ |
5979 | | /************************************************************************/ |
5980 | | |
5981 | | int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary *poLGIDict) |
5982 | 333 | { |
5983 | 333 | int i; |
5984 | | |
5985 | | /* -------------------------------------------------------------------- */ |
5986 | | /* Extract Description attribute */ |
5987 | | /* -------------------------------------------------------------------- */ |
5988 | 333 | GDALPDFObject *poDescription = poLGIDict->Get("Description"); |
5989 | 333 | if (poDescription != nullptr && |
5990 | 33 | poDescription->GetType() == PDFObjectType_String) |
5991 | 33 | { |
5992 | 33 | CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str()); |
5993 | 33 | } |
5994 | | |
5995 | | /* -------------------------------------------------------------------- */ |
5996 | | /* Extract CTM attribute */ |
5997 | | /* -------------------------------------------------------------------- */ |
5998 | 333 | GDALPDFObject *poCTM = poLGIDict->Get("CTM"); |
5999 | 333 | m_bHasCTM = false; |
6000 | 333 | if (poCTM != nullptr && poCTM->GetType() == PDFObjectType_Array && |
6001 | 299 | CPLTestBool(CPLGetConfigOption("PDF_USE_CTM", "YES"))) |
6002 | 299 | { |
6003 | 299 | int nLength = poCTM->GetArray()->GetLength(); |
6004 | 299 | if (nLength != 6) |
6005 | 45 | { |
6006 | 45 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for CTM"); |
6007 | 45 | return FALSE; |
6008 | 45 | } |
6009 | | |
6010 | 254 | m_bHasCTM = true; |
6011 | 1.77k | for (i = 0; i < nLength; i++) |
6012 | 1.52k | { |
6013 | 1.52k | m_adfCTM[i] = Get(poCTM, i); |
6014 | | /* Nullify rotation terms that are significantly smaller than */ |
6015 | | /* scaling terms. */ |
6016 | 1.52k | if ((i == 1 || i == 2) && |
6017 | 508 | fabs(m_adfCTM[i]) < fabs(m_adfCTM[0]) * 1e-10) |
6018 | 123 | m_adfCTM[i] = 0; |
6019 | 1.52k | CPLDebug("PDF", "CTM[%d] = %.16g", i, m_adfCTM[i]); |
6020 | 1.52k | } |
6021 | 254 | } |
6022 | | |
6023 | | /* -------------------------------------------------------------------- */ |
6024 | | /* Extract Registration attribute */ |
6025 | | /* -------------------------------------------------------------------- */ |
6026 | 288 | GDALPDFObject *poRegistration = poLGIDict->Get("Registration"); |
6027 | 288 | if (poRegistration != nullptr && |
6028 | 21 | poRegistration->GetType() == PDFObjectType_Array) |
6029 | 21 | { |
6030 | 21 | GDALPDFArray *poRegistrationArray = poRegistration->GetArray(); |
6031 | 21 | int nLength = poRegistrationArray->GetLength(); |
6032 | 21 | if (nLength > 4 || (!m_bHasCTM && nLength >= 2) || |
6033 | 0 | CPLTestBool(CPLGetConfigOption("PDF_REPORT_GCPS", "NO"))) |
6034 | 21 | { |
6035 | 21 | m_nGCPCount = 0; |
6036 | 21 | m_pasGCPList = |
6037 | 21 | static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nLength)); |
6038 | | |
6039 | 106 | for (i = 0; i < nLength; i++) |
6040 | 85 | { |
6041 | 85 | GDALPDFObject *poGCP = poRegistrationArray->Get(i); |
6042 | 85 | if (poGCP != nullptr && |
6043 | 85 | poGCP->GetType() == PDFObjectType_Array && |
6044 | 82 | poGCP->GetArray()->GetLength() == 4) |
6045 | 82 | { |
6046 | 82 | double dfUserX = Get(poGCP, 0); |
6047 | 82 | double dfUserY = Get(poGCP, 1); |
6048 | 82 | double dfX = Get(poGCP, 2); |
6049 | 82 | double dfY = Get(poGCP, 3); |
6050 | 82 | CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX); |
6051 | 82 | CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY); |
6052 | 82 | CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX); |
6053 | 82 | CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY); |
6054 | | |
6055 | 82 | char szID[32]; |
6056 | 82 | snprintf(szID, sizeof(szID), "%d", m_nGCPCount + 1); |
6057 | 82 | m_pasGCPList[m_nGCPCount].pszId = CPLStrdup(szID); |
6058 | 82 | m_pasGCPList[m_nGCPCount].pszInfo = CPLStrdup(""); |
6059 | 82 | m_pasGCPList[m_nGCPCount].dfGCPPixel = dfUserX; |
6060 | 82 | m_pasGCPList[m_nGCPCount].dfGCPLine = dfUserY; |
6061 | 82 | m_pasGCPList[m_nGCPCount].dfGCPX = dfX; |
6062 | 82 | m_pasGCPList[m_nGCPCount].dfGCPY = dfY; |
6063 | 82 | m_nGCPCount++; |
6064 | 82 | } |
6065 | 85 | } |
6066 | | |
6067 | 21 | if (m_nGCPCount == 0) |
6068 | 0 | { |
6069 | 0 | CPLFree(m_pasGCPList); |
6070 | 0 | m_pasGCPList = nullptr; |
6071 | 0 | } |
6072 | 21 | } |
6073 | 21 | } |
6074 | | |
6075 | 288 | if (!m_bHasCTM && m_nGCPCount == 0) |
6076 | 13 | { |
6077 | 13 | CPLDebug("PDF", "Neither CTM nor Registration found"); |
6078 | 13 | return FALSE; |
6079 | 13 | } |
6080 | | |
6081 | | /* -------------------------------------------------------------------- */ |
6082 | | /* Extract Projection attribute */ |
6083 | | /* -------------------------------------------------------------------- */ |
6084 | 275 | GDALPDFObject *poProjection = poLGIDict->Get("Projection"); |
6085 | 275 | if (poProjection == nullptr || |
6086 | 258 | poProjection->GetType() != PDFObjectType_Dictionary) |
6087 | 27 | { |
6088 | 27 | CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection"); |
6089 | 27 | return FALSE; |
6090 | 27 | } |
6091 | | |
6092 | 248 | return ParseProjDict(poProjection->GetDictionary()); |
6093 | 275 | } |
6094 | | |
6095 | | /************************************************************************/ |
6096 | | /* ParseProjDict() */ |
6097 | | /************************************************************************/ |
6098 | | |
6099 | | int PDFDataset::ParseProjDict(GDALPDFDictionary *poProjDict) |
6100 | 248 | { |
6101 | 248 | if (poProjDict == nullptr) |
6102 | 0 | return FALSE; |
6103 | 248 | OGRSpatialReference oSRS; |
6104 | 248 | oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
6105 | | |
6106 | | /* -------------------------------------------------------------------- */ |
6107 | | /* Extract WKT attribute (GDAL extension) */ |
6108 | | /* -------------------------------------------------------------------- */ |
6109 | 248 | GDALPDFObject *poWKT = poProjDict->Get("WKT"); |
6110 | 248 | if (poWKT != nullptr && poWKT->GetType() == PDFObjectType_String && |
6111 | 19 | CPLTestBool(CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE"))) |
6112 | 19 | { |
6113 | 19 | CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it"); |
6114 | 19 | const char *pszWKTRead = poWKT->GetString().c_str(); |
6115 | 19 | if (pszWKTRead[0] != 0) |
6116 | 19 | m_oSRS.importFromWkt(pszWKTRead); |
6117 | 19 | return TRUE; |
6118 | 19 | } |
6119 | | |
6120 | | /* -------------------------------------------------------------------- */ |
6121 | | /* Extract Type attribute */ |
6122 | | /* -------------------------------------------------------------------- */ |
6123 | 229 | GDALPDFObject *poType = poProjDict->Get("Type"); |
6124 | 229 | if (poType == nullptr) |
6125 | 13 | { |
6126 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
6127 | 13 | "Cannot find Type of Projection object"); |
6128 | 13 | return FALSE; |
6129 | 13 | } |
6130 | | |
6131 | 216 | if (poType->GetType() != PDFObjectType_Name) |
6132 | 3 | { |
6133 | 3 | CPLError(CE_Failure, CPLE_AppDefined, |
6134 | 3 | "Invalid type for Type of Projection object"); |
6135 | 3 | return FALSE; |
6136 | 3 | } |
6137 | | |
6138 | 213 | if (strcmp(poType->GetName().c_str(), "Projection") != 0) |
6139 | 18 | { |
6140 | 18 | CPLError(CE_Failure, CPLE_AppDefined, |
6141 | 18 | "Invalid value for Type of Projection object : %s", |
6142 | 18 | poType->GetName().c_str()); |
6143 | 18 | return FALSE; |
6144 | 18 | } |
6145 | | |
6146 | | /* -------------------------------------------------------------------- */ |
6147 | | /* Extract Datum attribute */ |
6148 | | /* -------------------------------------------------------------------- */ |
6149 | 195 | int bIsWGS84 = FALSE; |
6150 | 195 | int bIsNAD83 = FALSE; |
6151 | | /* int bIsNAD27 = FALSE; */ |
6152 | | |
6153 | 195 | GDALPDFObject *poDatum = poProjDict->Get("Datum"); |
6154 | 195 | if (poDatum != nullptr) |
6155 | 43 | { |
6156 | 43 | if (poDatum->GetType() == PDFObjectType_String) |
6157 | 43 | { |
6158 | | /* Using Annex A of |
6159 | | * http://portal.opengeospatial.org/files/?artifact_id=40537 */ |
6160 | 43 | const char *pszDatum = poDatum->GetString().c_str(); |
6161 | 43 | CPLDebug("PDF", "Datum = %s", pszDatum); |
6162 | 43 | if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE")) |
6163 | 19 | { |
6164 | 19 | bIsWGS84 = TRUE; |
6165 | 19 | oSRS.SetWellKnownGeogCS("WGS84"); |
6166 | 19 | } |
6167 | 24 | else if (EQUAL(pszDatum, "NAR") || STARTS_WITH_CI(pszDatum, "NAR-")) |
6168 | 0 | { |
6169 | 0 | bIsNAD83 = TRUE; |
6170 | 0 | oSRS.SetWellKnownGeogCS("NAD83"); |
6171 | 0 | } |
6172 | 24 | else if (EQUAL(pszDatum, "NAS") || STARTS_WITH_CI(pszDatum, "NAS-")) |
6173 | 0 | { |
6174 | | /* bIsNAD27 = TRUE; */ |
6175 | 0 | oSRS.SetWellKnownGeogCS("NAD27"); |
6176 | 0 | } |
6177 | 24 | else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */ |
6178 | 0 | { |
6179 | 0 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6180 | 0 | "unknown" /*const char * pszDatumName */, |
6181 | 0 | "International 1924", 6378388, 297); |
6182 | 0 | oSRS.SetTOWGS84(-333, -222, 114); |
6183 | 0 | } |
6184 | 24 | else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */ |
6185 | 0 | { |
6186 | 0 | oSRS.importFromEPSG(4131); |
6187 | 0 | } |
6188 | 24 | else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */ |
6189 | 0 | { |
6190 | 0 | oSRS.importFromEPSG(4283); |
6191 | 0 | } |
6192 | 24 | else if (STARTS_WITH_CI(pszDatum, "OHA-")) /* Old Hawaiian */ |
6193 | 0 | { |
6194 | 0 | oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */ |
6195 | 0 | if (!EQUAL(pszDatum, "OHA-M")) |
6196 | 0 | { |
6197 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6198 | 0 | "Using OHA-M (Old Hawaiian Mean) definition for " |
6199 | 0 | "%s. Potential issue with datum shift parameters", |
6200 | 0 | pszDatum); |
6201 | 0 | OGR_SRSNode *poNode = oSRS.GetRoot(); |
6202 | 0 | int iChild = poNode->FindChild("AUTHORITY"); |
6203 | 0 | if (iChild != -1) |
6204 | 0 | poNode->DestroyChild(iChild); |
6205 | 0 | iChild = poNode->FindChild("DATUM"); |
6206 | 0 | if (iChild != -1) |
6207 | 0 | { |
6208 | 0 | poNode = poNode->GetChild(iChild); |
6209 | 0 | iChild = poNode->FindChild("AUTHORITY"); |
6210 | 0 | if (iChild != -1) |
6211 | 0 | poNode->DestroyChild(iChild); |
6212 | 0 | } |
6213 | 0 | } |
6214 | 0 | } |
6215 | 24 | else |
6216 | 24 | { |
6217 | 24 | CPLError(CE_Warning, CPLE_AppDefined, |
6218 | 24 | "Unhandled (yet) value for Datum : %s. Defaulting to " |
6219 | 24 | "WGS84...", |
6220 | 24 | pszDatum); |
6221 | 24 | oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/, |
6222 | 24 | "unknown" /*const char * pszDatumName */, |
6223 | 24 | "unknown", 6378137, 298.257223563); |
6224 | 24 | } |
6225 | 43 | } |
6226 | 0 | else if (poDatum->GetType() == PDFObjectType_Dictionary) |
6227 | 0 | { |
6228 | 0 | GDALPDFDictionary *poDatumDict = poDatum->GetDictionary(); |
6229 | |
|
6230 | 0 | GDALPDFObject *poDatumDescription = poDatumDict->Get("Description"); |
6231 | 0 | const char *pszDatumDescription = "unknown"; |
6232 | 0 | if (poDatumDescription != nullptr && |
6233 | 0 | poDatumDescription->GetType() == PDFObjectType_String) |
6234 | 0 | pszDatumDescription = poDatumDescription->GetString().c_str(); |
6235 | 0 | CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription); |
6236 | |
|
6237 | 0 | GDALPDFObject *poEllipsoid = poDatumDict->Get("Ellipsoid"); |
6238 | 0 | if (poEllipsoid == nullptr || |
6239 | 0 | !(poEllipsoid->GetType() == PDFObjectType_String || |
6240 | 0 | poEllipsoid->GetType() == PDFObjectType_Dictionary)) |
6241 | 0 | { |
6242 | 0 | CPLError( |
6243 | 0 | CE_Warning, CPLE_AppDefined, |
6244 | 0 | "Cannot find Ellipsoid in Datum. Defaulting to WGS84..."); |
6245 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "unknown", |
6246 | 0 | 6378137, 298.257223563); |
6247 | 0 | } |
6248 | 0 | else if (poEllipsoid->GetType() == PDFObjectType_String) |
6249 | 0 | { |
6250 | 0 | const char *pszEllipsoid = poEllipsoid->GetString().c_str(); |
6251 | 0 | CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid); |
6252 | 0 | if (EQUAL(pszEllipsoid, "WE")) |
6253 | 0 | { |
6254 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, "WGS 84", |
6255 | 0 | 6378137, 298.257223563); |
6256 | 0 | } |
6257 | 0 | else |
6258 | 0 | { |
6259 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
6260 | 0 | "Unhandled (yet) value for Ellipsoid : %s. " |
6261 | 0 | "Defaulting to WGS84...", |
6262 | 0 | pszEllipsoid); |
6263 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, pszEllipsoid, |
6264 | 0 | 6378137, 298.257223563); |
6265 | 0 | } |
6266 | 0 | } |
6267 | 0 | else // if (poEllipsoid->GetType() == PDFObjectType_Dictionary) |
6268 | 0 | { |
6269 | 0 | GDALPDFDictionary *poEllipsoidDict = |
6270 | 0 | poEllipsoid->GetDictionary(); |
6271 | |
|
6272 | 0 | GDALPDFObject *poEllipsoidDescription = |
6273 | 0 | poEllipsoidDict->Get("Description"); |
6274 | 0 | const char *pszEllipsoidDescription = "unknown"; |
6275 | 0 | if (poEllipsoidDescription != nullptr && |
6276 | 0 | poEllipsoidDescription->GetType() == PDFObjectType_String) |
6277 | 0 | pszEllipsoidDescription = |
6278 | 0 | poEllipsoidDescription->GetString().c_str(); |
6279 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", |
6280 | 0 | pszEllipsoidDescription); |
6281 | |
|
6282 | 0 | double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis"); |
6283 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", |
6284 | 0 | dfSemiMajor); |
6285 | 0 | double dfInvFlattening = -1.0; |
6286 | |
|
6287 | 0 | if (poEllipsoidDict->Get("InvFlattening")) |
6288 | 0 | { |
6289 | 0 | dfInvFlattening = Get(poEllipsoidDict, "InvFlattening"); |
6290 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", |
6291 | 0 | dfInvFlattening); |
6292 | 0 | } |
6293 | 0 | else if (poEllipsoidDict->Get("SemiMinorAxis")) |
6294 | 0 | { |
6295 | 0 | double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis"); |
6296 | 0 | CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", |
6297 | 0 | dfSemiMinor); |
6298 | 0 | dfInvFlattening = |
6299 | 0 | OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); |
6300 | 0 | } |
6301 | |
|
6302 | 0 | if (dfSemiMajor != 0.0 && dfInvFlattening != -1.0) |
6303 | 0 | { |
6304 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6305 | 0 | pszEllipsoidDescription, dfSemiMajor, |
6306 | 0 | dfInvFlattening); |
6307 | 0 | } |
6308 | 0 | else |
6309 | 0 | { |
6310 | 0 | CPLError( |
6311 | 0 | CE_Warning, CPLE_AppDefined, |
6312 | 0 | "Invalid Ellipsoid object. Defaulting to WGS84..."); |
6313 | 0 | oSRS.SetGeogCS("unknown", pszDatumDescription, |
6314 | 0 | pszEllipsoidDescription, 6378137, |
6315 | 0 | 298.257223563); |
6316 | 0 | } |
6317 | 0 | } |
6318 | |
|
6319 | 0 | GDALPDFObject *poTOWGS84 = poDatumDict->Get("ToWGS84"); |
6320 | 0 | if (poTOWGS84 != nullptr && |
6321 | 0 | poTOWGS84->GetType() == PDFObjectType_Dictionary) |
6322 | 0 | { |
6323 | 0 | GDALPDFDictionary *poTOWGS84Dict = poTOWGS84->GetDictionary(); |
6324 | 0 | double dx = Get(poTOWGS84Dict, "dx"); |
6325 | 0 | double dy = Get(poTOWGS84Dict, "dy"); |
6326 | 0 | double dz = Get(poTOWGS84Dict, "dz"); |
6327 | 0 | if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") && |
6328 | 0 | poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf")) |
6329 | 0 | { |
6330 | 0 | double rx = Get(poTOWGS84Dict, "rx"); |
6331 | 0 | double ry = Get(poTOWGS84Dict, "ry"); |
6332 | 0 | double rz = Get(poTOWGS84Dict, "rz"); |
6333 | 0 | double sf = Get(poTOWGS84Dict, "sf"); |
6334 | 0 | oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf); |
6335 | 0 | } |
6336 | 0 | else |
6337 | 0 | { |
6338 | 0 | oSRS.SetTOWGS84(dx, dy, dz); |
6339 | 0 | } |
6340 | 0 | } |
6341 | 0 | } |
6342 | 43 | } |
6343 | | |
6344 | | /* -------------------------------------------------------------------- */ |
6345 | | /* Extract Hemisphere attribute */ |
6346 | | /* -------------------------------------------------------------------- */ |
6347 | 195 | CPLString osHemisphere; |
6348 | 195 | GDALPDFObject *poHemisphere = poProjDict->Get("Hemisphere"); |
6349 | 195 | if (poHemisphere != nullptr && |
6350 | 0 | poHemisphere->GetType() == PDFObjectType_String) |
6351 | 0 | { |
6352 | 0 | osHemisphere = poHemisphere->GetString(); |
6353 | 0 | } |
6354 | | |
6355 | | /* -------------------------------------------------------------------- */ |
6356 | | /* Extract ProjectionType attribute */ |
6357 | | /* -------------------------------------------------------------------- */ |
6358 | 195 | GDALPDFObject *poProjectionType = poProjDict->Get("ProjectionType"); |
6359 | 195 | if (poProjectionType == nullptr || |
6360 | 191 | poProjectionType->GetType() != PDFObjectType_String) |
6361 | 4 | { |
6362 | 4 | CPLError(CE_Failure, CPLE_AppDefined, |
6363 | 4 | "Cannot find ProjectionType of Projection object"); |
6364 | 4 | return FALSE; |
6365 | 4 | } |
6366 | 191 | CPLString osProjectionType(poProjectionType->GetString()); |
6367 | 191 | CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str()); |
6368 | | |
6369 | | /* Unhandled: NONE, GEODETIC */ |
6370 | | |
6371 | 191 | if (EQUAL(osProjectionType, "GEOGRAPHIC")) |
6372 | 178 | { |
6373 | | /* Nothing to do */ |
6374 | 178 | } |
6375 | | |
6376 | | /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */ |
6377 | | |
6378 | 13 | else if (EQUAL(osProjectionType, "UT")) /* UTM */ |
6379 | 0 | { |
6380 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6381 | 0 | if (dfZone >= 1 && dfZone <= 60) |
6382 | 0 | { |
6383 | 0 | int nZone = static_cast<int>(dfZone); |
6384 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6385 | 0 | if (bIsWGS84) |
6386 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6387 | 0 | else |
6388 | 0 | oSRS.SetUTM(nZone, bNorth); |
6389 | 0 | } |
6390 | 0 | } |
6391 | | |
6392 | 13 | else if (EQUAL(osProjectionType, |
6393 | 13 | "UP")) /* Universal Polar Stereographic (UPS) */ |
6394 | 0 | { |
6395 | 0 | int bNorth = EQUAL(osHemisphere, "N"); |
6396 | 0 | if (bIsWGS84) |
6397 | 0 | oSRS.importFromEPSG((bNorth) ? 32661 : 32761); |
6398 | 0 | else |
6399 | 0 | oSRS.SetPS((bNorth) ? 90 : -90, 0, 0.994, 200000, 200000); |
6400 | 0 | } |
6401 | | |
6402 | 13 | else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */ |
6403 | 0 | { |
6404 | 0 | const double dfZone = Get(poProjDict, "Zone"); |
6405 | 0 | if (dfZone >= 0 && dfZone <= INT_MAX) |
6406 | 0 | { |
6407 | 0 | int nZone = static_cast<int>(dfZone); |
6408 | 0 | oSRS.SetStatePlane(nZone, bIsNAD83); |
6409 | 0 | } |
6410 | 0 | } |
6411 | | |
6412 | 13 | else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */ |
6413 | 0 | { |
6414 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6415 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6416 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6417 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6418 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6419 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6420 | 0 | oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, |
6421 | 0 | dfFalseEasting, dfFalseNorthing); |
6422 | 0 | } |
6423 | | |
6424 | 13 | else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */ |
6425 | 0 | { |
6426 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6427 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6428 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6429 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6430 | 0 | oSRS.SetAE(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6431 | 0 | } |
6432 | | |
6433 | 13 | else if (EQUAL(osProjectionType, "BF")) /* Bonne */ |
6434 | 0 | { |
6435 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6436 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6437 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6438 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6439 | 0 | oSRS.SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6440 | 0 | dfFalseNorthing); |
6441 | 0 | } |
6442 | | |
6443 | 13 | else if (EQUAL(osProjectionType, "CS")) /* Cassini */ |
6444 | 0 | { |
6445 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6446 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6447 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6448 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6449 | 0 | oSRS.SetCS(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6450 | 0 | } |
6451 | | |
6452 | 13 | else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */ |
6453 | 0 | { |
6454 | 0 | double dfStdP1 = Get(poProjDict, "OriginLatitude"); |
6455 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6456 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6457 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6458 | 0 | oSRS.SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting, |
6459 | 0 | dfFalseNorthing); |
6460 | 0 | } |
6461 | | |
6462 | 13 | else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */ |
6463 | 0 | { |
6464 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6465 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6466 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6467 | 0 | oSRS.SetEckertIV(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6468 | 0 | } |
6469 | | |
6470 | 13 | else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */ |
6471 | 0 | { |
6472 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6473 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6474 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6475 | 0 | oSRS.SetEckertVI(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6476 | 0 | } |
6477 | | |
6478 | 13 | else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */ |
6479 | 0 | { |
6480 | 0 | double dfCenterLat = Get(poProjDict, "StandardParallel"); |
6481 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6482 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6483 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6484 | 0 | oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting, |
6485 | 0 | dfFalseNorthing); |
6486 | 0 | } |
6487 | | |
6488 | 13 | else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */ |
6489 | 0 | { |
6490 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6491 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6492 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6493 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6494 | 0 | oSRS.SetGnomonic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6495 | 0 | dfFalseNorthing); |
6496 | 0 | } |
6497 | | |
6498 | 13 | else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */ |
6499 | 0 | { |
6500 | 0 | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6501 | 0 | double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); |
6502 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6503 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6504 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6505 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6506 | 0 | oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting, |
6507 | 0 | dfFalseNorthing); |
6508 | 0 | } |
6509 | | |
6510 | 13 | else if (EQUAL(osProjectionType, "MC")) /* Mercator */ |
6511 | 0 | { |
6512 | | #ifdef not_supported |
6513 | | if (poProjDict->Get("StandardParallelOne") == nullptr) |
6514 | | #endif |
6515 | 0 | { |
6516 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6517 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6518 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6519 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6520 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6521 | 0 | oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6522 | 0 | dfFalseNorthing); |
6523 | 0 | } |
6524 | | #ifdef not_supported |
6525 | | else |
6526 | | { |
6527 | | double dfStdP1 = Get(poProjDict, "StandardParallelOne"); |
6528 | | double dfCenterLat = poProjDict->Get("OriginLatitude") |
6529 | | ? Get(poProjDict, "OriginLatitude") |
6530 | | : 0; |
6531 | | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6532 | | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6533 | | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6534 | | oSRS.SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong, |
6535 | | dfFalseEasting, dfFalseNorthing); |
6536 | | } |
6537 | | #endif |
6538 | 0 | } |
6539 | | |
6540 | 13 | else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */ |
6541 | 0 | { |
6542 | 0 | double dfCenterLat = 0 /* ? */; |
6543 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6544 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6545 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6546 | 0 | oSRS.SetMC(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6547 | 0 | } |
6548 | | |
6549 | 13 | else if (EQUAL(osProjectionType, "MP")) /* Mollweide */ |
6550 | 0 | { |
6551 | 0 | double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); |
6552 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6553 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6554 | 0 | oSRS.SetMollweide(dfCentralMeridian, dfFalseEasting, dfFalseNorthing); |
6555 | 0 | } |
6556 | | |
6557 | | /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */ |
6558 | | |
6559 | 13 | else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */ |
6560 | 0 | { |
6561 | | /* No parameter specified in the PDF, so let's take the ones of |
6562 | | * EPSG:27200 */ |
6563 | 0 | double dfCenterLat = -41; |
6564 | 0 | double dfCenterLong = 173; |
6565 | 0 | double dfFalseEasting = 2510000; |
6566 | 0 | double dfFalseNorthing = 6023150; |
6567 | 0 | oSRS.SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting, |
6568 | 0 | dfFalseNorthing); |
6569 | 0 | } |
6570 | | |
6571 | 13 | else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */ |
6572 | 0 | { |
6573 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6574 | 0 | double dfLat1 = Get(poProjDict, "LatitudeOne"); |
6575 | 0 | double dfLong1 = Get(poProjDict, "LongitudeOne"); |
6576 | 0 | double dfLat2 = Get(poProjDict, "LatitudeTwo"); |
6577 | 0 | double dfLong2 = Get(poProjDict, "LongitudeTwo"); |
6578 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6579 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6580 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6581 | 0 | oSRS.SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2, dfScale, |
6582 | 0 | dfFalseEasting, dfFalseNorthing); |
6583 | 0 | } |
6584 | | |
6585 | 13 | else if (EQUAL(osProjectionType, "OD")) /* Orthographic */ |
6586 | 0 | { |
6587 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6588 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6589 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6590 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6591 | 0 | oSRS.SetOrthographic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6592 | 0 | dfFalseNorthing); |
6593 | 0 | } |
6594 | | |
6595 | 13 | else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */ |
6596 | 0 | { |
6597 | 0 | double dfCenterLat = Get(poProjDict, "LatitudeTrueScale"); |
6598 | 0 | double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole"); |
6599 | 0 | double dfScale = 1.0; |
6600 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6601 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6602 | 0 | oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6603 | 0 | dfFalseNorthing); |
6604 | 0 | } |
6605 | | |
6606 | 13 | else if (EQUAL(osProjectionType, "PH")) /* Polyconic */ |
6607 | 0 | { |
6608 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6609 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6610 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6611 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6612 | 0 | oSRS.SetPolyconic(dfCenterLat, dfCenterLong, dfFalseEasting, |
6613 | 0 | dfFalseNorthing); |
6614 | 0 | } |
6615 | | |
6616 | 13 | else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */ |
6617 | 0 | { |
6618 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6619 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6620 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6621 | 0 | oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6622 | 0 | } |
6623 | | |
6624 | 13 | else if (EQUAL(osProjectionType, "SD")) /* Stereographic */ |
6625 | 0 | { |
6626 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6627 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6628 | 0 | double dfScale = 1.0; |
6629 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6630 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6631 | 0 | oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale, |
6632 | 0 | dfFalseEasting, dfFalseNorthing); |
6633 | 0 | } |
6634 | | |
6635 | 13 | else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */ |
6636 | 0 | { |
6637 | 0 | double dfCenterLat = Get(poProjDict, "OriginLatitude"); |
6638 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6639 | 0 | double dfScale = Get(poProjDict, "ScaleFactor"); |
6640 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6641 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6642 | 0 | if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfCenterLong >= -180 && |
6643 | 0 | dfCenterLong <= 180 && dfFalseEasting == 500000 && |
6644 | 0 | (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0)) |
6645 | 0 | { |
6646 | 0 | const int nZone = |
6647 | 0 | static_cast<int>(floor((dfCenterLong + 180.0) / 6.0) + 1); |
6648 | 0 | int bNorth = dfFalseNorthing == 0; |
6649 | 0 | if (bIsWGS84) |
6650 | 0 | oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone); |
6651 | 0 | else if (bIsNAD83 && bNorth) |
6652 | 0 | oSRS.importFromEPSG(26900 + nZone); |
6653 | 0 | else |
6654 | 0 | oSRS.SetUTM(nZone, bNorth); |
6655 | 0 | } |
6656 | 0 | else |
6657 | 0 | { |
6658 | 0 | oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, |
6659 | 0 | dfFalseNorthing); |
6660 | 0 | } |
6661 | 0 | } |
6662 | | |
6663 | | /* Unhandled TX : Transverse Cylindrical Equal Area */ |
6664 | | |
6665 | 13 | else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */ |
6666 | 0 | { |
6667 | 0 | double dfCenterLong = Get(poProjDict, "CentralMeridian"); |
6668 | 0 | double dfFalseEasting = Get(poProjDict, "FalseEasting"); |
6669 | 0 | double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); |
6670 | 0 | oSRS.SetVDG(dfCenterLong, dfFalseEasting, dfFalseNorthing); |
6671 | 0 | } |
6672 | | |
6673 | 13 | else |
6674 | 13 | { |
6675 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
6676 | 13 | "Unhandled (yet) value for ProjectionType : %s", |
6677 | 13 | osProjectionType.c_str()); |
6678 | 13 | return FALSE; |
6679 | 13 | } |
6680 | | |
6681 | | /* -------------------------------------------------------------------- */ |
6682 | | /* Extract Units attribute */ |
6683 | | /* -------------------------------------------------------------------- */ |
6684 | 178 | CPLString osUnits; |
6685 | 178 | GDALPDFObject *poUnits = poProjDict->Get("Units"); |
6686 | 178 | if (poUnits != nullptr && poUnits->GetType() == PDFObjectType_String && |
6687 | 0 | !EQUAL(osProjectionType, "GEOGRAPHIC")) |
6688 | 0 | { |
6689 | 0 | osUnits = poUnits->GetString(); |
6690 | 0 | CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str()); |
6691 | | |
6692 | | // This is super weird. The false easting/northing of the SRS |
6693 | | // are expressed in the unit, but the geotransform is expressed in |
6694 | | // meters. Hence this hack to have an equivalent SRS definition, but |
6695 | | // with linear units converted in meters. |
6696 | 0 | if (EQUAL(osUnits, "M")) |
6697 | 0 | oSRS.SetLinearUnits("Meter", 1.0); |
6698 | 0 | else if (EQUAL(osUnits, "FT")) |
6699 | 0 | { |
6700 | 0 | oSRS.SetLinearUnits("foot", 0.3048); |
6701 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6702 | 0 | } |
6703 | 0 | else if (EQUAL(osUnits, "USSF")) |
6704 | 0 | { |
6705 | 0 | oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV)); |
6706 | 0 | oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0); |
6707 | 0 | } |
6708 | 0 | else |
6709 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unhandled unit: %s", |
6710 | 0 | osUnits.c_str()); |
6711 | 0 | } |
6712 | | |
6713 | | /* -------------------------------------------------------------------- */ |
6714 | | /* Export SpatialRef */ |
6715 | | /* -------------------------------------------------------------------- */ |
6716 | 178 | m_oSRS = std::move(oSRS); |
6717 | | |
6718 | 178 | return TRUE; |
6719 | 191 | } |
6720 | | |
6721 | | /************************************************************************/ |
6722 | | /* ParseVP() */ |
6723 | | /************************************************************************/ |
6724 | | |
6725 | | int PDFDataset::ParseVP(GDALPDFObject *poVP, double dfMediaBoxWidth, |
6726 | | double dfMediaBoxHeight) |
6727 | 5.75k | { |
6728 | 5.75k | int i; |
6729 | | |
6730 | 5.75k | if (poVP->GetType() != PDFObjectType_Array) |
6731 | 55 | return FALSE; |
6732 | | |
6733 | 5.69k | GDALPDFArray *poVPArray = poVP->GetArray(); |
6734 | | |
6735 | 5.69k | int nLength = poVPArray->GetLength(); |
6736 | 5.69k | CPLDebug("PDF", "VP length = %d", nLength); |
6737 | 5.69k | if (nLength < 1) |
6738 | 0 | return FALSE; |
6739 | | |
6740 | | /* -------------------------------------------------------------------- */ |
6741 | | /* Find the largest BBox */ |
6742 | | /* -------------------------------------------------------------------- */ |
6743 | 5.69k | const char *pszNeatlineToSelect = |
6744 | 5.69k | GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); |
6745 | | |
6746 | 5.69k | int iLargest = 0; |
6747 | 5.69k | int iRequestedVP = -1; |
6748 | 5.69k | double dfLargestArea = 0; |
6749 | | |
6750 | 12.1k | for (i = 0; i < nLength; i++) |
6751 | 7.43k | { |
6752 | 7.43k | GDALPDFObject *poVPElt = poVPArray->Get(i); |
6753 | 7.43k | if (poVPElt == nullptr || |
6754 | 7.14k | poVPElt->GetType() != PDFObjectType_Dictionary) |
6755 | 794 | { |
6756 | 794 | return FALSE; |
6757 | 794 | } |
6758 | | |
6759 | 6.63k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6760 | | |
6761 | 6.63k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6762 | 6.63k | if (poMeasure == nullptr || |
6763 | 5.00k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6764 | 1.71k | { |
6765 | 1.71k | continue; |
6766 | 1.71k | } |
6767 | | /* -------------------------------------------------------------------- |
6768 | | */ |
6769 | | /* Extract Subtype attribute */ |
6770 | | /* -------------------------------------------------------------------- |
6771 | | */ |
6772 | 4.91k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6773 | 4.91k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6774 | 4.91k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
6775 | 338 | { |
6776 | 338 | continue; |
6777 | 338 | } |
6778 | | |
6779 | 4.57k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
6780 | 4.57k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
6781 | 198 | { |
6782 | 198 | continue; |
6783 | 198 | } |
6784 | | |
6785 | 4.38k | GDALPDFObject *poName = poVPEltDict->Get("Name"); |
6786 | 4.38k | if (poName != nullptr && poName->GetType() == PDFObjectType_String) |
6787 | 3.73k | { |
6788 | 3.73k | CPLDebug("PDF", "Name = %s", poName->GetString().c_str()); |
6789 | 3.73k | if (EQUAL(poName->GetString().c_str(), pszNeatlineToSelect)) |
6790 | 0 | { |
6791 | 0 | iRequestedVP = i; |
6792 | 0 | } |
6793 | 3.73k | } |
6794 | | |
6795 | 4.38k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6796 | 4.38k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6797 | 119 | { |
6798 | 119 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6799 | 119 | return FALSE; |
6800 | 119 | } |
6801 | | |
6802 | 4.26k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6803 | 4.26k | if (nBboxLength != 4) |
6804 | 82 | { |
6805 | 82 | CPLError(CE_Failure, CPLE_AppDefined, |
6806 | 82 | "Invalid length for Bbox object"); |
6807 | 82 | return FALSE; |
6808 | 82 | } |
6809 | | |
6810 | 4.18k | double adfBBox[4]; |
6811 | 4.18k | adfBBox[0] = Get(poBBox, 0); |
6812 | 4.18k | adfBBox[1] = Get(poBBox, 1); |
6813 | 4.18k | adfBBox[2] = Get(poBBox, 2); |
6814 | 4.18k | adfBBox[3] = Get(poBBox, 3); |
6815 | 4.18k | double dfArea = |
6816 | 4.18k | fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]); |
6817 | 4.18k | if (dfArea > dfLargestArea) |
6818 | 3.57k | { |
6819 | 3.57k | iLargest = i; |
6820 | 3.57k | dfLargestArea = dfArea; |
6821 | 3.57k | } |
6822 | 4.18k | } |
6823 | | |
6824 | 4.70k | if (nLength > 1) |
6825 | 1.10k | { |
6826 | 1.10k | CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest); |
6827 | 1.10k | } |
6828 | | |
6829 | 4.70k | GDALPDFObject *poVPElt = nullptr; |
6830 | | |
6831 | 4.70k | if (iRequestedVP > -1) |
6832 | 0 | { |
6833 | 0 | CPLDebug("PDF", "Requested NEATLINE BBox in VP array is element %d", |
6834 | 0 | iRequestedVP); |
6835 | 0 | poVPElt = poVPArray->Get(iRequestedVP); |
6836 | 0 | } |
6837 | 4.70k | else |
6838 | 4.70k | { |
6839 | 4.70k | poVPElt = poVPArray->Get(iLargest); |
6840 | 4.70k | } |
6841 | | |
6842 | 4.70k | if (poVPElt == nullptr || poVPElt->GetType() != PDFObjectType_Dictionary) |
6843 | 0 | { |
6844 | 0 | return FALSE; |
6845 | 0 | } |
6846 | | |
6847 | 4.70k | GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary(); |
6848 | | |
6849 | 4.70k | GDALPDFObject *poBBox = poVPEltDict->Get("BBox"); |
6850 | 4.70k | if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array) |
6851 | 154 | { |
6852 | 154 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object"); |
6853 | 154 | return FALSE; |
6854 | 154 | } |
6855 | | |
6856 | 4.54k | int nBboxLength = poBBox->GetArray()->GetLength(); |
6857 | 4.54k | if (nBboxLength != 4) |
6858 | 269 | { |
6859 | 269 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for Bbox object"); |
6860 | 269 | return FALSE; |
6861 | 269 | } |
6862 | | |
6863 | 4.27k | double dfULX = Get(poBBox, 0); |
6864 | 4.27k | double dfULY = dfMediaBoxHeight - Get(poBBox, 1); |
6865 | 4.27k | double dfLRX = Get(poBBox, 2); |
6866 | 4.27k | double dfLRY = dfMediaBoxHeight - Get(poBBox, 3); |
6867 | | |
6868 | | /* -------------------------------------------------------------------- */ |
6869 | | /* Extract Measure attribute */ |
6870 | | /* -------------------------------------------------------------------- */ |
6871 | 4.27k | GDALPDFObject *poMeasure = poVPEltDict->Get("Measure"); |
6872 | 4.27k | if (poMeasure == nullptr || |
6873 | 3.85k | poMeasure->GetType() != PDFObjectType_Dictionary) |
6874 | 453 | { |
6875 | 453 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Measure object"); |
6876 | 453 | return FALSE; |
6877 | 453 | } |
6878 | | |
6879 | 3.82k | int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, dfULX, |
6880 | 3.82k | dfULY, dfLRX, dfLRY); |
6881 | | |
6882 | | /* -------------------------------------------------------------------- */ |
6883 | | /* Extract PointData attribute */ |
6884 | | /* -------------------------------------------------------------------- */ |
6885 | 3.82k | GDALPDFObject *poPointData = poVPEltDict->Get("PtData"); |
6886 | 3.82k | if (poPointData != nullptr && |
6887 | 0 | poPointData->GetType() == PDFObjectType_Dictionary) |
6888 | 0 | { |
6889 | 0 | CPLDebug("PDF", "Found PointData"); |
6890 | 0 | } |
6891 | | |
6892 | 3.82k | return bRet; |
6893 | 4.27k | } |
6894 | | |
6895 | | /************************************************************************/ |
6896 | | /* ParseMeasure() */ |
6897 | | /************************************************************************/ |
6898 | | |
6899 | | int PDFDataset::ParseMeasure(GDALPDFObject *poMeasure, double dfMediaBoxWidth, |
6900 | | double dfMediaBoxHeight, double dfULX, |
6901 | | double dfULY, double dfLRX, double dfLRY) |
6902 | 3.82k | { |
6903 | 3.82k | GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary(); |
6904 | | |
6905 | | /* -------------------------------------------------------------------- */ |
6906 | | /* Extract Subtype attribute */ |
6907 | | /* -------------------------------------------------------------------- */ |
6908 | 3.82k | GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype"); |
6909 | 3.82k | if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name) |
6910 | 266 | { |
6911 | 266 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Subtype object"); |
6912 | 266 | return FALSE; |
6913 | 266 | } |
6914 | | |
6915 | 3.55k | CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); |
6916 | 3.55k | if (!EQUAL(poSubtype->GetName().c_str(), "GEO")) |
6917 | 15 | return FALSE; |
6918 | | |
6919 | | /* -------------------------------------------------------------------- */ |
6920 | | /* Extract Bounds attribute (optional) */ |
6921 | | /* -------------------------------------------------------------------- */ |
6922 | | |
6923 | | /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf |
6924 | | */ |
6925 | | /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */ |
6926 | | /* LPTS, GPTS and Bounds. Use those ones */ |
6927 | | |
6928 | 3.54k | GDALPDFObject *poBounds = poMeasureDict->Get("lgit:Bounds"); |
6929 | 3.54k | if (poBounds != nullptr && poBounds->GetType() == PDFObjectType_Array) |
6930 | 0 | { |
6931 | 0 | CPLDebug("PDF", "Using lgit:Bounds"); |
6932 | 0 | } |
6933 | 3.54k | else if ((poBounds = poMeasureDict->Get("Bounds")) == nullptr || |
6934 | 2.17k | poBounds->GetType() != PDFObjectType_Array) |
6935 | 1.38k | { |
6936 | 1.38k | poBounds = nullptr; |
6937 | 1.38k | } |
6938 | | |
6939 | 3.54k | if (poBounds != nullptr) |
6940 | 2.16k | { |
6941 | 2.16k | int nBoundsLength = poBounds->GetArray()->GetLength(); |
6942 | 2.16k | if (nBoundsLength == 8) |
6943 | 1.80k | { |
6944 | 1.80k | double adfBounds[8]; |
6945 | 16.2k | for (int i = 0; i < 8; i++) |
6946 | 14.4k | { |
6947 | 14.4k | adfBounds[i] = Get(poBounds, i); |
6948 | 14.4k | CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]); |
6949 | 14.4k | } |
6950 | | |
6951 | | // TODO we should use it to restrict the neatline but |
6952 | | // I have yet to set a sample where bounds are not the four |
6953 | | // corners of the unit square. |
6954 | 1.80k | } |
6955 | 2.16k | } |
6956 | | |
6957 | | /* -------------------------------------------------------------------- */ |
6958 | | /* Extract GPTS attribute */ |
6959 | | /* -------------------------------------------------------------------- */ |
6960 | 3.54k | GDALPDFObject *poGPTS = poMeasureDict->Get("lgit:GPTS"); |
6961 | 3.54k | if (poGPTS != nullptr && poGPTS->GetType() == PDFObjectType_Array) |
6962 | 0 | { |
6963 | 0 | CPLDebug("PDF", "Using lgit:GPTS"); |
6964 | 0 | } |
6965 | 3.54k | else if ((poGPTS = poMeasureDict->Get("GPTS")) == nullptr || |
6966 | 3.50k | poGPTS->GetType() != PDFObjectType_Array) |
6967 | 49 | { |
6968 | 49 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GPTS object"); |
6969 | 49 | return FALSE; |
6970 | 49 | } |
6971 | | |
6972 | 3.49k | int nGPTSLength = poGPTS->GetArray()->GetLength(); |
6973 | 3.49k | if ((nGPTSLength % 2) != 0 || nGPTSLength < 6) |
6974 | 137 | { |
6975 | 137 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for GPTS object"); |
6976 | 137 | return FALSE; |
6977 | 137 | } |
6978 | | |
6979 | 3.35k | std::vector<double> adfGPTS(nGPTSLength); |
6980 | 34.6k | for (int i = 0; i < nGPTSLength; i++) |
6981 | 31.2k | { |
6982 | 31.2k | adfGPTS[i] = Get(poGPTS, i); |
6983 | 31.2k | CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]); |
6984 | 31.2k | } |
6985 | | |
6986 | | /* -------------------------------------------------------------------- */ |
6987 | | /* Extract LPTS attribute */ |
6988 | | /* -------------------------------------------------------------------- */ |
6989 | 3.35k | GDALPDFObject *poLPTS = poMeasureDict->Get("lgit:LPTS"); |
6990 | 3.35k | if (poLPTS != nullptr && poLPTS->GetType() == PDFObjectType_Array) |
6991 | 0 | { |
6992 | 0 | CPLDebug("PDF", "Using lgit:LPTS"); |
6993 | 0 | } |
6994 | 3.35k | else if ((poLPTS = poMeasureDict->Get("LPTS")) == nullptr || |
6995 | 3.26k | poLPTS->GetType() != PDFObjectType_Array) |
6996 | 96 | { |
6997 | 96 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find LPTS object"); |
6998 | 96 | return FALSE; |
6999 | 96 | } |
7000 | | |
7001 | 3.26k | int nLPTSLength = poLPTS->GetArray()->GetLength(); |
7002 | 3.26k | if (nLPTSLength != nGPTSLength) |
7003 | 268 | { |
7004 | 268 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for LPTS object"); |
7005 | 268 | return FALSE; |
7006 | 268 | } |
7007 | | |
7008 | 2.99k | std::vector<double> adfLPTS(nLPTSLength); |
7009 | 26.9k | for (int i = 0; i < nLPTSLength; i++) |
7010 | 23.9k | { |
7011 | 23.9k | adfLPTS[i] = Get(poLPTS, i); |
7012 | 23.9k | CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]); |
7013 | 23.9k | } |
7014 | | |
7015 | | /* -------------------------------------------------------------------- */ |
7016 | | /* Extract GCS attribute */ |
7017 | | /* -------------------------------------------------------------------- */ |
7018 | 2.99k | GDALPDFObject *poGCS = poMeasureDict->Get("GCS"); |
7019 | 2.99k | if (poGCS == nullptr || poGCS->GetType() != PDFObjectType_Dictionary) |
7020 | 135 | { |
7021 | 135 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS object"); |
7022 | 135 | return FALSE; |
7023 | 135 | } |
7024 | | |
7025 | 2.85k | GDALPDFDictionary *poGCSDict = poGCS->GetDictionary(); |
7026 | | |
7027 | | /* -------------------------------------------------------------------- */ |
7028 | | /* Extract GCS.Type attribute */ |
7029 | | /* -------------------------------------------------------------------- */ |
7030 | 2.85k | GDALPDFObject *poGCSType = poGCSDict->Get("Type"); |
7031 | 2.85k | if (poGCSType == nullptr || poGCSType->GetType() != PDFObjectType_Name) |
7032 | 62 | { |
7033 | 62 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS.Type object"); |
7034 | 62 | return FALSE; |
7035 | 62 | } |
7036 | | |
7037 | 2.79k | CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str()); |
7038 | | |
7039 | | /* -------------------------------------------------------------------- */ |
7040 | | /* Extract EPSG attribute */ |
7041 | | /* -------------------------------------------------------------------- */ |
7042 | 2.79k | GDALPDFObject *poEPSG = poGCSDict->Get("EPSG"); |
7043 | 2.79k | int nEPSGCode = 0; |
7044 | 2.79k | if (poEPSG != nullptr && poEPSG->GetType() == PDFObjectType_Int) |
7045 | 1.53k | { |
7046 | 1.53k | nEPSGCode = poEPSG->GetInt(); |
7047 | 1.53k | CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode); |
7048 | 1.53k | } |
7049 | | |
7050 | | /* -------------------------------------------------------------------- */ |
7051 | | /* Extract GCS.WKT attribute */ |
7052 | | /* -------------------------------------------------------------------- */ |
7053 | 2.79k | GDALPDFObject *poGCSWKT = poGCSDict->Get("WKT"); |
7054 | 2.79k | if (poGCSWKT != nullptr && poGCSWKT->GetType() != PDFObjectType_String) |
7055 | 3 | { |
7056 | 3 | poGCSWKT = nullptr; |
7057 | 3 | } |
7058 | | |
7059 | 2.79k | if (poGCSWKT != nullptr) |
7060 | 2.76k | CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str()); |
7061 | | |
7062 | 2.79k | if (nEPSGCode <= 0 && poGCSWKT == nullptr) |
7063 | 9 | { |
7064 | 9 | CPLError(CE_Failure, CPLE_AppDefined, |
7065 | 9 | "Cannot find GCS.WKT or GCS.EPSG objects"); |
7066 | 9 | return FALSE; |
7067 | 9 | } |
7068 | | |
7069 | 2.78k | if (poGCSWKT != nullptr) |
7070 | 2.76k | { |
7071 | 2.76k | m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()); |
7072 | 2.76k | } |
7073 | | |
7074 | 2.78k | bool bSRSOK = false; |
7075 | 2.78k | if (nEPSGCode != 0) |
7076 | 1.53k | { |
7077 | | // At time of writing EPSG CRS codes are <= 32767. |
7078 | | // The usual practice is that codes >= 100000 are in the ESRI namespace |
7079 | | // instead |
7080 | 1.53k | if (nEPSGCode >= 100000) |
7081 | 69 | { |
7082 | 69 | CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler); |
7083 | 69 | OGRSpatialReference oSRS_ESRI; |
7084 | 69 | oSRS_ESRI.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
7085 | 69 | if (oSRS_ESRI.SetFromUserInput(CPLSPrintf("ESRI:%d", nEPSGCode)) == |
7086 | 69 | OGRERR_NONE) |
7087 | 68 | { |
7088 | 68 | bSRSOK = true; |
7089 | | |
7090 | | // Check consistency of ESRI:xxxx and WKT definitions |
7091 | 68 | if (poGCSWKT != nullptr) |
7092 | 68 | { |
7093 | 68 | if (!m_oSRS.GetName() || |
7094 | 49 | (!EQUAL(oSRS_ESRI.GetName(), m_oSRS.GetName()) && |
7095 | 20 | !oSRS_ESRI.IsSame(&m_oSRS))) |
7096 | 39 | { |
7097 | 39 | CPLDebug("PDF", |
7098 | 39 | "Definition from ESRI:%d and WKT=%s do not " |
7099 | 39 | "match. Using WKT string", |
7100 | 39 | nEPSGCode, poGCSWKT->GetString().c_str()); |
7101 | 39 | bSRSOK = false; |
7102 | 39 | } |
7103 | 68 | } |
7104 | 68 | if (bSRSOK) |
7105 | 29 | { |
7106 | 29 | m_oSRS = std::move(oSRS_ESRI); |
7107 | 29 | } |
7108 | 68 | } |
7109 | 69 | } |
7110 | 1.46k | else if (m_oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE) |
7111 | 875 | { |
7112 | 875 | bSRSOK = true; |
7113 | 875 | } |
7114 | 1.53k | } |
7115 | | |
7116 | 2.78k | if (!bSRSOK) |
7117 | 1.88k | { |
7118 | 1.88k | if (poGCSWKT == nullptr) |
7119 | 9 | { |
7120 | 9 | CPLError(CE_Failure, CPLE_AppDefined, |
7121 | 9 | "Cannot resolve EPSG object, and GCS.WKT not found"); |
7122 | 9 | return FALSE; |
7123 | 9 | } |
7124 | | |
7125 | 1.87k | if (m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()) != OGRERR_NONE) |
7126 | 574 | { |
7127 | 574 | m_oSRS.Clear(); |
7128 | 574 | return FALSE; |
7129 | 574 | } |
7130 | 1.87k | } |
7131 | | |
7132 | | /* -------------------------------------------------------------------- */ |
7133 | | /* Compute geotransform */ |
7134 | | /* -------------------------------------------------------------------- */ |
7135 | 2.20k | OGRSpatialReference *poSRSGeog = m_oSRS.CloneGeogCS(); |
7136 | | |
7137 | | /* Files found at |
7138 | | * http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ |
7139 | | */ |
7140 | | /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, |
7141 | | * long) as required by the */ |
7142 | | /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is |
7143 | | * able to understand that, */ |
7144 | | /* so let's also try to do it with a heuristics. */ |
7145 | | |
7146 | 2.20k | bool bReproject = true; |
7147 | 2.20k | if (m_oSRS.IsProjected()) |
7148 | 1.37k | { |
7149 | 6.73k | for (int i = 0; i < nGPTSLength / 2; i++) |
7150 | 5.42k | { |
7151 | 5.42k | if (fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361) |
7152 | 66 | { |
7153 | 66 | CPLDebug("PDF", "GPTS coordinates seems to be in (northing, " |
7154 | 66 | "easting), which is non-standard"); |
7155 | 66 | bReproject = false; |
7156 | 66 | break; |
7157 | 66 | } |
7158 | 5.42k | } |
7159 | 1.37k | } |
7160 | | |
7161 | 2.20k | OGRCoordinateTransformation *poCT = nullptr; |
7162 | 2.20k | if (bReproject) |
7163 | 2.13k | { |
7164 | 2.13k | poCT = OGRCreateCoordinateTransformation(poSRSGeog, &m_oSRS); |
7165 | 2.13k | if (poCT == nullptr) |
7166 | 2 | { |
7167 | 2 | delete poSRSGeog; |
7168 | 2 | m_oSRS.Clear(); |
7169 | 2 | return FALSE; |
7170 | 2 | } |
7171 | 2.13k | } |
7172 | | |
7173 | 2.20k | std::vector<GDAL_GCP> asGCPS(nGPTSLength / 2); |
7174 | | |
7175 | | /* Create NEATLINE */ |
7176 | 2.20k | OGRLinearRing *poRing = nullptr; |
7177 | 2.20k | if (nGPTSLength == 8) |
7178 | 2.20k | { |
7179 | 2.20k | m_poNeatLine = new OGRPolygon(); |
7180 | 2.20k | poRing = new OGRLinearRing(); |
7181 | 2.20k | m_poNeatLine->addRingDirectly(poRing); |
7182 | 2.20k | } |
7183 | | |
7184 | 10.1k | for (int i = 0; i < nGPTSLength / 2; i++) |
7185 | 8.14k | { |
7186 | | /* We probably assume LPTS is 0 or 1 */ |
7187 | 8.14k | asGCPS[i].dfGCPPixel = |
7188 | 8.14k | (dfULX * (1 - adfLPTS[2 * i + 0]) + dfLRX * adfLPTS[2 * i + 0]) / |
7189 | 8.14k | dfMediaBoxWidth * nRasterXSize; |
7190 | 8.14k | asGCPS[i].dfGCPLine = |
7191 | 8.14k | (dfULY * (1 - adfLPTS[2 * i + 1]) + dfLRY * adfLPTS[2 * i + 1]) / |
7192 | 8.14k | dfMediaBoxHeight * nRasterYSize; |
7193 | | |
7194 | 8.14k | double lat = adfGPTS[2 * i]; |
7195 | 8.14k | double lon = adfGPTS[2 * i + 1]; |
7196 | 8.14k | double x = lon; |
7197 | 8.14k | double y = lat; |
7198 | 8.14k | if (bReproject) |
7199 | 7.87k | { |
7200 | 7.87k | if (!poCT->Transform(1, &x, &y, nullptr)) |
7201 | 224 | { |
7202 | 224 | CPLError(CE_Failure, CPLE_AppDefined, |
7203 | 224 | "Cannot reproject (%f, %f)", lon, lat); |
7204 | 224 | delete poSRSGeog; |
7205 | 224 | delete poCT; |
7206 | 224 | m_oSRS.Clear(); |
7207 | 224 | return FALSE; |
7208 | 224 | } |
7209 | 7.87k | } |
7210 | | |
7211 | 7.91k | x = ROUND_IF_CLOSE(x); |
7212 | 7.91k | y = ROUND_IF_CLOSE(y); |
7213 | | |
7214 | 7.91k | asGCPS[i].dfGCPX = x; |
7215 | 7.91k | asGCPS[i].dfGCPY = y; |
7216 | | |
7217 | 7.91k | if (poRing) |
7218 | 7.91k | poRing->addPoint(x, y); |
7219 | 7.91k | } |
7220 | | |
7221 | 1.97k | delete poSRSGeog; |
7222 | 1.97k | delete poCT; |
7223 | | |
7224 | 1.97k | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7225 | 1.97k | FALSE)) |
7226 | 931 | { |
7227 | 931 | CPLDebug("PDF", |
7228 | 931 | "Could not compute GT with exact match. Try with approximate"); |
7229 | 931 | if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(), |
7230 | 931 | TRUE)) |
7231 | 393 | { |
7232 | 393 | CPLError(CE_Failure, CPLE_AppDefined, |
7233 | 393 | "Could not compute GT with approximate match."); |
7234 | 393 | return FALSE; |
7235 | 393 | } |
7236 | 931 | } |
7237 | 1.58k | m_bGeoTransformValid = true; |
7238 | | |
7239 | | // If the non scaling terms of the geotransform are significantly smaller |
7240 | | // than the pixel size, then nullify them as being just artifacts of |
7241 | | // reprojection and GDALGCPsToGeoTransform() numerical imprecisions. |
7242 | 1.58k | const double dfPixelSize = std::min(fabs(m_gt[1]), fabs(m_gt[5])); |
7243 | 1.58k | const double dfRotationShearTerm = std::max(fabs(m_gt[2]), fabs(m_gt[4])); |
7244 | 1.58k | if (dfRotationShearTerm < 1e-5 * dfPixelSize || |
7245 | 670 | (m_bUseLib.test(PDFLIB_PDFIUM) && |
7246 | 0 | std::min(fabs(m_gt[2]), fabs(m_gt[4])) < 1e-5 * dfPixelSize)) |
7247 | 915 | { |
7248 | 915 | dfLRX = m_gt[0] + nRasterXSize * m_gt[1] + nRasterYSize * m_gt[2]; |
7249 | 915 | dfLRY = m_gt[3] + nRasterXSize * m_gt[4] + nRasterYSize * m_gt[5]; |
7250 | 915 | m_gt[1] = (dfLRX - m_gt[0]) / nRasterXSize; |
7251 | 915 | m_gt[5] = (dfLRY - m_gt[3]) / nRasterYSize; |
7252 | 915 | m_gt[2] = m_gt[4] = 0; |
7253 | 915 | } |
7254 | | |
7255 | 1.58k | return TRUE; |
7256 | 1.97k | } |
7257 | | |
7258 | | /************************************************************************/ |
7259 | | /* GetSpatialRef() */ |
7260 | | /************************************************************************/ |
7261 | | |
7262 | | const OGRSpatialReference *PDFDataset::GetSpatialRef() const |
7263 | 37.8k | { |
7264 | 37.8k | const auto poSRS = GDALPamDataset::GetSpatialRef(); |
7265 | 37.8k | if (poSRS) |
7266 | 777 | return poSRS; |
7267 | | |
7268 | 37.1k | if (!m_oSRS.IsEmpty() && m_bGeoTransformValid) |
7269 | 578 | return &m_oSRS; |
7270 | 36.5k | return nullptr; |
7271 | 37.1k | } |
7272 | | |
7273 | | /************************************************************************/ |
7274 | | /* GetGeoTransform() */ |
7275 | | /************************************************************************/ |
7276 | | |
7277 | | CPLErr PDFDataset::GetGeoTransform(GDALGeoTransform >) const |
7278 | | |
7279 | 7.21k | { |
7280 | 7.21k | if (GDALPamDataset::GetGeoTransform(gt) == CE_None) |
7281 | 0 | { |
7282 | 0 | return CE_None; |
7283 | 0 | } |
7284 | | |
7285 | 7.21k | gt = m_gt; |
7286 | 7.21k | return ((m_bGeoTransformValid) ? CE_None : CE_Failure); |
7287 | 7.21k | } |
7288 | | |
7289 | | /************************************************************************/ |
7290 | | /* SetSpatialRef() */ |
7291 | | /************************************************************************/ |
7292 | | |
7293 | | CPLErr PDFDataset::SetSpatialRef(const OGRSpatialReference *poSRS) |
7294 | 0 | { |
7295 | 0 | if (eAccess == GA_ReadOnly) |
7296 | 0 | GDALPamDataset::SetSpatialRef(poSRS); |
7297 | |
|
7298 | 0 | m_oSRS.Clear(); |
7299 | 0 | if (poSRS) |
7300 | 0 | m_oSRS = *poSRS; |
7301 | 0 | m_bProjDirty = true; |
7302 | 0 | return CE_None; |
7303 | 0 | } |
7304 | | |
7305 | | /************************************************************************/ |
7306 | | /* SetGeoTransform() */ |
7307 | | /************************************************************************/ |
7308 | | |
7309 | | CPLErr PDFDataset::SetGeoTransform(const GDALGeoTransform >) |
7310 | 0 | { |
7311 | 0 | if (eAccess == GA_ReadOnly) |
7312 | 0 | GDALPamDataset::SetGeoTransform(gt); |
7313 | |
|
7314 | 0 | m_gt = gt; |
7315 | 0 | m_bGeoTransformValid = true; |
7316 | 0 | m_bProjDirty = true; |
7317 | | |
7318 | | /* Reset NEATLINE if not explicitly set by the user */ |
7319 | 0 | if (!m_bNeatLineDirty) |
7320 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7321 | 0 | return CE_None; |
7322 | 0 | } |
7323 | | |
7324 | | /************************************************************************/ |
7325 | | /* GetMetadataDomainList() */ |
7326 | | /************************************************************************/ |
7327 | | |
7328 | | char **PDFDataset::GetMetadataDomainList() |
7329 | 0 | { |
7330 | 0 | return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), |
7331 | 0 | TRUE, "xml:XMP", "LAYERS", |
7332 | 0 | "EMBEDDED_METADATA", nullptr); |
7333 | 0 | } |
7334 | | |
7335 | | /************************************************************************/ |
7336 | | /* GetMetadata() */ |
7337 | | /************************************************************************/ |
7338 | | |
7339 | | char **PDFDataset::GetMetadata(const char *pszDomain) |
7340 | 58.1k | { |
7341 | 58.1k | if (pszDomain != nullptr && EQUAL(pszDomain, "EMBEDDED_METADATA")) |
7342 | 0 | { |
7343 | 0 | char **papszRet = m_oMDMD_PDF.GetMetadata(pszDomain); |
7344 | 0 | if (papszRet) |
7345 | 0 | return papszRet; |
7346 | | |
7347 | 0 | GDALPDFObject *poCatalog = GetCatalog(); |
7348 | 0 | if (poCatalog == nullptr) |
7349 | 0 | return nullptr; |
7350 | 0 | GDALPDFObject *poFirstElt = |
7351 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]"); |
7352 | 0 | GDALPDFObject *poF = |
7353 | 0 | poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F"); |
7354 | |
|
7355 | 0 | if (poFirstElt == nullptr || |
7356 | 0 | poFirstElt->GetType() != PDFObjectType_String || |
7357 | 0 | poFirstElt->GetString() != "Metadata") |
7358 | 0 | return nullptr; |
7359 | 0 | if (poF == nullptr || poF->GetType() != PDFObjectType_Dictionary) |
7360 | 0 | return nullptr; |
7361 | 0 | GDALPDFStream *poStream = poF->GetStream(); |
7362 | 0 | if (poStream == nullptr) |
7363 | 0 | return nullptr; |
7364 | | |
7365 | 0 | char *apszMetadata[2] = {nullptr, nullptr}; |
7366 | 0 | apszMetadata[0] = poStream->GetBytes(); |
7367 | 0 | m_oMDMD_PDF.SetMetadata(apszMetadata, pszDomain); |
7368 | 0 | VSIFree(apszMetadata[0]); |
7369 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7370 | 0 | } |
7371 | 58.1k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7372 | 21.6k | { |
7373 | 21.6k | char **papszPAMMD = GDALPamDataset::GetMetadata(pszDomain); |
7374 | 108k | for (char **papszIter = papszPAMMD; papszIter && *papszIter; |
7375 | 86.7k | ++papszIter) |
7376 | 86.7k | { |
7377 | 86.7k | char *pszKey = nullptr; |
7378 | 86.7k | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7379 | 86.7k | if (pszKey && pszValue) |
7380 | 86.7k | { |
7381 | 86.7k | if (m_oMDMD_PDF.GetMetadataItem(pszKey, pszDomain) == nullptr) |
7382 | 28.9k | m_oMDMD_PDF.SetMetadataItem(pszKey, pszValue, pszDomain); |
7383 | 86.7k | } |
7384 | 86.7k | CPLFree(pszKey); |
7385 | 86.7k | } |
7386 | 21.6k | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7387 | 21.6k | } |
7388 | 36.4k | if (EQUAL(pszDomain, "LAYERS") || EQUAL(pszDomain, "xml:XMP") || |
7389 | 36.4k | EQUAL(pszDomain, "SUBDATASETS")) |
7390 | 0 | { |
7391 | 0 | return m_oMDMD_PDF.GetMetadata(pszDomain); |
7392 | 0 | } |
7393 | 36.4k | return GDALPamDataset::GetMetadata(pszDomain); |
7394 | 36.4k | } |
7395 | | |
7396 | | /************************************************************************/ |
7397 | | /* SetMetadata() */ |
7398 | | /************************************************************************/ |
7399 | | |
7400 | | CPLErr PDFDataset::SetMetadata(char **papszMetadata, const char *pszDomain) |
7401 | 8.32k | { |
7402 | 8.32k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7403 | 58 | { |
7404 | 58 | char **papszMetadataDup = CSLDuplicate(papszMetadata); |
7405 | 58 | m_oMDMD_PDF.SetMetadata(nullptr, pszDomain); |
7406 | | |
7407 | 375 | for (char **papszIter = papszMetadataDup; papszIter && *papszIter; |
7408 | 317 | ++papszIter) |
7409 | 317 | { |
7410 | 317 | char *pszKey = nullptr; |
7411 | 317 | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
7412 | 317 | if (pszKey && pszValue) |
7413 | 317 | { |
7414 | 317 | SetMetadataItem(pszKey, pszValue, pszDomain); |
7415 | 317 | } |
7416 | 317 | CPLFree(pszKey); |
7417 | 317 | } |
7418 | 58 | CSLDestroy(papszMetadataDup); |
7419 | 58 | return CE_None; |
7420 | 58 | } |
7421 | 8.26k | else if (EQUAL(pszDomain, "xml:XMP")) |
7422 | 4.75k | { |
7423 | 4.75k | m_bXMPDirty = true; |
7424 | 4.75k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7425 | 4.75k | } |
7426 | 3.51k | else if (EQUAL(pszDomain, "SUBDATASETS")) |
7427 | 3.51k | { |
7428 | 3.51k | return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain); |
7429 | 3.51k | } |
7430 | 0 | else |
7431 | 0 | { |
7432 | 0 | return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); |
7433 | 0 | } |
7434 | 8.32k | } |
7435 | | |
7436 | | /************************************************************************/ |
7437 | | /* GetMetadataItem() */ |
7438 | | /************************************************************************/ |
7439 | | |
7440 | | const char *PDFDataset::GetMetadataItem(const char *pszName, |
7441 | | const char *pszDomain) |
7442 | 50.8k | { |
7443 | 50.8k | if (pszDomain != nullptr && EQUAL(pszDomain, "_INTERNAL_") && |
7444 | 0 | pszName != nullptr && EQUAL(pszName, "PDF_LIB")) |
7445 | 0 | { |
7446 | 0 | if (m_bUseLib.test(PDFLIB_POPPLER)) |
7447 | 0 | return "POPPLER"; |
7448 | 0 | if (m_bUseLib.test(PDFLIB_PODOFO)) |
7449 | 0 | return "PODOFO"; |
7450 | 0 | if (m_bUseLib.test(PDFLIB_PDFIUM)) |
7451 | 0 | return "PDFIUM"; |
7452 | 0 | } |
7453 | 50.8k | return CSLFetchNameValue(GetMetadata(pszDomain), pszName); |
7454 | 50.8k | } |
7455 | | |
7456 | | /************************************************************************/ |
7457 | | /* SetMetadataItem() */ |
7458 | | /************************************************************************/ |
7459 | | |
7460 | | CPLErr PDFDataset::SetMetadataItem(const char *pszName, const char *pszValue, |
7461 | | const char *pszDomain) |
7462 | 87.8k | { |
7463 | 87.8k | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
7464 | 32.3k | { |
7465 | 32.3k | if (EQUAL(pszName, "NEATLINE")) |
7466 | 2.54k | { |
7467 | 2.54k | const char *pszOldValue = |
7468 | 2.54k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7469 | 2.54k | if ((pszValue == nullptr && pszOldValue != nullptr) || |
7470 | 2.54k | (pszValue != nullptr && pszOldValue == nullptr) || |
7471 | 0 | (pszValue != nullptr && pszOldValue != nullptr && |
7472 | 0 | strcmp(pszValue, pszOldValue) != 0)) |
7473 | 2.54k | { |
7474 | 2.54k | m_bProjDirty = true; |
7475 | 2.54k | m_bNeatLineDirty = true; |
7476 | 2.54k | } |
7477 | 2.54k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7478 | 2.54k | } |
7479 | 29.8k | else |
7480 | 29.8k | { |
7481 | 29.8k | if (EQUAL(pszName, "AUTHOR") || EQUAL(pszName, "PRODUCER") || |
7482 | 21.1k | EQUAL(pszName, "CREATOR") || EQUAL(pszName, "CREATION_DATE") || |
7483 | 8.85k | EQUAL(pszName, "SUBJECT") || EQUAL(pszName, "TITLE") || |
7484 | 4.79k | EQUAL(pszName, "KEYWORDS")) |
7485 | 25.9k | { |
7486 | 25.9k | if (pszValue == nullptr) |
7487 | 0 | pszValue = ""; |
7488 | 25.9k | const char *pszOldValue = |
7489 | 25.9k | m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain); |
7490 | 25.9k | if (pszOldValue == nullptr || |
7491 | 0 | strcmp(pszValue, pszOldValue) != 0) |
7492 | 25.9k | { |
7493 | 25.9k | m_bInfoDirty = true; |
7494 | 25.9k | } |
7495 | 25.9k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7496 | 25.9k | pszDomain); |
7497 | 25.9k | } |
7498 | 3.86k | else if (EQUAL(pszName, "DPI")) |
7499 | 3.62k | { |
7500 | 3.62k | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, |
7501 | 3.62k | pszDomain); |
7502 | 3.62k | } |
7503 | 237 | else |
7504 | 237 | { |
7505 | 237 | m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7506 | 237 | return GDALPamDataset::SetMetadataItem(pszName, pszValue, |
7507 | 237 | pszDomain); |
7508 | 237 | } |
7509 | 29.8k | } |
7510 | 32.3k | } |
7511 | 55.4k | else if (EQUAL(pszDomain, "xml:XMP")) |
7512 | 0 | { |
7513 | 0 | m_bXMPDirty = true; |
7514 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7515 | 0 | } |
7516 | 55.4k | else if (EQUAL(pszDomain, "SUBDATASETS")) |
7517 | 0 | { |
7518 | 0 | return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain); |
7519 | 0 | } |
7520 | 55.4k | else |
7521 | 55.4k | { |
7522 | 55.4k | return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); |
7523 | 55.4k | } |
7524 | 87.8k | } |
7525 | | |
7526 | | /************************************************************************/ |
7527 | | /* GetGCPCount() */ |
7528 | | /************************************************************************/ |
7529 | | |
7530 | | int PDFDataset::GetGCPCount() |
7531 | 7.20k | { |
7532 | 7.20k | return m_nGCPCount; |
7533 | 7.20k | } |
7534 | | |
7535 | | /************************************************************************/ |
7536 | | /* GetGCPSpatialRef() */ |
7537 | | /************************************************************************/ |
7538 | | |
7539 | | const OGRSpatialReference *PDFDataset::GetGCPSpatialRef() const |
7540 | 7.20k | { |
7541 | 7.20k | if (!m_oSRS.IsEmpty() && m_nGCPCount != 0) |
7542 | 2 | return &m_oSRS; |
7543 | 7.19k | return nullptr; |
7544 | 7.20k | } |
7545 | | |
7546 | | /************************************************************************/ |
7547 | | /* GetGCPs() */ |
7548 | | /************************************************************************/ |
7549 | | |
7550 | | const GDAL_GCP *PDFDataset::GetGCPs() |
7551 | 7.20k | { |
7552 | 7.20k | return m_pasGCPList; |
7553 | 7.20k | } |
7554 | | |
7555 | | /************************************************************************/ |
7556 | | /* SetGCPs() */ |
7557 | | /************************************************************************/ |
7558 | | |
7559 | | CPLErr PDFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn, |
7560 | | const OGRSpatialReference *poSRS) |
7561 | 0 | { |
7562 | 0 | const char *pszGEO_ENCODING = |
7563 | 0 | CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); |
7564 | 0 | if (nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000")) |
7565 | 0 | { |
7566 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
7567 | 0 | "PDF driver only supports writing 4 GCPs when " |
7568 | 0 | "GDAL_PDF_GEO_ENCODING=ISO32000."); |
7569 | 0 | return CE_Failure; |
7570 | 0 | } |
7571 | | |
7572 | | /* Free previous GCPs */ |
7573 | 0 | GDALDeinitGCPs(m_nGCPCount, m_pasGCPList); |
7574 | 0 | CPLFree(m_pasGCPList); |
7575 | | |
7576 | | /* Duplicate in GCPs */ |
7577 | 0 | m_nGCPCount = nGCPCountIn; |
7578 | 0 | m_pasGCPList = GDALDuplicateGCPs(m_nGCPCount, pasGCPListIn); |
7579 | |
|
7580 | 0 | m_oSRS.Clear(); |
7581 | 0 | if (poSRS) |
7582 | 0 | m_oSRS = *poSRS; |
7583 | |
|
7584 | 0 | m_bProjDirty = true; |
7585 | | |
7586 | | /* Reset NEATLINE if not explicitly set by the user */ |
7587 | 0 | if (!m_bNeatLineDirty) |
7588 | 0 | SetMetadataItem("NEATLINE", nullptr); |
7589 | |
|
7590 | 0 | return CE_None; |
7591 | 0 | } |
7592 | | |
7593 | | #endif // #ifdef HAVE_PDF_READ_SUPPORT |
7594 | | |
7595 | | /************************************************************************/ |
7596 | | /* GDALPDFOpen() */ |
7597 | | /************************************************************************/ |
7598 | | |
7599 | | GDALDataset *GDALPDFOpen( |
7600 | | #ifdef HAVE_PDF_READ_SUPPORT |
7601 | | const char *pszFilename, GDALAccess eAccess |
7602 | | #else |
7603 | | CPL_UNUSED const char *pszFilename, CPL_UNUSED GDALAccess eAccess |
7604 | | #endif |
7605 | | ) |
7606 | 58 | { |
7607 | 58 | #ifdef HAVE_PDF_READ_SUPPORT |
7608 | 58 | GDALOpenInfo oOpenInfo(pszFilename, eAccess); |
7609 | 58 | return PDFDataset::Open(&oOpenInfo); |
7610 | | #else |
7611 | | return nullptr; |
7612 | | #endif |
7613 | 58 | } |
7614 | | |
7615 | | /************************************************************************/ |
7616 | | /* GDALPDFUnloadDriver() */ |
7617 | | /************************************************************************/ |
7618 | | |
7619 | | static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver *poDriver) |
7620 | 0 | { |
7621 | 0 | #ifdef HAVE_POPPLER |
7622 | 0 | if (hGlobalParamsMutex != nullptr) |
7623 | 0 | CPLDestroyMutex(hGlobalParamsMutex); |
7624 | 0 | #endif |
7625 | | #ifdef HAVE_PDFIUM |
7626 | | if (PDFDataset::g_bPdfiumInit) |
7627 | | { |
7628 | | CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT); |
7629 | | // Destroy every loaded document or page |
7630 | | TMapPdfiumDatasets::iterator itDoc; |
7631 | | TMapPdfiumPages::iterator itPage; |
7632 | | for (itDoc = g_mPdfiumDatasets.begin(); |
7633 | | itDoc != g_mPdfiumDatasets.end(); ++itDoc) |
7634 | | { |
7635 | | TPdfiumDocumentStruct *pDoc = itDoc->second; |
7636 | | for (itPage = pDoc->pages.begin(); itPage != pDoc->pages.end(); |
7637 | | ++itPage) |
7638 | | { |
7639 | | TPdfiumPageStruct *pPage = itPage->second; |
7640 | | |
7641 | | CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, |
7642 | | PDFIUM_MUTEX_TIMEOUT); |
7643 | | CPLCreateOrAcquireMutex(&(pPage->readMutex), |
7644 | | PDFIUM_MUTEX_TIMEOUT); |
7645 | | CPLReleaseMutex(pPage->readMutex); |
7646 | | CPLDestroyMutex(pPage->readMutex); |
7647 | | FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page)); |
7648 | | delete pPage; |
7649 | | CPLReleaseMutex(g_oPdfiumReadMutex); |
7650 | | } // ~ foreach page |
7651 | | |
7652 | | FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc)); |
7653 | | CPLFree(pDoc->filename); |
7654 | | VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param)); |
7655 | | delete pDoc->psFileAccess; |
7656 | | pDoc->pages.clear(); |
7657 | | |
7658 | | delete pDoc; |
7659 | | } // ~ foreach document |
7660 | | g_mPdfiumDatasets.clear(); |
7661 | | FPDF_DestroyLibrary(); |
7662 | | PDFDataset::g_bPdfiumInit = FALSE; |
7663 | | |
7664 | | CPLReleaseMutex(g_oPdfiumLoadDocMutex); |
7665 | | |
7666 | | if (g_oPdfiumReadMutex) |
7667 | | CPLDestroyMutex(g_oPdfiumReadMutex); |
7668 | | CPLDestroyMutex(g_oPdfiumLoadDocMutex); |
7669 | | } |
7670 | | #endif |
7671 | 0 | } |
7672 | | |
7673 | | /************************************************************************/ |
7674 | | /* PDFSanitizeLayerName() */ |
7675 | | /************************************************************************/ |
7676 | | |
7677 | | CPLString PDFSanitizeLayerName(const char *pszName) |
7678 | 956k | { |
7679 | 956k | if (!CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES"))) |
7680 | 0 | return pszName; |
7681 | | |
7682 | 956k | CPLString osName; |
7683 | 71.2M | for (int i = 0; pszName[i] != '\0'; i++) |
7684 | 70.3M | { |
7685 | 70.3M | if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',') |
7686 | 6.61M | osName += "_"; |
7687 | 63.7M | else if (pszName[i] != '"') |
7688 | 63.6M | osName += pszName[i]; |
7689 | 70.3M | } |
7690 | 956k | if (osName.empty()) |
7691 | 532 | osName = "unnamed"; |
7692 | 956k | return osName; |
7693 | 956k | } |
7694 | | |
7695 | | /************************************************************************/ |
7696 | | /* GDALPDFListLayersAlgorithm */ |
7697 | | /************************************************************************/ |
7698 | | |
7699 | | #ifdef HAVE_PDF_READ_SUPPORT |
7700 | | |
7701 | | class GDALPDFListLayersAlgorithm final : public GDALAlgorithm |
7702 | | { |
7703 | | public: |
7704 | | GDALPDFListLayersAlgorithm() |
7705 | 0 | : GDALAlgorithm("list-layers", |
7706 | 0 | std::string("List layers of a PDF dataset"), |
7707 | 0 | "/drivers/raster/pdf.html") |
7708 | 0 | { |
7709 | 0 | AddInputDatasetArg(&m_dataset, GDAL_OF_RASTER | GDAL_OF_VECTOR); |
7710 | 0 | AddOutputFormatArg(&m_format).SetDefault(m_format).SetChoices("json", |
7711 | 0 | "text"); |
7712 | 0 | AddOutputStringArg(&m_output); |
7713 | 0 | } |
7714 | | |
7715 | | protected: |
7716 | | bool RunImpl(GDALProgressFunc, void *) override; |
7717 | | |
7718 | | private: |
7719 | | GDALArgDatasetValue m_dataset{}; |
7720 | | std::string m_format = "json"; |
7721 | | std::string m_output{}; |
7722 | | }; |
7723 | | |
7724 | | bool GDALPDFListLayersAlgorithm::RunImpl(GDALProgressFunc, void *) |
7725 | 0 | { |
7726 | 0 | auto poDS = dynamic_cast<PDFDataset *>(m_dataset.GetDatasetRef()); |
7727 | 0 | if (!poDS) |
7728 | 0 | { |
7729 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "%s is not a PDF", |
7730 | 0 | m_dataset.GetName().c_str()); |
7731 | 0 | return false; |
7732 | 0 | } |
7733 | 0 | if (m_format == "json") |
7734 | 0 | { |
7735 | 0 | CPLJSonStreamingWriter oWriter(nullptr, nullptr); |
7736 | 0 | oWriter.StartArray(); |
7737 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7738 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7739 | 0 | { |
7740 | 0 | CPL_IGNORE_RET_VAL(key); |
7741 | 0 | oWriter.Add(value); |
7742 | 0 | } |
7743 | 0 | oWriter.EndArray(); |
7744 | 0 | m_output = oWriter.GetString(); |
7745 | 0 | m_output += '\n'; |
7746 | 0 | } |
7747 | 0 | else |
7748 | 0 | { |
7749 | 0 | for (const auto &[key, value] : cpl::IterateNameValue( |
7750 | 0 | const_cast<CSLConstList>(poDS->GetMetadata("LAYERS")))) |
7751 | 0 | { |
7752 | 0 | CPL_IGNORE_RET_VAL(key); |
7753 | 0 | m_output += value; |
7754 | 0 | m_output += '\n'; |
7755 | 0 | } |
7756 | 0 | } |
7757 | 0 | return true; |
7758 | 0 | } |
7759 | | |
7760 | | /************************************************************************/ |
7761 | | /* GDALPDFInstantiateAlgorithm() */ |
7762 | | /************************************************************************/ |
7763 | | |
7764 | | static GDALAlgorithm * |
7765 | | GDALPDFInstantiateAlgorithm(const std::vector<std::string> &aosPath) |
7766 | 0 | { |
7767 | 0 | if (aosPath.size() == 1 && aosPath[0] == "list-layers") |
7768 | 0 | { |
7769 | 0 | return std::make_unique<GDALPDFListLayersAlgorithm>().release(); |
7770 | 0 | } |
7771 | 0 | else |
7772 | 0 | { |
7773 | 0 | return nullptr; |
7774 | 0 | } |
7775 | 0 | } |
7776 | | |
7777 | | #endif // HAVE_PDF_READ_SUPPORT |
7778 | | |
7779 | | /************************************************************************/ |
7780 | | /* GDALRegister_PDF() */ |
7781 | | /************************************************************************/ |
7782 | | |
7783 | | void GDALRegister_PDF() |
7784 | | |
7785 | 22 | { |
7786 | 22 | if (!GDAL_CHECK_VERSION("PDF driver")) |
7787 | 0 | return; |
7788 | | |
7789 | 22 | if (GDALGetDriverByName(DRIVER_NAME) != nullptr) |
7790 | 0 | return; |
7791 | | |
7792 | 22 | GDALDriver *poDriver = new GDALDriver(); |
7793 | 22 | PDFDriverSetCommonMetadata(poDriver); |
7794 | | |
7795 | 22 | #ifdef HAVE_PDF_READ_SUPPORT |
7796 | 22 | poDriver->pfnOpen = PDFDataset::OpenWrapper; |
7797 | 22 | poDriver->pfnInstantiateAlgorithm = GDALPDFInstantiateAlgorithm; |
7798 | 22 | #endif // HAVE_PDF_READ_SUPPORT |
7799 | | |
7800 | 22 | poDriver->pfnCreateCopy = GDALPDFCreateCopy; |
7801 | 22 | poDriver->pfnCreate = PDFWritableVectorDataset::Create; |
7802 | 22 | poDriver->pfnUnloadDriver = GDALPDFUnloadDriver; |
7803 | | |
7804 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
7805 | 22 | } |