Line data Source code
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 0 : void skipBytes(Stream *str, int width, int height, int nComps, int nBits)
83 : {
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 : {
89 0 : if (str->getChar() == EOF)
90 0 : break;
91 : }
92 0 : }
93 :
94 : public:
95 56 : GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
96 : bool reverseVideoA, SplashColorPtr paperColorA)
97 56 : : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA,
98 : paperColorA),
99 56 : bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE)
100 : {
101 56 : }
102 :
103 11 : void SetEnableVector(int bFlag)
104 : {
105 11 : bEnableVector = bFlag;
106 11 : }
107 :
108 11 : void SetEnableText(int bFlag)
109 : {
110 11 : bEnableText = bFlag;
111 11 : }
112 :
113 11 : void SetEnableBitmap(int bFlag)
114 : {
115 11 : bEnableBitmap = bFlag;
116 11 : }
117 :
118 : void startPage(int pageNum, GfxState *state, XRef *xrefIn) override;
119 :
120 1673 : void stroke(GfxState *state) override
121 : {
122 1673 : if (bEnableVector)
123 1664 : SplashOutputDev::stroke(state);
124 1673 : }
125 :
126 8 : void fill(GfxState *state) override
127 : {
128 8 : if (bEnableVector)
129 8 : SplashOutputDev::fill(state);
130 8 : }
131 :
132 38 : void eoFill(GfxState *state) override
133 : {
134 38 : if (bEnableVector)
135 32 : SplashOutputDev::eoFill(state);
136 38 : }
137 :
138 4295 : 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 : {
143 4295 : if (bEnableText)
144 4259 : SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY,
145 : code, nBytes, u, uLen);
146 4295 : }
147 :
148 681 : void beginTextObject(GfxState *state) override
149 : {
150 681 : if (bEnableText)
151 678 : SplashOutputDev::beginTextObject(state);
152 681 : }
153 :
154 681 : void endTextObject(GfxState *state) override
155 : {
156 681 : if (bEnableText)
157 678 : SplashOutputDev::endTextObject(state);
158 681 : }
159 :
160 0 : virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
161 : int width, int height, bool invert,
162 : bool interpolate, bool inlineImg) override
163 : {
164 0 : if (bEnableBitmap)
165 0 : SplashOutputDev::drawImageMask(state, ref, str, width, height,
166 : invert, interpolate, inlineImg);
167 : else
168 : {
169 0 : VSIPDFFileStream::resetNoCheckReturnValue(str);
170 0 : if (inlineImg)
171 : {
172 0 : skipBytes(str, width, height, 1, 1);
173 : }
174 0 : str->close();
175 : }
176 0 : }
177 :
178 0 : virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref,
179 : Stream *str, int width, int height,
180 : bool invert, bool inlineImg,
181 : double *baseMatrix) override
182 : {
183 0 : if (bEnableBitmap)
184 0 : SplashOutputDev::setSoftMaskFromImageMask(
185 : state, ref, str, width, height, invert, inlineImg, baseMatrix);
186 : else
187 0 : str->close();
188 0 : }
189 :
190 0 : virtual void unsetSoftMaskFromImageMask(GfxState *state,
191 : double *baseMatrix) override
192 : {
193 0 : if (bEnableBitmap)
194 0 : SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix);
195 0 : }
196 :
197 43 : 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 : {
202 43 : if (bEnableBitmap)
203 40 : SplashOutputDev::drawImage(state, ref, str, width, height, colorMap,
204 : interpolate, maskColors, inlineImg);
205 : else
206 : {
207 3 : VSIPDFFileStream::resetNoCheckReturnValue(str);
208 3 : if (inlineImg)
209 : {
210 0 : skipBytes(str, width, height, colorMap->getNumPixelComps(),
211 : colorMap->getBits());
212 : }
213 3 : str->close();
214 : }
215 43 : }
216 :
217 0 : 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 : {
223 0 : if (bEnableBitmap)
224 0 : SplashOutputDev::drawMaskedImage(
225 : state, ref, str, width, height, colorMap, interpolate, maskStr,
226 : maskWidth, maskHeight, maskInvert, maskInterpolate);
227 : else
228 0 : str->close();
229 0 : }
230 :
231 2 : 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 : {
239 2 : if (bEnableBitmap)
240 : {
241 2 : if (maskColorMap->getBits() <=
242 : 0) /* workaround poppler bug (robustness) */
243 : {
244 0 : str->close();
245 0 : return;
246 : }
247 2 : SplashOutputDev::drawSoftMaskedImage(
248 : state, ref, str, width, height, colorMap, interpolate, maskStr,
249 : maskWidth, maskHeight, maskColorMap, maskInterpolate);
250 : }
251 : else
252 0 : str->close();
253 : }
254 : };
255 :
256 56 : void GDALPDFOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefIn)
257 : {
258 56 : SplashOutputDev::startPage(pageNum, state, xrefIn);
259 56 : SplashBitmap *poBitmap = getBitmap();
260 112 : memset(poBitmap->getDataPtr(), 255,
261 56 : static_cast<size_t>(poBitmap->getRowSize()) * poBitmap->getHeight());
262 56 : }
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 1 : GDALPDFDumper(const char *pszFilename, const char *pszDumpFile,
284 : int nDepthLimitIn = -1)
285 1 : : nDepthLimit(nDepthLimitIn),
286 1 : bDumpParent(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE"))
287 : {
288 1 : if (strcmp(pszDumpFile, "stderr") == 0)
289 0 : f = stderr;
290 1 : else if (EQUAL(pszDumpFile, "YES"))
291 0 : f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)),
292 : "wt");
293 : else
294 1 : f = fopen(pszDumpFile, "wt");
295 1 : if (f == nullptr)
296 0 : f = stderr;
297 1 : }
298 :
299 1 : ~GDALPDFDumper()
300 1 : {
301 1 : if (f != stderr)
302 1 : fclose(f);
303 1 : }
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 3 : void GDALPDFDumper::Dump(GDALPDFArray *poArray, int nDepth)
311 : {
312 3 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
313 0 : return;
314 :
315 3 : int nLength = poArray->GetLength();
316 : int i;
317 6 : CPLString osIndent;
318 14 : for (i = 0; i < nDepth; i++)
319 11 : osIndent += " ";
320 8 : for (i = 0; i < nLength; i++)
321 : {
322 5 : fprintf(f, "%sItem[%d]:", osIndent.c_str(), i);
323 5 : GDALPDFObject *poObj = nullptr;
324 5 : if ((poObj = poArray->Get(i)) != nullptr)
325 : {
326 5 : if (poObj->GetType() == PDFObjectType_String ||
327 5 : poObj->GetType() == PDFObjectType_Null ||
328 5 : poObj->GetType() == PDFObjectType_Bool ||
329 5 : poObj->GetType() == PDFObjectType_Int ||
330 11 : poObj->GetType() == PDFObjectType_Real ||
331 1 : poObj->GetType() == PDFObjectType_Name)
332 : {
333 4 : fprintf(f, " ");
334 4 : DumpSimplified(poObj);
335 4 : fprintf(f, "\n");
336 : }
337 : else
338 : {
339 1 : fprintf(f, "\n");
340 1 : Dump(poObj, nDepth + 1);
341 : }
342 : }
343 : }
344 : }
345 :
346 493 : void GDALPDFDumper::DumpSimplified(GDALPDFObject *poObj)
347 : {
348 493 : switch (poObj->GetType())
349 : {
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 247 : case PDFObjectType_Int:
363 247 : fprintf(f, "%d (int)", poObj->GetInt());
364 247 : break;
365 :
366 0 : case PDFObjectType_Real:
367 0 : fprintf(f, "%f (real)", poObj->GetReal());
368 0 : break;
369 :
370 246 : case PDFObjectType_Name:
371 246 : fprintf(f, "%s (name)", poObj->GetName().c_str());
372 246 : break;
373 :
374 0 : default:
375 0 : fprintf(f, "unknown !");
376 0 : break;
377 : }
378 493 : }
379 :
380 70 : void GDALPDFDumper::Dump(GDALPDFObject *poObj, int nDepth)
381 : {
382 70 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
383 1 : return;
384 :
385 : int i;
386 70 : CPLString osIndent;
387 516 : for (i = 0; i < nDepth; i++)
388 446 : osIndent += " ";
389 70 : fprintf(f, "%sType = %s", osIndent.c_str(), poObj->GetTypeName());
390 70 : int nRefNum = poObj->GetRefNum().toInt();
391 70 : if (nRefNum != 0)
392 66 : fprintf(f, ", Num = %d, Gen = %d", nRefNum, poObj->GetRefGen());
393 70 : fprintf(f, "\n");
394 :
395 70 : if (nRefNum != 0)
396 : {
397 66 : if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end())
398 1 : return;
399 65 : aoSetObjectExplored.insert(nRefNum);
400 : }
401 :
402 69 : switch (poObj->GetType())
403 : {
404 3 : case PDFObjectType_Array:
405 3 : Dump(poObj->GetArray(), nDepth + 1);
406 3 : break;
407 :
408 66 : case PDFObjectType_Dictionary:
409 66 : Dump(poObj->GetDictionary(), nDepth + 1);
410 66 : break;
411 :
412 0 : case PDFObjectType_String:
413 : case PDFObjectType_Null:
414 : case PDFObjectType_Bool:
415 : case PDFObjectType_Int:
416 : case PDFObjectType_Real:
417 : 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 : }
428 :
429 69 : GDALPDFStream *poStream = poObj->GetStream();
430 69 : if (poStream != nullptr)
431 : {
432 61 : fprintf(f,
433 : "%sHas stream (" CPL_FRMT_GIB
434 : " uncompressed bytes, " CPL_FRMT_GIB " raw bytes)\n",
435 61 : osIndent.c_str(), static_cast<GIntBig>(poStream->GetLength()),
436 61 : static_cast<GIntBig>(poStream->GetRawLength()));
437 : }
438 : }
439 :
440 66 : void GDALPDFDumper::Dump(GDALPDFDictionary *poDict, int nDepth)
441 : {
442 66 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
443 0 : return;
444 :
445 132 : CPLString osIndent;
446 564 : for (int i = 0; i < nDepth; i++)
447 498 : osIndent += " ";
448 66 : int i = 0;
449 66 : const auto &oMap = poDict->GetValues();
450 623 : for (const auto &[osKey, poObj] : oMap)
451 : {
452 557 : fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, osKey.c_str());
453 557 : ++i;
454 557 : if (osKey == "Parent" && !bDumpParent)
455 : {
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 : }
462 557 : if (poObj != nullptr)
463 : {
464 557 : if (poObj->GetType() == PDFObjectType_String ||
465 557 : poObj->GetType() == PDFObjectType_Null ||
466 557 : poObj->GetType() == PDFObjectType_Bool ||
467 557 : poObj->GetType() == PDFObjectType_Int ||
468 1428 : poObj->GetType() == PDFObjectType_Real ||
469 314 : poObj->GetType() == PDFObjectType_Name)
470 : {
471 489 : fprintf(f, " = ");
472 489 : DumpSimplified(poObj);
473 489 : fprintf(f, "\n");
474 : }
475 : else
476 : {
477 68 : fprintf(f, "\n");
478 68 : Dump(poObj, nDepth + 1);
479 : }
480 : }
481 : }
482 : }
483 :
484 : /************************************************************************/
485 : /* PDFRasterBand() */
486 : /************************************************************************/
487 :
488 649 : PDFRasterBand::PDFRasterBand(PDFDataset *poDSIn, int nBandIn,
489 649 : int nResolutionLevelIn)
490 649 : : nResolutionLevel(nResolutionLevelIn)
491 : {
492 649 : poDS = poDSIn;
493 649 : nBand = nBandIn;
494 :
495 649 : eDataType = GDT_UInt8;
496 :
497 649 : if (nResolutionLevel > 0)
498 : {
499 0 : nBlockXSize = 256;
500 0 : nBlockYSize = 256;
501 0 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
502 : }
503 649 : else if (poDSIn->m_nBlockXSize)
504 : {
505 36 : nBlockXSize = poDSIn->m_nBlockXSize;
506 36 : nBlockYSize = poDSIn->m_nBlockYSize;
507 36 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
508 : }
509 613 : else if (poDSIn->GetRasterXSize() <
510 613 : 64 * 1024 * 1024 / poDSIn->GetRasterYSize())
511 : {
512 610 : nBlockXSize = poDSIn->GetRasterXSize();
513 610 : nBlockYSize = 1;
514 : }
515 : else
516 : {
517 3 : nBlockXSize = std::min(1024, poDSIn->GetRasterXSize());
518 3 : nBlockYSize = std::min(1024, poDSIn->GetRasterYSize());
519 3 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
520 : }
521 649 : }
522 :
523 : /************************************************************************/
524 : /* InitOverviews() */
525 : /************************************************************************/
526 :
527 5 : void PDFDataset::InitOverviews()
528 : {
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 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
556 13 : if (!m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() &&
557 13 : m_apoOvrDSBackup.empty() && m_osUserPwd != "ASK_INTERACTIVE")
558 : {
559 2 : int nXSize = nRasterXSize;
560 2 : int nYSize = nRasterYSize;
561 2 : constexpr int minSize = 256;
562 2 : double dfDPI = m_dfDPI;
563 4 : while (nXSize > minSize || nYSize > minSize)
564 : {
565 2 : nXSize = (nXSize + 1) / 2;
566 2 : nYSize = (nYSize + 1) / 2;
567 2 : dfDPI /= 2;
568 :
569 2 : GDALOpenInfo oOpenInfo(GetDescription(), GA_ReadOnly);
570 2 : CPLStringList aosOpenOptions(CSLDuplicate(papszOpenOptions));
571 2 : aosOpenOptions.SetNameValue("DPI", CPLSPrintf("%g", dfDPI));
572 2 : aosOpenOptions.SetNameValue("BANDS", CPLSPrintf("%d", nBands));
573 2 : aosOpenOptions.SetNameValue("@OPEN_FOR_OVERVIEW", "YES");
574 2 : if (!m_osUserPwd.empty())
575 0 : aosOpenOptions.SetNameValue("USER_PWD", m_osUserPwd.c_str());
576 2 : oOpenInfo.papszOpenOptions = aosOpenOptions.List();
577 2 : auto poOvrDS = std::unique_ptr<PDFDataset>(Open(&oOpenInfo));
578 2 : if (!poOvrDS || poOvrDS->nBands != nBands)
579 0 : break;
580 2 : poOvrDS->m_bIsOvrDS = true;
581 2 : m_apoOvrDS.emplace_back(std::move(poOvrDS));
582 : }
583 : }
584 : #endif
585 5 : }
586 :
587 : /************************************************************************/
588 : /* GetColorInterpretation() */
589 : /************************************************************************/
590 :
591 16 : GDALColorInterp PDFRasterBand::GetColorInterpretation()
592 : {
593 16 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
594 16 : if (poGDS->nBands == 1)
595 0 : return GCI_GrayIndex;
596 : else
597 16 : return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1));
598 : }
599 :
600 : /************************************************************************/
601 : /* GetOverviewCount() */
602 : /************************************************************************/
603 :
604 9 : int PDFRasterBand::GetOverviewCount()
605 : {
606 9 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
607 9 : if (poGDS->m_bIsOvrDS)
608 0 : return 0;
609 9 : if (GDALPamRasterBand::GetOverviewCount() > 0)
610 4 : return GDALPamRasterBand::GetOverviewCount();
611 : else
612 : {
613 5 : poGDS->InitOverviews();
614 5 : return static_cast<int>(poGDS->m_apoOvrDS.size());
615 : }
616 : }
617 :
618 : /************************************************************************/
619 : /* GetOverview() */
620 : /************************************************************************/
621 :
622 4 : GDALRasterBand *PDFRasterBand::GetOverview(int iOverviewIndex)
623 : {
624 4 : if (GDALPamRasterBand::GetOverviewCount() > 0)
625 1 : return GDALPamRasterBand::GetOverview(iOverviewIndex);
626 :
627 3 : else if (iOverviewIndex < 0 || iOverviewIndex >= GetOverviewCount())
628 2 : return nullptr;
629 : else
630 : {
631 1 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
632 1 : return poGDS->m_apoOvrDS[iOverviewIndex]->GetRasterBand(nBand);
633 : }
634 : }
635 :
636 : /************************************************************************/
637 : /* ~PDFRasterBand() */
638 : /************************************************************************/
639 :
640 1298 : PDFRasterBand::~PDFRasterBand()
641 : {
642 1298 : }
643 :
644 : /************************************************************************/
645 : /* IReadBlockFromTile() */
646 : /************************************************************************/
647 :
648 160 : CPLErr PDFRasterBand::IReadBlockFromTile(int nBlockXOff, int nBlockYOff,
649 : void *pImage)
650 :
651 : {
652 160 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
653 :
654 160 : int nReqXSize = nBlockXSize;
655 160 : int nReqYSize = nBlockYSize;
656 160 : if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
657 20 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
658 160 : if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
659 24 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
660 :
661 160 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
662 160 : int iTile = poGDS->m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff];
663 160 : if (iTile < 0)
664 : {
665 0 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
666 0 : return CE_None;
667 : }
668 :
669 160 : GDALPDFTileDesc &sTile = poGDS->m_asTiles[iTile];
670 160 : GDALPDFObject *poImage = sTile.poImage;
671 :
672 160 : if (nBand == 4)
673 : {
674 30 : GDALPDFDictionary *poImageDict = poImage->GetDictionary();
675 30 : GDALPDFObject *poSMask = poImageDict->Get("SMask");
676 60 : if (poSMask != nullptr &&
677 30 : poSMask->GetType() == PDFObjectType_Dictionary)
678 : {
679 30 : GDALPDFDictionary *poSMaskDict = poSMask->GetDictionary();
680 30 : GDALPDFObject *poWidth = poSMaskDict->Get("Width");
681 30 : GDALPDFObject *poHeight = poSMaskDict->Get("Height");
682 30 : GDALPDFObject *poColorSpace = poSMaskDict->Get("ColorSpace");
683 : GDALPDFObject *poBitsPerComponent =
684 30 : poSMaskDict->Get("BitsPerComponent");
685 30 : double dfBits = 0;
686 30 : if (poBitsPerComponent)
687 30 : dfBits = Get(poBitsPerComponent);
688 30 : if (poWidth && Get(poWidth) == nReqXSize && poHeight &&
689 30 : Get(poHeight) == nReqYSize && poColorSpace &&
690 60 : poColorSpace->GetType() == PDFObjectType_Name &&
691 112 : poColorSpace->GetName() == "DeviceGray" &&
692 22 : (dfBits == 1 || dfBits == 8))
693 : {
694 30 : GDALPDFStream *poStream = poSMask->GetStream();
695 30 : GByte *pabyStream = nullptr;
696 :
697 30 : if (poStream == nullptr)
698 0 : return CE_Failure;
699 :
700 30 : pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes());
701 30 : if (pabyStream == nullptr)
702 0 : return CE_Failure;
703 :
704 30 : const int nReqXSize1 = (nReqXSize + 7) / 8;
705 52 : if ((dfBits == 8 &&
706 22 : static_cast<size_t>(poStream->GetLength()) !=
707 60 : static_cast<size_t>(nReqXSize) * nReqYSize) ||
708 8 : (dfBits == 1 &&
709 8 : static_cast<size_t>(poStream->GetLength()) !=
710 8 : static_cast<size_t>(nReqXSize1) * nReqYSize))
711 : {
712 0 : VSIFree(pabyStream);
713 0 : return CE_Failure;
714 : }
715 :
716 30 : GByte *pabyData = static_cast<GByte *>(pImage);
717 30 : if (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize)
718 : {
719 10 : memset(pabyData, 0,
720 10 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
721 : }
722 :
723 30 : if (dfBits == 8)
724 : {
725 686 : for (int j = 0; j < nReqYSize; j++)
726 : {
727 21912 : for (int i = 0; i < nReqXSize; i++)
728 : {
729 21248 : pabyData[j * nBlockXSize + i] =
730 21248 : pabyStream[j * nReqXSize + i];
731 : }
732 : }
733 : }
734 : else
735 : {
736 244 : for (int j = 0; j < nReqYSize; j++)
737 : {
738 3288 : for (int i = 0; i < nReqXSize; i++)
739 : {
740 3052 : if (pabyStream[j * nReqXSize1 + i / 8] &
741 3052 : (1 << (7 - (i % 8))))
742 896 : pabyData[j * nBlockXSize + i] = 255;
743 : else
744 2156 : pabyData[j * nBlockXSize + i] = 0;
745 : }
746 : }
747 : }
748 :
749 30 : VSIFree(pabyStream);
750 30 : return CE_None;
751 : }
752 : }
753 :
754 0 : memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
755 0 : return CE_None;
756 : }
757 :
758 130 : if (poGDS->m_nLastBlockXOff == nBlockXOff &&
759 0 : poGDS->m_nLastBlockYOff == nBlockYOff &&
760 0 : poGDS->m_pabyCachedData != nullptr)
761 : {
762 : #ifdef DEBUG
763 0 : CPLDebug("PDF", "Using cached block (%d, %d)", nBlockXOff, nBlockYOff);
764 : #endif
765 : // do nothing
766 : }
767 : else
768 : {
769 130 : if (!poGDS->m_bTried)
770 : {
771 5 : poGDS->m_bTried = true;
772 5 : poGDS->m_pabyCachedData =
773 5 : static_cast<GByte *>(VSIMalloc3(3, nBlockXSize, nBlockYSize));
774 : }
775 130 : if (poGDS->m_pabyCachedData == nullptr)
776 0 : return CE_Failure;
777 :
778 130 : GDALPDFStream *poStream = poImage->GetStream();
779 130 : GByte *pabyStream = nullptr;
780 :
781 130 : if (poStream == nullptr)
782 0 : return CE_Failure;
783 :
784 130 : pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes());
785 130 : if (pabyStream == nullptr)
786 0 : return CE_Failure;
787 :
788 130 : if (static_cast<size_t>(poStream->GetLength()) !=
789 130 : static_cast<size_t>(sTile.nBands) * nReqXSize * nReqYSize)
790 : {
791 0 : VSIFree(pabyStream);
792 0 : return CE_Failure;
793 : }
794 :
795 130 : memcpy(poGDS->m_pabyCachedData, pabyStream,
796 130 : static_cast<size_t>(poStream->GetLength()));
797 130 : VSIFree(pabyStream);
798 130 : poGDS->m_nLastBlockXOff = nBlockXOff;
799 130 : poGDS->m_nLastBlockYOff = nBlockYOff;
800 : }
801 :
802 130 : GByte *pabyData = static_cast<GByte *>(pImage);
803 130 : if (nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize))
804 : {
805 30 : memset(pabyData, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
806 : }
807 :
808 130 : if (poGDS->nBands >= 3 && sTile.nBands == 3)
809 : {
810 2790 : for (int j = 0; j < nReqYSize; j++)
811 : {
812 75600 : for (int i = 0; i < nReqXSize; i++)
813 : {
814 72900 : pabyData[j * nBlockXSize + i] =
815 : poGDS
816 72900 : ->m_pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1];
817 : }
818 90 : }
819 : }
820 40 : else if (sTile.nBands == 1)
821 : {
822 6184 : for (int j = 0; j < nReqYSize; j++)
823 : {
824 1054720 : for (int i = 0; i < nReqXSize; i++)
825 : {
826 1048580 : pabyData[j * nBlockXSize + i] =
827 1048580 : poGDS->m_pabyCachedData[j * nReqXSize + i];
828 : }
829 : }
830 : }
831 :
832 130 : return CE_None;
833 : }
834 :
835 : /************************************************************************/
836 : /* GetSuggestedBlockAccessPattern() */
837 : /************************************************************************/
838 :
839 : GDALSuggestedBlockAccessPattern
840 1 : PDFRasterBand::GetSuggestedBlockAccessPattern() const
841 : {
842 1 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
843 1 : if (!poGDS->m_aiTiles.empty())
844 0 : return GSBAP_RANDOM;
845 1 : return GSBAP_LARGEST_CHUNK_POSSIBLE;
846 : }
847 :
848 : /************************************************************************/
849 : /* IReadBlock() */
850 : /************************************************************************/
851 :
852 25832 : CPLErr PDFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
853 :
854 : {
855 25832 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
856 :
857 25832 : if (!poGDS->m_aiTiles.empty())
858 : {
859 160 : if (IReadBlockFromTile(nBlockXOff, nBlockYOff, pImage) == CE_None)
860 : {
861 160 : return CE_None;
862 : }
863 : else
864 : {
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 : }
872 : }
873 :
874 25672 : int nReqXSize = nBlockXSize;
875 25672 : int nReqYSize = nBlockYSize;
876 25672 : if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
877 0 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
878 25672 : if (nBlockYSize == 1)
879 25671 : nReqYSize = nRasterYSize;
880 1 : else if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
881 0 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
882 :
883 25672 : if (!poGDS->m_bTried)
884 : {
885 55 : poGDS->m_bTried = true;
886 55 : if (nBlockYSize == 1)
887 162 : poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3(
888 54 : std::max(3, poGDS->nBands), nRasterXSize, nRasterYSize));
889 : else
890 3 : poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3(
891 1 : std::max(3, poGDS->nBands), nBlockXSize, nBlockYSize));
892 : }
893 25672 : if (poGDS->m_pabyCachedData == nullptr)
894 0 : return CE_Failure;
895 :
896 25672 : if (poGDS->m_nLastBlockXOff == nBlockXOff &&
897 25617 : (nBlockYSize == 1 || poGDS->m_nLastBlockYOff == nBlockYOff) &&
898 25617 : poGDS->m_pabyCachedData != nullptr)
899 : {
900 : /*CPLDebug("PDF", "Using cached block (%d, %d)",
901 : nBlockXOff, nBlockYOff);*/
902 : // do nothing
903 : }
904 : else
905 : {
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 55 : const int nReqXOff = nBlockXOff * nBlockXSize;
915 55 : const int nReqYOff = (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize;
916 55 : const GSpacing nPixelSpace = 1;
917 55 : const GSpacing nLineSpace = nBlockXSize;
918 55 : const GSpacing nBandSpace =
919 55 : static_cast<GSpacing>(nBlockXSize) *
920 55 : ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize);
921 :
922 55 : CPLErr eErr = poGDS->ReadPixels(nReqXOff, nReqYOff, nReqXSize,
923 : nReqYSize, nPixelSpace, nLineSpace,
924 : nBandSpace, poGDS->m_pabyCachedData);
925 :
926 55 : if (eErr == CE_None)
927 : {
928 55 : poGDS->m_nLastBlockXOff = nBlockXOff;
929 55 : poGDS->m_nLastBlockYOff = nBlockYOff;
930 : }
931 : else
932 : {
933 0 : CPLFree(poGDS->m_pabyCachedData);
934 0 : poGDS->m_pabyCachedData = nullptr;
935 : }
936 : }
937 25672 : if (poGDS->m_pabyCachedData == nullptr)
938 0 : return CE_Failure;
939 :
940 25672 : if (nBlockYSize == 1)
941 25671 : memcpy(pImage,
942 25671 : poGDS->m_pabyCachedData +
943 25671 : (nBand - 1) * nBlockXSize * nRasterYSize +
944 25671 : nBlockYOff * nBlockXSize,
945 25671 : nBlockXSize);
946 : else
947 : {
948 1 : memcpy(pImage,
949 1 : poGDS->m_pabyCachedData +
950 1 : static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize,
951 1 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
952 :
953 1 : if (poGDS->m_bCacheBlocksForOtherBands && nBand == 1)
954 : {
955 3 : for (int iBand = 2; iBand <= poGDS->nBands; ++iBand)
956 : {
957 4 : auto poOtherBand = cpl::down_cast<PDFRasterBand *>(
958 2 : poGDS->papoBands[iBand - 1]);
959 : GDALRasterBlock *poBlock =
960 2 : poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
961 2 : if (poBlock)
962 : {
963 0 : poBlock->DropLock();
964 : }
965 : else
966 : {
967 4 : poBlock = poOtherBand->GetLockedBlockRef(nBlockXOff,
968 2 : nBlockYOff, TRUE);
969 2 : if (poBlock)
970 : {
971 4 : memcpy(poBlock->GetDataRef(),
972 2 : poGDS->m_pabyCachedData +
973 2 : static_cast<size_t>(iBand - 1) *
974 2 : nBlockXSize * nBlockYSize,
975 2 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
976 2 : poBlock->DropLock();
977 : }
978 : }
979 : }
980 : }
981 : }
982 :
983 25672 : return CE_None;
984 : }
985 :
986 : /************************************************************************/
987 : /* PDFEnterPasswordFromConsoleIfNeeded() */
988 : /************************************************************************/
989 :
990 2 : static const char *PDFEnterPasswordFromConsoleIfNeeded(const char *pszUserPwd)
991 : {
992 2 : if (EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
993 : {
994 : static char szPassword[81];
995 2 : printf("Enter password (will be echo'ed in the console): "); /*ok*/
996 2 : if (nullptr == fgets(szPassword, sizeof(szPassword), stdin))
997 : {
998 0 : fprintf(stderr, "WARNING: Error getting password.\n"); /*ok*/
999 : }
1000 2 : szPassword[sizeof(szPassword) - 1] = 0;
1001 2 : char *sz10 = strchr(szPassword, '\n');
1002 2 : if (sz10)
1003 0 : *sz10 = 0;
1004 2 : return szPassword;
1005 : }
1006 0 : return pszUserPwd;
1007 : }
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 1151 : const char *PDFDataset::GetOption(char **papszOpenOptionsIn,
1357 : const char *pszOptionName,
1358 : const char *pszDefaultVal)
1359 : {
1360 1151 : CPLErr eLastErrType = CPLGetLastErrorType();
1361 1151 : CPLErrorNum nLastErrno = CPLGetLastErrorNo();
1362 2302 : CPLString osLastErrorMsg(CPLGetLastErrorMsg());
1363 1151 : CPLXMLNode *psNode = CPLParseXMLString(PDFGetOpenOptionList());
1364 1151 : CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg);
1365 1151 : if (psNode == nullptr)
1366 0 : return pszDefaultVal;
1367 1151 : CPLXMLNode *psIter = psNode->psChild;
1368 4736 : while (psIter != nullptr)
1369 : {
1370 4736 : if (EQUAL(CPLGetXMLValue(psIter, "name", ""), pszOptionName))
1371 : {
1372 : const char *pszVal =
1373 1151 : CSLFetchNameValue(papszOpenOptionsIn, pszOptionName);
1374 1151 : if (pszVal != nullptr)
1375 : {
1376 20 : CPLDestroyXMLNode(psNode);
1377 20 : return pszVal;
1378 : }
1379 : const char *pszAltConfigOption =
1380 1131 : CPLGetXMLValue(psIter, "alt_config_option", nullptr);
1381 1131 : if (pszAltConfigOption != nullptr)
1382 : {
1383 1131 : pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal);
1384 1131 : CPLDestroyXMLNode(psNode);
1385 1131 : return pszVal;
1386 : }
1387 0 : CPLDestroyXMLNode(psNode);
1388 0 : return pszDefaultVal;
1389 : }
1390 3585 : psIter = psIter->psNext;
1391 : }
1392 0 : CPLError(CE_Failure, CPLE_AppDefined,
1393 : "Requesting an undocumented open option '%s'", pszOptionName);
1394 0 : CPLDestroyXMLNode(psNode);
1395 0 : return pszDefaultVal;
1396 : }
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 56 : CPLErr PDFDataset::ReadPixels(int nReqXOff, int nReqYOff, int nReqXSize,
1909 : int nReqYSize, GSpacing nPixelSpace,
1910 : GSpacing nLineSpace, GSpacing nBandSpace,
1911 : GByte *pabyData)
1912 : {
1913 56 : CPLErr eErr = CE_None;
1914 : const char *pszRenderingOptions =
1915 56 : GetOption(papszOpenOptions, "RENDERING_OPTIONS", nullptr);
1916 :
1917 : #ifdef HAVE_POPPLER
1918 56 : if (m_bUseLib.test(PDFLIB_POPPLER))
1919 : {
1920 : SplashColor sColor;
1921 56 : sColor[0] = 255;
1922 56 : sColor[1] = 255;
1923 56 : sColor[2] = 255;
1924 : GDALPDFOutputDev *poSplashOut = new GDALPDFOutputDev(
1925 56 : (nBands < 4) ? splashModeRGB8 : splashModeXBGR8, 4, false,
1926 56 : (nBands < 4) ? sColor : nullptr);
1927 :
1928 56 : if (pszRenderingOptions != nullptr)
1929 : {
1930 7 : poSplashOut->SetEnableVector(FALSE);
1931 7 : poSplashOut->SetEnableText(FALSE);
1932 7 : poSplashOut->SetEnableBitmap(FALSE);
1933 :
1934 : char **papszTokens =
1935 7 : CSLTokenizeString2(pszRenderingOptions, " ,", 0);
1936 19 : for (int i = 0; papszTokens[i] != nullptr; i++)
1937 : {
1938 12 : if (EQUAL(papszTokens[i], "VECTOR"))
1939 4 : poSplashOut->SetEnableVector(TRUE);
1940 8 : else if (EQUAL(papszTokens[i], "TEXT"))
1941 4 : poSplashOut->SetEnableText(TRUE);
1942 4 : else if (EQUAL(papszTokens[i], "RASTER") ||
1943 0 : EQUAL(papszTokens[i], "BITMAP"))
1944 4 : poSplashOut->SetEnableBitmap(TRUE);
1945 : else
1946 : {
1947 0 : CPLError(CE_Warning, CPLE_NotSupported,
1948 : "Value %s is not a valid value for "
1949 : "GDAL_PDF_RENDERING_OPTIONS",
1950 0 : papszTokens[i]);
1951 : }
1952 : }
1953 7 : CSLDestroy(papszTokens);
1954 : }
1955 :
1956 56 : PDFDoc *poDoc = m_poDocPoppler;
1957 56 : poSplashOut->startDoc(poDoc);
1958 :
1959 : // Note: Poppler 25.2 is certainly not the lowest version where we can
1960 : // avoid the hack.
1961 : #if !(POPPLER_MAJOR_VERSION > 25 || \
1962 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2))
1963 : #define USE_OPTCONTENT_HACK
1964 : #endif
1965 :
1966 : #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 56 : Catalog *poCatalog = poDoc->getCatalog();
1978 56 : OCGs *poOldOCGs = poCatalog->optContent;
1979 56 : if (!m_bUseOCG)
1980 49 : poCatalog->optContent = nullptr;
1981 : #endif
1982 : try
1983 : {
1984 56 : poDoc->displayPageSlice(poSplashOut, m_iPage, m_dfDPI, m_dfDPI, 0,
1985 : TRUE, false, false, nReqXOff, nReqYOff,
1986 : nReqXSize, nReqYSize);
1987 : }
1988 0 : catch (const std::exception &e)
1989 : {
1990 0 : CPLError(CE_Failure, CPLE_AppDefined,
1991 0 : "PDFDoc::displayPageSlice() failed with %s", e.what());
1992 :
1993 : #ifdef USE_OPTCONTENT_HACK
1994 : /* Restore back */
1995 0 : poCatalog->optContent = poOldOCGs;
1996 : #endif
1997 0 : delete poSplashOut;
1998 0 : return CE_Failure;
1999 : }
2000 :
2001 : #ifdef USE_OPTCONTENT_HACK
2002 : /* Restore back */
2003 56 : poCatalog->optContent = poOldOCGs;
2004 : #endif
2005 :
2006 56 : SplashBitmap *poBitmap = poSplashOut->getBitmap();
2007 112 : if (poBitmap->getWidth() != nReqXSize ||
2008 56 : poBitmap->getHeight() != nReqYSize)
2009 : {
2010 0 : CPLError(
2011 : CE_Failure, CPLE_AppDefined,
2012 : "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)",
2013 : poBitmap->getWidth(), poBitmap->getHeight(), nReqXSize,
2014 : nReqYSize);
2015 0 : delete poSplashOut;
2016 0 : return CE_Failure;
2017 : }
2018 :
2019 56 : GByte *pabyDataR = pabyData;
2020 56 : GByte *pabyDataG = pabyData + nBandSpace;
2021 56 : GByte *pabyDataB = pabyData + 2 * nBandSpace;
2022 56 : GByte *pabyDataA = pabyData + 3 * nBandSpace;
2023 56 : GByte *pabySrc = poBitmap->getDataPtr();
2024 : GByte *pabyAlphaSrc =
2025 56 : reinterpret_cast<GByte *>(poBitmap->getAlphaPtr());
2026 : int i, j;
2027 22919 : for (j = 0; j < nReqYSize; j++)
2028 : {
2029 20751600 : for (i = 0; i < nReqXSize; i++)
2030 : {
2031 20728800 : if (nBands < 4)
2032 : {
2033 20680200 : pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0];
2034 20680200 : pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1];
2035 20680200 : pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2];
2036 : }
2037 : else
2038 : {
2039 48600 : pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2];
2040 48600 : pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1];
2041 48600 : pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0];
2042 48600 : pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i];
2043 : }
2044 : }
2045 22863 : pabyDataR += nLineSpace;
2046 22863 : pabyDataG += nLineSpace;
2047 22863 : pabyDataB += nLineSpace;
2048 22863 : pabyDataA += nLineSpace;
2049 22863 : pabyAlphaSrc += poBitmap->getAlphaRowSize();
2050 22863 : pabySrc += poBitmap->getRowSize();
2051 : }
2052 56 : delete poSplashOut;
2053 : }
2054 : #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 56 : return eErr;
2298 : }
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 0 : PDFImageRasterBand::PDFImageRasterBand(PDFDataset *poDSIn, int nBandIn)
2321 0 : : PDFRasterBand(poDSIn, nBandIn, 0)
2322 : {
2323 0 : }
2324 :
2325 : /************************************************************************/
2326 : /* IReadBlock() */
2327 : /************************************************************************/
2328 :
2329 0 : CPLErr PDFImageRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
2330 : void *pImage)
2331 : {
2332 0 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
2333 0 : CPLAssert(poGDS->m_poImageObj != nullptr);
2334 :
2335 0 : if (!poGDS->m_bTried)
2336 : {
2337 0 : int nBands = (poGDS->nBands == 1) ? 1 : 3;
2338 0 : poGDS->m_bTried = true;
2339 0 : if (nBands == 3)
2340 : {
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 : }
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 : nullptr)
2355 : {
2356 0 : VSIFree(poGDS->m_pabyCachedData);
2357 0 : poGDS->m_pabyCachedData = nullptr;
2358 0 : return CE_Failure;
2359 : }
2360 :
2361 0 : if (nBands == 3)
2362 : {
2363 : /* pixel interleaved to band interleaved */
2364 0 : for (size_t i = 0;
2365 0 : i < static_cast<size_t>(nRasterXSize) * nRasterYSize; i++)
2366 : {
2367 0 : poGDS->m_pabyCachedData[0 * static_cast<size_t>(nRasterXSize) *
2368 : 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 : }
2377 0 : VSIFree(pabyStream);
2378 : }
2379 : else
2380 0 : poGDS->m_pabyCachedData = pabyStream;
2381 : }
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 : 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 : }
2398 :
2399 : /************************************************************************/
2400 : /* PDFDataset() */
2401 : /************************************************************************/
2402 :
2403 214 : PDFDataset::PDFDataset(PDFDataset *poParentDSIn, int nXSize, int nYSize)
2404 214 : : 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 214 : m_bSetStyle(CPLTestBool(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES")))
2410 : {
2411 214 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2412 214 : nRasterXSize = nXSize;
2413 214 : nRasterYSize = nYSize;
2414 214 : if (poParentDSIn)
2415 0 : m_bUseLib = poParentDSIn->m_bUseLib;
2416 :
2417 214 : InitMapOperators();
2418 214 : }
2419 :
2420 : /************************************************************************/
2421 : /* IBuildOverviews() */
2422 : /************************************************************************/
2423 :
2424 1 : 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 : {
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 1 : if (!m_apoOvrDS.empty())
2438 : {
2439 1 : m_apoOvrDSBackup = std::move(m_apoOvrDS);
2440 1 : m_apoOvrDS.clear();
2441 : }
2442 :
2443 : // Prevents InitOverviews() to run
2444 1 : m_apoOvrDSBackup.emplace_back(nullptr);
2445 1 : const CPLErr eErr = GDALPamDataset::IBuildOverviews(
2446 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2447 : pfnProgress, pProgressData, papszOptions);
2448 1 : m_apoOvrDSBackup.pop_back();
2449 1 : return eErr;
2450 : }
2451 :
2452 : /************************************************************************/
2453 : /* PDFFreeDoc() */
2454 : /************************************************************************/
2455 :
2456 : #ifdef HAVE_POPPLER
2457 228 : static void PDFFreeDoc(PDFDoc *poDoc)
2458 : {
2459 228 : if (poDoc)
2460 : {
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 228 : delete poDoc->str;
2467 228 : poDoc->str = nullptr;
2468 :
2469 228 : delete poDoc;
2470 : }
2471 228 : }
2472 : #endif
2473 :
2474 : /************************************************************************/
2475 : /* GetCatalog() */
2476 : /************************************************************************/
2477 :
2478 454 : GDALPDFObject *PDFDataset::GetCatalog()
2479 : {
2480 454 : if (m_poCatalogObject)
2481 240 : return m_poCatalogObject;
2482 :
2483 : #ifdef HAVE_POPPLER
2484 214 : if (m_bUseLib.test(PDFLIB_POPPLER) && m_poDocPoppler)
2485 : {
2486 : m_poCatalogObjectPoppler =
2487 214 : std::make_unique<Object>(m_poDocPoppler->getXRef()->getCatalog());
2488 214 : if (!m_poCatalogObjectPoppler->isNull())
2489 214 : m_poCatalogObject =
2490 214 : new GDALPDFObjectPoppler(m_poCatalogObjectPoppler.get(), FALSE);
2491 : }
2492 : #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 214 : return m_poCatalogObject;
2531 : }
2532 :
2533 : /************************************************************************/
2534 : /* ~PDFDataset() */
2535 : /************************************************************************/
2536 :
2537 428 : PDFDataset::~PDFDataset()
2538 : {
2539 : #ifdef HAVE_PDFIUM
2540 : m_apoOvrDS.clear();
2541 : m_apoOvrDSBackup.clear();
2542 : #endif
2543 :
2544 214 : CPLFree(m_pabyCachedData);
2545 214 : m_pabyCachedData = nullptr;
2546 :
2547 214 : delete m_poNeatLine;
2548 214 : m_poNeatLine = nullptr;
2549 :
2550 : /* Collect data necessary to update */
2551 214 : int nNum = 0;
2552 214 : int nGen = 0;
2553 214 : GDALPDFDictionaryRW *poPageDictCopy = nullptr;
2554 214 : GDALPDFDictionaryRW *poCatalogDictCopy = nullptr;
2555 214 : if (m_poPageObj)
2556 : {
2557 214 : nNum = m_poPageObj->GetRefNum().toInt();
2558 214 : nGen = m_poPageObj->GetRefGen();
2559 440 : if (eAccess == GA_Update &&
2560 12 : (m_bProjDirty || m_bNeatLineDirty || m_bInfoDirty || m_bXMPDirty) &&
2561 238 : nNum != 0 && m_poPageObj != nullptr &&
2562 12 : m_poPageObj->GetType() == PDFObjectType_Dictionary)
2563 : {
2564 12 : poPageDictCopy = m_poPageObj->GetDictionary()->Clone();
2565 :
2566 12 : if (m_bXMPDirty)
2567 : {
2568 : /* We need the catalog because it points to the XMP Metadata
2569 : * object */
2570 3 : GetCatalog();
2571 6 : if (m_poCatalogObject &&
2572 3 : m_poCatalogObject->GetType() == PDFObjectType_Dictionary)
2573 : poCatalogDictCopy =
2574 3 : m_poCatalogObject->GetDictionary()->Clone();
2575 : }
2576 : }
2577 : }
2578 :
2579 : /* Close document (and file descriptor) to be able to open it */
2580 : /* in read-write mode afterwards */
2581 214 : delete m_poPageObj;
2582 214 : m_poPageObj = nullptr;
2583 214 : delete m_poCatalogObject;
2584 214 : m_poCatalogObject = nullptr;
2585 : #ifdef HAVE_POPPLER
2586 214 : if (m_bUseLib.test(PDFLIB_POPPLER))
2587 : {
2588 214 : m_poCatalogObjectPoppler.reset();
2589 214 : PDFFreeDoc(m_poDocPoppler);
2590 : }
2591 214 : m_poDocPoppler = nullptr;
2592 : #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 214 : m_bHasLoadedLayers = true;
2613 214 : m_apoLayers.clear();
2614 :
2615 : /* Now do the update */
2616 214 : if (poPageDictCopy)
2617 : {
2618 12 : VSILFILE *fp = VSIFOpenL(m_osFilename, "rb+");
2619 12 : if (fp != nullptr)
2620 : {
2621 24 : GDALPDFUpdateWriter oWriter(fp);
2622 12 : if (oWriter.ParseTrailerAndXRef())
2623 : {
2624 12 : if ((m_bProjDirty || m_bNeatLineDirty) &&
2625 : poPageDictCopy != nullptr)
2626 6 : oWriter.UpdateProj(this, m_dfDPI, poPageDictCopy,
2627 12 : GDALPDFObjectNum(nNum), nGen);
2628 :
2629 12 : if (m_bInfoDirty)
2630 3 : oWriter.UpdateInfo(this);
2631 :
2632 12 : if (m_bXMPDirty && poCatalogDictCopy != nullptr)
2633 3 : oWriter.UpdateXMP(this, poCatalogDictCopy);
2634 : }
2635 12 : oWriter.Close();
2636 : }
2637 : else
2638 : {
2639 0 : CPLError(CE_Failure, CPLE_AppDefined,
2640 : "Cannot open %s in update mode", m_osFilename.c_str());
2641 : }
2642 : }
2643 214 : delete poPageDictCopy;
2644 214 : poPageDictCopy = nullptr;
2645 214 : delete poCatalogDictCopy;
2646 214 : poCatalogDictCopy = nullptr;
2647 :
2648 214 : if (m_nGCPCount > 0)
2649 : {
2650 1 : GDALDeinitGCPs(m_nGCPCount, m_pasGCPList);
2651 1 : CPLFree(m_pasGCPList);
2652 1 : m_pasGCPList = nullptr;
2653 1 : m_nGCPCount = 0;
2654 : }
2655 :
2656 214 : CleanupIntermediateResources();
2657 :
2658 : // Do that only after having destroyed Poppler objects
2659 214 : m_fp.reset();
2660 428 : }
2661 :
2662 : /************************************************************************/
2663 : /* IRasterIO() */
2664 : /************************************************************************/
2665 :
2666 1671 : 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 : {
2674 : // Try to pass the request to the most appropriate overview dataset.
2675 1671 : if (nBufXSize < nXSize && nBufYSize < nYSize)
2676 : {
2677 0 : int bTried = FALSE;
2678 0 : const CPLErr eErr = TryOverviewRasterIO(
2679 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2680 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2681 : nBandSpace, psExtraArg, &bTried);
2682 0 : if (bTried)
2683 0 : return eErr;
2684 : }
2685 :
2686 : int nBandBlockXSize, nBandBlockYSize;
2687 1671 : int bReadPixels = FALSE;
2688 1671 : GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize);
2689 3342 : if (m_aiTiles.empty() && eRWFlag == GF_Read && nXSize == nBufXSize &&
2690 1671 : nYSize == nBufYSize &&
2691 1671 : (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) &&
2692 3343 : eBufType == GDT_UInt8 && nBandCount == nBands &&
2693 1 : IsAllBands(nBandCount, panBandMap))
2694 : {
2695 1 : bReadPixels = TRUE;
2696 : #ifdef HAVE_PODOFO
2697 : if (m_bUseLib.test(PDFLIB_PODOFO) && nBands == 4)
2698 : {
2699 : bReadPixels = FALSE;
2700 : }
2701 : #endif
2702 : }
2703 :
2704 1671 : if (bReadPixels)
2705 1 : return ReadPixels(nXOff, nYOff, nXSize, nYSize, nPixelSpace, nLineSpace,
2706 1 : nBandSpace, static_cast<GByte *>(pData));
2707 :
2708 1670 : if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8)
2709 : {
2710 0 : m_bCacheBlocksForOtherBands = true;
2711 : }
2712 1670 : CPLErr eErr = GDALPamDataset::IRasterIO(
2713 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2714 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2715 : psExtraArg);
2716 1670 : m_bCacheBlocksForOtherBands = false;
2717 1670 : return eErr;
2718 : }
2719 :
2720 : /************************************************************************/
2721 : /* IRasterIO() */
2722 : /************************************************************************/
2723 :
2724 25684 : 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 : {
2731 25684 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
2732 :
2733 : // Try to pass the request to the most appropriate overview dataset.
2734 25684 : if (nBufXSize < nXSize && nBufYSize < nYSize)
2735 : {
2736 0 : int bTried = FALSE;
2737 0 : const CPLErr eErr = TryOverviewRasterIO(
2738 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2739 : eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
2740 0 : if (bTried)
2741 0 : return eErr;
2742 : }
2743 :
2744 25684 : if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_UInt8)
2745 : {
2746 20674 : poGDS->m_bCacheBlocksForOtherBands = true;
2747 : }
2748 25684 : CPLErr eErr = GDALPamRasterBand::IRasterIO(
2749 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2750 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
2751 25684 : poGDS->m_bCacheBlocksForOtherBands = false;
2752 25684 : return eErr;
2753 : }
2754 :
2755 : /************************************************************************/
2756 : /* PDFDatasetErrorFunction() */
2757 : /************************************************************************/
2758 :
2759 : #ifdef HAVE_POPPLER
2760 :
2761 29 : static void PDFDatasetErrorFunctionCommon(const CPLString &osError)
2762 : {
2763 29 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
2764 5 : return;
2765 : /* Reported on newer USGS GeoPDF */
2766 24 : if (strcmp(osError.c_str(),
2767 24 : "Couldn't find group for reference to set OFF") == 0)
2768 : {
2769 0 : CPLDebug("PDF", "%s", osError.c_str());
2770 0 : return;
2771 : }
2772 :
2773 24 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
2774 : }
2775 :
2776 : static int g_nPopplerErrors = 0;
2777 : constexpr int MAX_POPPLER_ERRORS = 1000;
2778 :
2779 29 : static void PDFDatasetErrorFunction(ErrorCategory /* eErrCategory */,
2780 : Goffset nPos, const char *pszMsg)
2781 : {
2782 29 : if (g_nPopplerErrors >= MAX_POPPLER_ERRORS)
2783 : {
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 0 : setErrorCallback(nullptr);
2788 0 : globalParams->setErrQuiet(true);
2789 0 : return;
2790 : }
2791 :
2792 29 : g_nPopplerErrors++;
2793 58 : CPLString osError;
2794 :
2795 29 : if (nPos >= 0)
2796 : osError.Printf("Pos = " CPL_FRMT_GUIB ", ",
2797 0 : static_cast<GUIntBig>(nPos));
2798 29 : osError += pszMsg;
2799 29 : PDFDatasetErrorFunctionCommon(osError);
2800 : }
2801 : #endif
2802 :
2803 : /************************************************************************/
2804 : /* GDALPDFParseStreamContentOnlyDrawForm() */
2805 : /************************************************************************/
2806 :
2807 197 : static CPLString GDALPDFParseStreamContentOnlyDrawForm(const char *pszContent)
2808 : {
2809 394 : CPLString osToken;
2810 : char ch;
2811 197 : int nCurIdx = 0;
2812 394 : CPLString osCurrentForm;
2813 :
2814 : // CPLDebug("PDF", "content = %s", pszContent);
2815 :
2816 722 : while ((ch = *pszContent) != '\0')
2817 : {
2818 722 : if (ch == '%')
2819 : {
2820 : /* Skip comments until end-of-line */
2821 0 : while ((ch = *pszContent) != '\0')
2822 : {
2823 0 : if (ch == '\r' || ch == '\n')
2824 : break;
2825 0 : pszContent++;
2826 : }
2827 0 : if (ch == 0)
2828 0 : break;
2829 : }
2830 722 : else if (ch == ' ' || ch == '\r' || ch == '\n')
2831 : {
2832 234 : if (!osToken.empty())
2833 : {
2834 234 : if (nCurIdx == 0 && osToken[0] == '/')
2835 : {
2836 37 : osCurrentForm = osToken.substr(1);
2837 37 : nCurIdx++;
2838 : }
2839 197 : else if (nCurIdx == 1 && osToken == "Do")
2840 : {
2841 0 : nCurIdx++;
2842 : }
2843 : else
2844 : {
2845 197 : return "";
2846 : }
2847 : }
2848 37 : osToken = "";
2849 : }
2850 : else
2851 488 : osToken += ch;
2852 525 : pszContent++;
2853 : }
2854 :
2855 0 : return osCurrentForm;
2856 : }
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 197 : 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 : {
2889 394 : CPLString osToken;
2890 : char ch;
2891 197 : PDFStreamState nState = STATE_INIT;
2892 197 : int nCurIdx = 0;
2893 : double adfVals[6];
2894 394 : CPLString osCurrentImage;
2895 :
2896 197 : double dfDPI = DEFAULT_DPI;
2897 197 : *pbDPISet = FALSE;
2898 :
2899 11224 : while ((ch = *pszContent) != '\0')
2900 : {
2901 11080 : if (ch == '%')
2902 : {
2903 : /* Skip comments until end-of-line */
2904 0 : while ((ch = *pszContent) != '\0')
2905 : {
2906 0 : if (ch == '\r' || ch == '\n')
2907 : break;
2908 0 : pszContent++;
2909 : }
2910 0 : if (ch == 0)
2911 0 : break;
2912 : }
2913 11080 : else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2914 : {
2915 3342 : if (!osToken.empty())
2916 : {
2917 3342 : if (nState == STATE_INIT)
2918 : {
2919 352 : if (osToken == "q")
2920 : {
2921 299 : nState = STATE_AFTER_q;
2922 299 : nCurIdx = 0;
2923 : }
2924 53 : else if (osToken != "Q")
2925 53 : return FALSE;
2926 : }
2927 2990 : else if (nState == STATE_AFTER_q)
2928 : {
2929 2093 : if (osToken == "q")
2930 : {
2931 : // ignore
2932 : }
2933 2093 : else if (nCurIdx < 6)
2934 : {
2935 1794 : adfVals[nCurIdx++] = CPLAtof(osToken);
2936 : }
2937 299 : else if (nCurIdx == 6 && osToken == "cm")
2938 : {
2939 299 : nState = STATE_AFTER_cm;
2940 299 : nCurIdx = 0;
2941 : }
2942 : else
2943 0 : return FALSE;
2944 : }
2945 897 : else if (nState == STATE_AFTER_cm)
2946 : {
2947 598 : if (nCurIdx == 0 && osToken[0] == '/')
2948 : {
2949 299 : osCurrentImage = osToken.substr(1);
2950 : }
2951 299 : else if (osToken == "Do")
2952 : {
2953 299 : nState = STATE_AFTER_Do;
2954 : }
2955 : else
2956 0 : return FALSE;
2957 : }
2958 299 : else if (nState == STATE_AFTER_Do)
2959 : {
2960 299 : if (osToken == "Q")
2961 : {
2962 : GDALPDFObject *poImage =
2963 299 : poXObjectDict->Get(osCurrentImage);
2964 598 : if (poImage != nullptr &&
2965 299 : poImage->GetType() == PDFObjectType_Dictionary)
2966 : {
2967 : GDALPDFTileDesc sTile;
2968 : GDALPDFDictionary *poImageDict =
2969 299 : poImage->GetDictionary();
2970 299 : GDALPDFObject *poWidth = poImageDict->Get("Width");
2971 : GDALPDFObject *poHeight =
2972 299 : poImageDict->Get("Height");
2973 : GDALPDFObject *poColorSpace =
2974 299 : poImageDict->Get("ColorSpace");
2975 299 : GDALPDFObject *poSMask = poImageDict->Get("SMask");
2976 598 : if (poColorSpace &&
2977 299 : poColorSpace->GetType() == PDFObjectType_Name)
2978 : {
2979 296 : if (poColorSpace->GetName() == "DeviceRGB")
2980 : {
2981 113 : sTile.nBands = 3;
2982 113 : if (*pnBands < 3)
2983 26 : *pnBands = 3;
2984 : }
2985 183 : else if (poColorSpace->GetName() ==
2986 : "DeviceGray")
2987 : {
2988 183 : sTile.nBands = 1;
2989 183 : if (*pnBands < 1)
2990 129 : *pnBands = 1;
2991 : }
2992 : else
2993 0 : sTile.nBands = 0;
2994 : }
2995 299 : if (poSMask != nullptr)
2996 94 : *pnBands = 4;
2997 :
2998 299 : if (poWidth && poHeight &&
2999 0 : ((bAcceptRotationTerms &&
3000 299 : adfVals[1] == -adfVals[2]) ||
3001 299 : (!bAcceptRotationTerms && adfVals[1] == 0.0 &&
3002 299 : adfVals[2] == 0.0)))
3003 : {
3004 299 : double dfWidth = Get(poWidth);
3005 299 : double dfHeight = Get(poHeight);
3006 299 : double dfScaleX = adfVals[0];
3007 299 : double dfScaleY = adfVals[3];
3008 299 : if (dfWidth > 0 && dfHeight > 0 &&
3009 299 : dfScaleX > 0 && dfScaleY > 0 &&
3010 299 : dfWidth / dfScaleX * DEFAULT_DPI <
3011 299 : INT_MAX &&
3012 299 : dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX)
3013 : {
3014 598 : double dfDPI_X = ROUND_IF_CLOSE(
3015 299 : dfWidth / dfScaleX * DEFAULT_DPI, 1e-3);
3016 598 : double dfDPI_Y = ROUND_IF_CLOSE(
3017 299 : dfHeight / dfScaleY * DEFAULT_DPI,
3018 : 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 299 : if (dfDPI_X > dfDPI)
3027 20 : dfDPI = dfDPI_X;
3028 299 : if (dfDPI_Y > dfDPI)
3029 0 : dfDPI = dfDPI_Y;
3030 :
3031 299 : memcpy(&(sTile.adfCM), adfVals,
3032 : 6 * sizeof(double));
3033 299 : sTile.poImage = poImage;
3034 299 : sTile.dfWidth = dfWidth;
3035 299 : sTile.dfHeight = dfHeight;
3036 299 : asTiles.push_back(sTile);
3037 :
3038 299 : *pbDPISet = TRUE;
3039 299 : *pdfDPI = dfDPI;
3040 : }
3041 : }
3042 : }
3043 299 : nState = STATE_INIT;
3044 : }
3045 : else
3046 0 : return FALSE;
3047 : }
3048 : }
3049 3289 : osToken = "";
3050 : }
3051 : else
3052 7738 : osToken += ch;
3053 11027 : pszContent++;
3054 : }
3055 :
3056 144 : return TRUE;
3057 : }
3058 :
3059 : /************************************************************************/
3060 : /* CheckTiledRaster() */
3061 : /************************************************************************/
3062 :
3063 158 : int PDFDataset::CheckTiledRaster()
3064 : {
3065 : size_t i;
3066 158 : int l_nBlockXSize = 0;
3067 158 : int l_nBlockYSize = 0;
3068 158 : 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 454 : for (i = 0; i < m_asTiles.size(); i++)
3074 : {
3075 299 : double dfDrawWidth = m_asTiles[i].adfCM[0] * dfUserUnit;
3076 299 : double dfDrawHeight = m_asTiles[i].adfCM[3] * dfUserUnit;
3077 299 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3078 299 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3079 299 : int nX = static_cast<int>(dfX + 0.1);
3080 299 : int nY = static_cast<int>(dfY + 0.1);
3081 299 : int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8);
3082 299 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3083 :
3084 299 : GDALPDFDictionary *poImageDict = m_asTiles[i].poImage->GetDictionary();
3085 : GDALPDFObject *poBitsPerComponent =
3086 299 : poImageDict->Get("BitsPerComponent");
3087 299 : GDALPDFObject *poColorSpace = poImageDict->Get("ColorSpace");
3088 299 : GDALPDFObject *poFilter = poImageDict->Get("Filter");
3089 :
3090 : /* Podofo cannot uncompress JPEG2000 streams */
3091 299 : if (m_bUseLib.test(PDFLIB_PODOFO) && poFilter != nullptr &&
3092 299 : poFilter->GetType() == PDFObjectType_Name &&
3093 0 : poFilter->GetName() == "JPXDecode")
3094 : {
3095 0 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
3096 : static_cast<int>(i));
3097 0 : return FALSE;
3098 : }
3099 :
3100 299 : if (poBitsPerComponent == nullptr || Get(poBitsPerComponent) != 8 ||
3101 299 : poColorSpace == nullptr ||
3102 1077 : poColorSpace->GetType() != PDFObjectType_Name ||
3103 479 : (poColorSpace->GetName() != "DeviceRGB" &&
3104 183 : poColorSpace->GetName() != "DeviceGray"))
3105 : {
3106 3 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
3107 : static_cast<int>(i));
3108 3 : return FALSE;
3109 : }
3110 :
3111 296 : if (fabs(dfDrawWidth - m_asTiles[i].dfWidth) > 1e-2 ||
3112 296 : fabs(dfDrawHeight - m_asTiles[i].dfHeight) > 1e-2 ||
3113 296 : fabs(nWidth - m_asTiles[i].dfWidth) > 1e-8 ||
3114 296 : fabs(nHeight - m_asTiles[i].dfHeight) > 1e-8 ||
3115 296 : fabs(nX - dfX) > 1e-1 || fabs(nY - dfY) > 1e-1 || nX < 0 ||
3116 592 : nY < 0 || nX + nWidth > nRasterXSize || nY >= nRasterYSize)
3117 : {
3118 0 : CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", static_cast<int>(i),
3119 0 : dfX, dfY, dfDrawWidth, dfDrawHeight, m_asTiles[i].dfWidth,
3120 0 : m_asTiles[i].dfHeight);
3121 0 : return FALSE;
3122 : }
3123 296 : if (l_nBlockXSize == 0 && l_nBlockYSize == 0 && nX == 0 && nY != 0)
3124 : {
3125 9 : l_nBlockXSize = nWidth;
3126 9 : l_nBlockYSize = nHeight;
3127 : }
3128 : }
3129 155 : if (l_nBlockXSize <= 0 || l_nBlockYSize <= 0 || l_nBlockXSize > 2048 ||
3130 : l_nBlockYSize > 2048)
3131 146 : return FALSE;
3132 :
3133 9 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, l_nBlockXSize);
3134 9 : 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 159 : for (i = 0; i < m_asTiles.size(); i++)
3139 : {
3140 150 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3141 150 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3142 150 : int nX = static_cast<int>(dfX + 0.1);
3143 150 : int nY = static_cast<int>(dfY + 0.1);
3144 150 : int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8);
3145 150 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3146 150 : int bOK = TRUE;
3147 150 : int nBlockXOff = nX / l_nBlockXSize;
3148 150 : if ((nX % l_nBlockXSize) != 0)
3149 0 : bOK = FALSE;
3150 150 : if (nBlockXOff < nXBlocks - 1 && nWidth != l_nBlockXSize)
3151 0 : bOK = FALSE;
3152 150 : if (nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize)
3153 0 : bOK = FALSE;
3154 :
3155 150 : if (nY > 0 && nHeight != l_nBlockYSize)
3156 0 : bOK = FALSE;
3157 150 : if (nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * l_nBlockYSize)
3158 0 : bOK = FALSE;
3159 :
3160 150 : if (!bOK)
3161 : {
3162 0 : CPLDebug("PDF", "Tile %d : %d %d %d %d", static_cast<int>(i), nX,
3163 : nY, nWidth, nHeight);
3164 0 : return FALSE;
3165 : }
3166 : }
3167 :
3168 : /* Third pass to set the aiTiles array */
3169 9 : m_aiTiles.resize(static_cast<size_t>(nXBlocks) * nYBlocks, -1);
3170 159 : for (i = 0; i < m_asTiles.size(); i++)
3171 : {
3172 150 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3173 150 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3174 150 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3175 150 : int nX = static_cast<int>(dfX + 0.1);
3176 150 : int nY = nRasterYSize - (static_cast<int>(dfY + 0.1) + nHeight);
3177 150 : int nBlockXOff = nX / l_nBlockXSize;
3178 150 : int nBlockYOff = nY / l_nBlockYSize;
3179 150 : m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff] = static_cast<int>(i);
3180 : }
3181 :
3182 9 : this->m_nBlockXSize = l_nBlockXSize;
3183 9 : this->m_nBlockYSize = l_nBlockYSize;
3184 :
3185 9 : return TRUE;
3186 : }
3187 :
3188 : /************************************************************************/
3189 : /* GuessDPI() */
3190 : /************************************************************************/
3191 :
3192 214 : void PDFDataset::GuessDPI(GDALPDFDictionary *poPageDict, int *pnBands)
3193 : {
3194 214 : const char *pszDPI = GetOption(papszOpenOptions, "DPI", nullptr);
3195 214 : if (pszDPI != nullptr)
3196 : {
3197 : // coverity[tainted_data]
3198 3 : m_dfDPI = CPLAtof(pszDPI);
3199 : }
3200 : else
3201 : {
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 211 : GDALPDFObject *poContents = poPageDict->Get("Contents");
3206 421 : if (poContents != nullptr &&
3207 210 : poContents->GetType() == PDFObjectType_Array)
3208 : {
3209 1 : GDALPDFArray *poContentsArray = poContents->GetArray();
3210 1 : if (poContentsArray->GetLength() == 1)
3211 : {
3212 1 : poContents = poContentsArray->Get(0);
3213 : }
3214 : }
3215 :
3216 : GDALPDFObject *poXObject =
3217 211 : poPageDict->LookupObject("Resources.XObject");
3218 421 : if (poContents != nullptr &&
3219 210 : poContents->GetType() == PDFObjectType_Dictionary &&
3220 421 : poXObject != nullptr &&
3221 198 : poXObject->GetType() == PDFObjectType_Dictionary)
3222 : {
3223 198 : GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary();
3224 198 : GDALPDFDictionary *poContentDict = poXObjectDict;
3225 198 : GDALPDFStream *poPageStream = poContents->GetStream();
3226 198 : if (poPageStream != nullptr)
3227 : {
3228 197 : char *pszContent = nullptr;
3229 197 : const int64_t MAX_LENGTH = 10 * 1000 * 1000;
3230 197 : int64_t nLength = poPageStream->GetLength(MAX_LENGTH);
3231 197 : int bResetTiles = FALSE;
3232 197 : double dfScaleDPI = 1.0;
3233 :
3234 197 : if (nLength < MAX_LENGTH)
3235 : {
3236 394 : CPLString osForm;
3237 197 : pszContent = poPageStream->GetBytes();
3238 197 : if (pszContent != nullptr)
3239 : {
3240 : #ifdef DEBUG
3241 : const char *pszDumpStream =
3242 197 : CPLGetConfigOption("PDF_DUMP_STREAM", nullptr);
3243 197 : if (pszDumpStream != nullptr)
3244 : {
3245 0 : VSILFILE *fpDump = VSIFOpenL(pszDumpStream, "wb");
3246 0 : if (fpDump)
3247 : {
3248 0 : VSIFWriteL(pszContent, 1,
3249 : static_cast<int>(nLength), fpDump);
3250 0 : VSIFCloseL(fpDump);
3251 : }
3252 : }
3253 : #endif // DEBUG
3254 : osForm =
3255 197 : GDALPDFParseStreamContentOnlyDrawForm(pszContent);
3256 197 : if (osForm.empty())
3257 : {
3258 : /* Special case for USGS Topo PDF, like
3259 : * CA_Hollywood_20090811_OM_geo.pdf */
3260 : const char *pszOGCDo =
3261 197 : strstr(pszContent, " /XO1 Do");
3262 197 : if (pszOGCDo)
3263 : {
3264 0 : const char *pszcm = strstr(pszContent, " cm ");
3265 0 : if (pszcm != nullptr && pszcm < pszOGCDo)
3266 : {
3267 : const char *pszNextcm =
3268 0 : strstr(pszcm + 2, "cm");
3269 0 : if (pszNextcm == nullptr ||
3270 : pszNextcm > pszOGCDo)
3271 : {
3272 0 : const char *pszIter = pszcm;
3273 0 : while (pszIter > pszContent)
3274 : {
3275 0 : if ((*pszIter >= '0' &&
3276 0 : *pszIter <= '9') ||
3277 0 : *pszIter == '-' ||
3278 0 : *pszIter == '.' ||
3279 0 : *pszIter == ' ')
3280 0 : pszIter--;
3281 : else
3282 : {
3283 0 : pszIter++;
3284 0 : break;
3285 : }
3286 : }
3287 0 : CPLString oscm(pszIter);
3288 0 : oscm.resize(pszcm - pszIter);
3289 : 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 : {
3295 0 : dfScaleX = CPLAtof(papszTokens[0]);
3296 0 : dfScaleY = CPLAtof(papszTokens[3]);
3297 : }
3298 0 : CSLDestroy(papszTokens);
3299 0 : if (dfScaleX == dfScaleY &&
3300 : dfScaleX > 0.0)
3301 : {
3302 0 : osForm = "XO1";
3303 0 : bResetTiles = TRUE;
3304 0 : dfScaleDPI = 1.0 / dfScaleX;
3305 : }
3306 0 : }
3307 : }
3308 : else
3309 : {
3310 0 : osForm = "XO1";
3311 0 : bResetTiles = TRUE;
3312 : }
3313 : }
3314 : /* Special case for USGS Topo PDF, like
3315 : * CA_Sacramento_East_20120308_TM_geo.pdf */
3316 : else
3317 : {
3318 : CPLString osOCG =
3319 394 : FindLayerOCG(poPageDict, "Orthoimage");
3320 197 : if (!osOCG.empty())
3321 : {
3322 0 : const char *pszBDCLookup = CPLSPrintf(
3323 : "/OC /%s BDC", osOCG.c_str());
3324 : const char *pszBDC =
3325 0 : strstr(pszContent, pszBDCLookup);
3326 0 : if (pszBDC != nullptr)
3327 : {
3328 0 : const char *pszIter =
3329 0 : pszBDC + strlen(pszBDCLookup);
3330 0 : while (*pszIter != '\0')
3331 : {
3332 0 : if (*pszIter == 13 ||
3333 0 : *pszIter == 10 ||
3334 0 : *pszIter == ' ' ||
3335 0 : *pszIter == 'q')
3336 0 : pszIter++;
3337 : else
3338 : break;
3339 : }
3340 0 : if (STARTS_WITH(pszIter,
3341 : "1 0 0 1 0 0 cm\n"))
3342 0 : pszIter +=
3343 : strlen("1 0 0 1 0 0 cm\n");
3344 0 : if (*pszIter == '/')
3345 : {
3346 0 : pszIter++;
3347 : const char *pszDo =
3348 0 : strstr(pszIter, " Do");
3349 0 : if (pszDo != nullptr)
3350 : {
3351 0 : osForm = pszIter;
3352 0 : osForm.resize(pszDo - pszIter);
3353 0 : bResetTiles = TRUE;
3354 : }
3355 : }
3356 : }
3357 : }
3358 : }
3359 : }
3360 : }
3361 :
3362 197 : if (!osForm.empty())
3363 : {
3364 0 : CPLFree(pszContent);
3365 0 : pszContent = nullptr;
3366 :
3367 0 : GDALPDFObject *poObjForm = poXObjectDict->Get(osForm);
3368 0 : if (poObjForm != nullptr &&
3369 0 : poObjForm->GetType() == PDFObjectType_Dictionary &&
3370 0 : (poPageStream = poObjForm->GetStream()) != nullptr)
3371 : {
3372 : GDALPDFDictionary *poObjFormDict =
3373 0 : poObjForm->GetDictionary();
3374 : GDALPDFObject *poSubtype =
3375 0 : poObjFormDict->Get("Subtype");
3376 0 : if (poSubtype != nullptr &&
3377 0 : poSubtype->GetType() == PDFObjectType_Name &&
3378 0 : poSubtype->GetName() == "Form")
3379 : {
3380 0 : nLength = poPageStream->GetLength(MAX_LENGTH);
3381 0 : if (nLength < MAX_LENGTH)
3382 : {
3383 0 : pszContent = poPageStream->GetBytes();
3384 :
3385 : GDALPDFObject *poXObject2 =
3386 0 : poObjFormDict->LookupObject(
3387 : "Resources.XObject");
3388 0 : if (poXObject2 != nullptr &&
3389 0 : poXObject2->GetType() ==
3390 : PDFObjectType_Dictionary)
3391 : poContentDict =
3392 0 : poXObject2->GetDictionary();
3393 : }
3394 : }
3395 : }
3396 : }
3397 : }
3398 :
3399 197 : if (pszContent != nullptr)
3400 : {
3401 197 : int bDPISet = FALSE;
3402 :
3403 197 : const char *pszContentToParse = pszContent;
3404 197 : if (bResetTiles)
3405 : {
3406 0 : while (*pszContentToParse != '\0')
3407 : {
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 : else
3421 : break;
3422 : }
3423 : }
3424 :
3425 197 : GDALPDFParseStreamContent(pszContentToParse, poContentDict,
3426 : &(m_dfDPI), &bDPISet, pnBands,
3427 197 : m_asTiles, bResetTiles);
3428 197 : CPLFree(pszContent);
3429 197 : if (bDPISet)
3430 : {
3431 158 : m_dfDPI *= dfScaleDPI;
3432 :
3433 158 : CPLDebug("PDF",
3434 : "DPI guessed from contents stream = %.16g",
3435 : m_dfDPI);
3436 158 : SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI));
3437 158 : if (bResetTiles)
3438 0 : m_asTiles.resize(0);
3439 : }
3440 : else
3441 39 : m_asTiles.resize(0);
3442 : }
3443 : }
3444 : }
3445 :
3446 211 : GDALPDFObject *poUserUnit = nullptr;
3447 387 : if ((poUserUnit = poPageDict->Get("UserUnit")) != nullptr &&
3448 176 : (poUserUnit->GetType() == PDFObjectType_Int ||
3449 12 : poUserUnit->GetType() == PDFObjectType_Real))
3450 : {
3451 176 : m_dfDPI = ROUND_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI, 1e-5);
3452 176 : CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", m_dfDPI);
3453 176 : SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI));
3454 : }
3455 : }
3456 :
3457 214 : if (m_dfDPI < 1e-2 || m_dfDPI > 7200)
3458 : {
3459 0 : CPLError(CE_Warning, CPLE_AppDefined,
3460 : "Invalid value for GDAL_PDF_DPI. Using default value instead");
3461 0 : m_dfDPI = GDAL_DEFAULT_DPI;
3462 : }
3463 214 : }
3464 :
3465 : /************************************************************************/
3466 : /* FindXMP() */
3467 : /************************************************************************/
3468 :
3469 0 : void PDFDataset::FindXMP(GDALPDFObject *poObj)
3470 : {
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 : {
3482 0 : return;
3483 : }
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 : {
3494 : char *apszMDList[2];
3495 0 : apszMDList[0] = pszContent;
3496 0 : apszMDList[1] = nullptr;
3497 0 : SetMetadata(apszMDList, "xml:XMP");
3498 : }
3499 0 : CPLFree(pszContent);
3500 : }
3501 :
3502 : /************************************************************************/
3503 : /* ParseInfo() */
3504 : /************************************************************************/
3505 :
3506 214 : void PDFDataset::ParseInfo(GDALPDFObject *poInfoObj)
3507 : {
3508 214 : if (poInfoObj->GetType() != PDFObjectType_Dictionary)
3509 175 : return;
3510 :
3511 39 : GDALPDFDictionary *poInfoObjDict = poInfoObj->GetDictionary();
3512 39 : GDALPDFObject *poItem = nullptr;
3513 39 : int bOneMDISet = FALSE;
3514 48 : if ((poItem = poInfoObjDict->Get("Author")) != nullptr &&
3515 9 : poItem->GetType() == PDFObjectType_String)
3516 : {
3517 9 : SetMetadataItem("AUTHOR", poItem->GetString().c_str());
3518 9 : bOneMDISet = TRUE;
3519 : }
3520 66 : if ((poItem = poInfoObjDict->Get("Creator")) != nullptr &&
3521 27 : poItem->GetType() == PDFObjectType_String)
3522 : {
3523 27 : SetMetadataItem("CREATOR", poItem->GetString().c_str());
3524 27 : bOneMDISet = TRUE;
3525 : }
3526 43 : if ((poItem = poInfoObjDict->Get("Keywords")) != nullptr &&
3527 4 : poItem->GetType() == PDFObjectType_String)
3528 : {
3529 4 : SetMetadataItem("KEYWORDS", poItem->GetString().c_str());
3530 4 : bOneMDISet = TRUE;
3531 : }
3532 45 : if ((poItem = poInfoObjDict->Get("Subject")) != nullptr &&
3533 6 : poItem->GetType() == PDFObjectType_String)
3534 : {
3535 6 : SetMetadataItem("SUBJECT", poItem->GetString().c_str());
3536 6 : bOneMDISet = TRUE;
3537 : }
3538 46 : if ((poItem = poInfoObjDict->Get("Title")) != nullptr &&
3539 7 : poItem->GetType() == PDFObjectType_String)
3540 : {
3541 7 : SetMetadataItem("TITLE", poItem->GetString().c_str());
3542 7 : bOneMDISet = TRUE;
3543 : }
3544 52 : if ((poItem = poInfoObjDict->Get("Producer")) != nullptr &&
3545 13 : poItem->GetType() == PDFObjectType_String)
3546 : {
3547 19 : if (bOneMDISet ||
3548 6 : poItem->GetString() != "PoDoFo - http://podofo.sf.net")
3549 : {
3550 7 : SetMetadataItem("PRODUCER", poItem->GetString().c_str());
3551 7 : bOneMDISet = TRUE;
3552 : }
3553 : }
3554 68 : if ((poItem = poInfoObjDict->Get("CreationDate")) != nullptr &&
3555 29 : poItem->GetType() == PDFObjectType_String)
3556 : {
3557 29 : if (bOneMDISet)
3558 23 : SetMetadataItem("CREATION_DATE", poItem->GetString().c_str());
3559 : }
3560 : }
3561 :
3562 : #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
3563 :
3564 : /************************************************************************/
3565 : /* AddLayer() */
3566 : /************************************************************************/
3567 :
3568 368 : void PDFDataset::AddLayer(const std::string &osName, int iPage)
3569 : {
3570 736 : LayerStruct layerStruct;
3571 368 : layerStruct.osName = osName;
3572 368 : layerStruct.nInsertIdx = static_cast<int>(m_oLayerNameSet.size());
3573 368 : layerStruct.iPage = iPage;
3574 368 : m_oLayerNameSet.emplace_back(std::move(layerStruct));
3575 368 : }
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 63 : void PDFDataset::SortLayerList()
3585 : {
3586 : // Sort layers by prioritizing page number and then insertion index
3587 63 : std::sort(m_oLayerNameSet.begin(), m_oLayerNameSet.end(),
3588 1777 : [](const LayerStruct &a, const LayerStruct &b)
3589 : {
3590 1777 : if (a.iPage < b.iPage)
3591 39 : return true;
3592 1738 : if (a.iPage > b.iPage)
3593 0 : return false;
3594 1738 : return a.nInsertIdx < b.nInsertIdx;
3595 : });
3596 63 : }
3597 :
3598 : /************************************************************************/
3599 : /* CreateLayerList() */
3600 : /************************************************************************/
3601 :
3602 63 : void PDFDataset::CreateLayerList()
3603 : {
3604 63 : SortLayerList();
3605 :
3606 63 : if (m_oLayerNameSet.size() >= 100)
3607 : {
3608 199 : for (const auto &oLayerStruct : m_oLayerNameSet)
3609 : {
3610 : m_aosLayerNames.AddNameValue(
3611 : CPLSPrintf("LAYER_%03d_NAME", m_aosLayerNames.size()),
3612 198 : oLayerStruct.osName.c_str());
3613 : }
3614 : }
3615 : else
3616 : {
3617 232 : for (const auto &oLayerStruct : m_oLayerNameSet)
3618 : {
3619 : m_aosLayerNames.AddNameValue(
3620 : CPLSPrintf("LAYER_%02d_NAME", m_aosLayerNames.size()),
3621 170 : oLayerStruct.osName.c_str());
3622 : }
3623 : }
3624 63 : }
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 440 : std::string PDFDataset::BuildPostfixedLayerNameAndAddLayer(
3636 : const std::string &osName, const std::pair<int, int> &oOCGRef,
3637 : int iPageOfInterest, int nPageCount)
3638 : {
3639 880 : std::string osPostfixedName = osName;
3640 440 : int iLayerPage = 0;
3641 440 : if (nPageCount > 1 && !m_oMapOCGNumGenToPages.empty())
3642 : {
3643 108 : const auto oIterToPages = m_oMapOCGNumGenToPages.find(oOCGRef);
3644 108 : if (oIterToPages != m_oMapOCGNumGenToPages.end())
3645 : {
3646 108 : const auto &anPages = oIterToPages->second;
3647 108 : if (iPageOfInterest > 0)
3648 : {
3649 96 : if (std::find(anPages.begin(), anPages.end(),
3650 96 : iPageOfInterest) == anPages.end())
3651 : {
3652 72 : return std::string();
3653 : }
3654 : }
3655 12 : else if (anPages.size() == 1)
3656 : {
3657 12 : iLayerPage = anPages.front();
3658 12 : osPostfixedName += CPLSPrintf(" (page %d)", anPages.front());
3659 : }
3660 : else
3661 : {
3662 0 : osPostfixedName += " (pages ";
3663 0 : for (size_t j = 0; j < anPages.size(); ++j)
3664 : {
3665 0 : if (j > 0)
3666 0 : osPostfixedName += ", ";
3667 0 : osPostfixedName += CPLSPrintf("%d", anPages[j]);
3668 : }
3669 0 : osPostfixedName += ')';
3670 : }
3671 : }
3672 : }
3673 :
3674 368 : AddLayer(osPostfixedName, iLayerPage);
3675 :
3676 368 : return osPostfixedName;
3677 : }
3678 :
3679 : #endif // defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
3680 :
3681 : #ifdef HAVE_POPPLER
3682 :
3683 : /************************************************************************/
3684 : /* ExploreLayersPoppler() */
3685 : /************************************************************************/
3686 :
3687 215 : void PDFDataset::ExploreLayersPoppler(GDALPDFArray *poArray,
3688 : int iPageOfInterest, int nPageCount,
3689 : CPLString osTopLayer, int nRecLevel,
3690 : int &nVisited, bool &bStop)
3691 : {
3692 215 : if (nRecLevel == 16 || nVisited == 1000)
3693 : {
3694 0 : CPLError(
3695 : CE_Failure, CPLE_AppDefined,
3696 : "ExploreLayersPoppler(): too deep exploration or too many items");
3697 0 : bStop = true;
3698 0 : return;
3699 : }
3700 215 : if (bStop)
3701 0 : return;
3702 :
3703 215 : int nLength = poArray->GetLength();
3704 215 : CPLString osCurLayer;
3705 807 : for (int i = 0; i < nLength; i++)
3706 : {
3707 592 : nVisited++;
3708 592 : GDALPDFObject *poObj = poArray->Get(i);
3709 592 : if (poObj == nullptr)
3710 0 : continue;
3711 592 : if (i == 0 && poObj->GetType() == PDFObjectType_String)
3712 : {
3713 : std::string osName =
3714 0 : PDFSanitizeLayerName(poObj->GetString().c_str());
3715 0 : if (!osTopLayer.empty())
3716 : {
3717 0 : osTopLayer += '.';
3718 0 : osTopLayer += osName;
3719 : }
3720 : else
3721 0 : osTopLayer = std::move(osName);
3722 0 : AddLayer(osTopLayer, 0);
3723 0 : m_oLayerOCGListPoppler.push_back(std::pair(osTopLayer, nullptr));
3724 : }
3725 592 : else if (poObj->GetType() == PDFObjectType_Array)
3726 : {
3727 152 : ExploreLayersPoppler(poObj->GetArray(), iPageOfInterest, nPageCount,
3728 : osCurLayer, nRecLevel + 1, nVisited, bStop);
3729 152 : if (bStop)
3730 0 : return;
3731 152 : osCurLayer = "";
3732 : }
3733 440 : else if (poObj->GetType() == PDFObjectType_Dictionary)
3734 : {
3735 440 : GDALPDFDictionary *poDict = poObj->GetDictionary();
3736 440 : GDALPDFObject *poName = poDict->Get("Name");
3737 440 : if (poName != nullptr && poName->GetType() == PDFObjectType_String)
3738 : {
3739 : std::string osName =
3740 440 : PDFSanitizeLayerName(poName->GetString().c_str());
3741 : /* coverity[copy_paste_error] */
3742 440 : if (!osTopLayer.empty())
3743 : {
3744 310 : osCurLayer = osTopLayer;
3745 310 : osCurLayer += '.';
3746 310 : osCurLayer += osName;
3747 : }
3748 : else
3749 130 : 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 : OCGs *optContentConfig =
3757 440 : m_poDocPoppler->getOptContentConfig();
3758 : struct Ref r;
3759 440 : r.num = poObj->GetRefNum().toInt();
3760 440 : r.gen = poObj->GetRefGen();
3761 440 : OptionalContentGroup *ocg = optContentConfig->findOcgByRef(r);
3762 440 : if (ocg)
3763 : {
3764 440 : const auto oRefPair = std::pair(poObj->GetRefNum().toInt(),
3765 880 : poObj->GetRefGen());
3766 : const std::string osPostfixedName =
3767 : BuildPostfixedLayerNameAndAddLayer(
3768 440 : osCurLayer, oRefPair, iPageOfInterest, nPageCount);
3769 440 : if (osPostfixedName.empty())
3770 72 : continue;
3771 :
3772 368 : m_oLayerOCGListPoppler.push_back(
3773 736 : std::make_pair(osPostfixedName, ocg));
3774 368 : m_aoLayerWithRef.emplace_back(osPostfixedName.c_str(),
3775 736 : poObj->GetRefNum(), r.gen);
3776 : }
3777 : }
3778 : }
3779 : }
3780 : }
3781 :
3782 : /************************************************************************/
3783 : /* FindLayersPoppler() */
3784 : /************************************************************************/
3785 :
3786 214 : void PDFDataset::FindLayersPoppler(int iPageOfInterest)
3787 : {
3788 214 : int nPageCount = 0;
3789 214 : const auto poPages = GetPagesKids();
3790 214 : if (poPages)
3791 214 : nPageCount = poPages->GetLength();
3792 :
3793 : #if POPPLER_MAJOR_VERSION > 25 || \
3794 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3795 : const
3796 : #endif
3797 214 : OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig();
3798 214 : if (optContentConfig == nullptr || !optContentConfig->isOk())
3799 151 : return;
3800 :
3801 : #if POPPLER_MAJOR_VERSION > 25 || \
3802 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3803 : const
3804 : #endif
3805 63 : Array *array = optContentConfig->getOrderArray();
3806 63 : if (array)
3807 : {
3808 63 : GDALPDFArray *poArray = GDALPDFCreateArray(array);
3809 63 : int nVisited = 0;
3810 63 : bool bStop = false;
3811 63 : ExploreLayersPoppler(poArray, iPageOfInterest, nPageCount, CPLString(),
3812 : 0, nVisited, bStop);
3813 63 : delete poArray;
3814 : }
3815 : else
3816 : {
3817 0 : for (const auto &refOCGPair : optContentConfig->getOCGs())
3818 : {
3819 0 : auto ocg = refOCGPair.second.get();
3820 0 : if (ocg != nullptr && ocg->getName() != nullptr)
3821 : {
3822 : const char *pszLayerName =
3823 0 : reinterpret_cast<const char *>(ocg->getName()->c_str());
3824 0 : AddLayer(pszLayerName, 0);
3825 0 : m_oLayerOCGListPoppler.push_back(
3826 0 : std::make_pair(CPLString(pszLayerName), ocg));
3827 : }
3828 : }
3829 : }
3830 :
3831 63 : CreateLayerList();
3832 63 : m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS");
3833 : }
3834 :
3835 : /************************************************************************/
3836 : /* TurnLayersOnOffPoppler() */
3837 : /************************************************************************/
3838 :
3839 214 : void PDFDataset::TurnLayersOnOffPoppler()
3840 : {
3841 : #if POPPLER_MAJOR_VERSION > 25 || \
3842 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3843 : const
3844 : #endif
3845 214 : OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig();
3846 214 : if (optContentConfig == nullptr || !optContentConfig->isOk())
3847 151 : return;
3848 :
3849 : // Which layers to turn ON ?
3850 63 : const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr);
3851 63 : if (pszLayers)
3852 : {
3853 : int i;
3854 2 : int bAll = EQUAL(pszLayers, "ALL");
3855 12 : for (const auto &refOCGPair : optContentConfig->getOCGs())
3856 : {
3857 10 : auto ocg = refOCGPair.second.get();
3858 10 : ocg->setState((bAll) ? OptionalContentGroup::On
3859 : : OptionalContentGroup::Off);
3860 : }
3861 :
3862 2 : char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
3863 4 : for (i = 0; !bAll && papszLayers[i] != nullptr; i++)
3864 : {
3865 2 : bool isFound = false;
3866 12 : for (auto oIter2 = m_oLayerOCGListPoppler.begin();
3867 22 : oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2)
3868 : {
3869 10 : if (oIter2->first != papszLayers[i])
3870 8 : continue;
3871 :
3872 2 : isFound = true;
3873 2 : auto oIter = oIter2;
3874 2 : if (oIter->second)
3875 : {
3876 : // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
3877 2 : oIter->second->setState(OptionalContentGroup::On);
3878 : }
3879 :
3880 : // Turn child layers on, unless there's one of them explicitly
3881 : // listed in the list.
3882 2 : size_t nLen = strlen(papszLayers[i]);
3883 2 : int bFoundChildLayer = FALSE;
3884 2 : oIter = m_oLayerOCGListPoppler.begin();
3885 10 : for (;
3886 12 : oIter != m_oLayerOCGListPoppler.end() && !bFoundChildLayer;
3887 10 : ++oIter)
3888 : {
3889 10 : if (oIter->first.size() > nLen &&
3890 5 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) ==
3891 15 : 0 &&
3892 2 : oIter->first[nLen] == '.')
3893 : {
3894 4 : for (int j = 0; papszLayers[j] != nullptr; j++)
3895 : {
3896 2 : if (strcmp(papszLayers[j], oIter->first.c_str()) ==
3897 : 0)
3898 : {
3899 0 : bFoundChildLayer = TRUE;
3900 0 : break;
3901 : }
3902 : }
3903 : }
3904 : }
3905 :
3906 2 : if (!bFoundChildLayer)
3907 : {
3908 2 : oIter = m_oLayerOCGListPoppler.begin();
3909 12 : for (; oIter != m_oLayerOCGListPoppler.end() &&
3910 : !bFoundChildLayer;
3911 10 : ++oIter)
3912 : {
3913 10 : if (oIter->first.size() > nLen &&
3914 5 : strncmp(oIter->first.c_str(), papszLayers[i],
3915 15 : nLen) == 0 &&
3916 2 : oIter->first[nLen] == '.')
3917 : {
3918 2 : if (oIter->second)
3919 : {
3920 : // CPLDebug("PDF", "Turn '%s' on too",
3921 : // oIter->first.c_str());
3922 2 : oIter->second->setState(
3923 : OptionalContentGroup::On);
3924 : }
3925 : }
3926 : }
3927 : }
3928 :
3929 : // Turn parent layers on too
3930 6 : std::string layer(papszLayers[i]);
3931 : std::string::size_type j;
3932 3 : while ((j = layer.find_last_of('.')) != std::string::npos)
3933 : {
3934 1 : layer.resize(j);
3935 1 : oIter = m_oLayerOCGListPoppler.begin();
3936 6 : for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter)
3937 : {
3938 5 : if (oIter->first == layer && oIter->second)
3939 : {
3940 : // CPLDebug("PDF", "Turn '%s' on too",
3941 : // layer.c_str());
3942 1 : oIter->second->setState(OptionalContentGroup::On);
3943 : }
3944 : }
3945 : }
3946 : }
3947 2 : if (!isFound)
3948 : {
3949 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
3950 0 : papszLayers[i]);
3951 : }
3952 : }
3953 2 : CSLDestroy(papszLayers);
3954 :
3955 2 : m_bUseOCG = true;
3956 : }
3957 :
3958 : // Which layers to turn OFF ?
3959 : const char *pszLayersOFF =
3960 63 : GetOption(papszOpenOptions, "LAYERS_OFF", nullptr);
3961 63 : if (pszLayersOFF)
3962 : {
3963 5 : char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
3964 10 : for (int i = 0; papszLayersOFF[i] != nullptr; i++)
3965 : {
3966 5 : bool isFound = false;
3967 22 : for (auto oIter2 = m_oLayerOCGListPoppler.begin();
3968 39 : oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2)
3969 : {
3970 17 : if (oIter2->first != papszLayersOFF[i])
3971 12 : continue;
3972 :
3973 5 : isFound = true;
3974 5 : auto oIter = oIter2;
3975 5 : if (oIter->second)
3976 : {
3977 : // CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]);
3978 5 : oIter->second->setState(OptionalContentGroup::Off);
3979 : }
3980 :
3981 : // Turn child layers off too
3982 5 : size_t nLen = strlen(papszLayersOFF[i]);
3983 5 : oIter = m_oLayerOCGListPoppler.begin();
3984 22 : for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter)
3985 : {
3986 17 : if (oIter->first.size() > nLen &&
3987 3 : strncmp(oIter->first.c_str(), papszLayersOFF[i],
3988 20 : nLen) == 0 &&
3989 1 : oIter->first[nLen] == '.')
3990 : {
3991 1 : if (oIter->second)
3992 : {
3993 : // CPLDebug("PDF", "Turn '%s' off too",
3994 : // oIter->first.c_str());
3995 1 : oIter->second->setState(OptionalContentGroup::Off);
3996 : }
3997 : }
3998 : }
3999 : }
4000 5 : if (!isFound)
4001 : {
4002 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
4003 0 : papszLayersOFF[i]);
4004 : }
4005 : }
4006 5 : CSLDestroy(papszLayersOFF);
4007 :
4008 5 : m_bUseOCG = true;
4009 : }
4010 : }
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 428 : GDALPDFArray *PDFDataset::GetPagesKids()
4324 : {
4325 428 : const auto poCatalog = GetCatalog();
4326 428 : if (!poCatalog || poCatalog->GetType() != PDFObjectType_Dictionary)
4327 : {
4328 0 : return nullptr;
4329 : }
4330 428 : const auto poKids = poCatalog->LookupObject("Pages.Kids");
4331 428 : if (!poKids || poKids->GetType() != PDFObjectType_Array)
4332 : {
4333 0 : return nullptr;
4334 : }
4335 428 : return poKids->GetArray();
4336 : }
4337 :
4338 : /************************************************************************/
4339 : /* MapOCGsToPages() */
4340 : /************************************************************************/
4341 :
4342 214 : void PDFDataset::MapOCGsToPages()
4343 : {
4344 214 : const auto poKidsArray = GetPagesKids();
4345 214 : if (!poKidsArray)
4346 : {
4347 0 : return;
4348 : }
4349 214 : const int nKidsArrayLength = poKidsArray->GetLength();
4350 460 : for (int iPage = 0; iPage < nKidsArrayLength; ++iPage)
4351 : {
4352 246 : const auto poPage = poKidsArray->Get(iPage);
4353 246 : if (poPage && poPage->GetType() == PDFObjectType_Dictionary)
4354 : {
4355 246 : const auto poXObject = poPage->LookupObject("Resources.XObject");
4356 246 : if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary)
4357 : {
4358 530 : for (const auto &oNameObjectPair :
4359 1293 : poXObject->GetDictionary()->GetValues())
4360 : {
4361 : const auto poProperties =
4362 530 : oNameObjectPair.second->LookupObject(
4363 : "Resources.Properties");
4364 567 : if (poProperties &&
4365 37 : poProperties->GetType() == PDFObjectType_Dictionary)
4366 : {
4367 : const auto &oMap =
4368 37 : poProperties->GetDictionary()->GetValues();
4369 282 : for (const auto &[osKey, poObj] : oMap)
4370 : {
4371 490 : if (poObj->GetRefNum().toBool() &&
4372 245 : poObj->GetType() == PDFObjectType_Dictionary)
4373 : {
4374 : GDALPDFObject *poType =
4375 245 : poObj->GetDictionary()->Get("Type");
4376 : GDALPDFObject *poName =
4377 245 : poObj->GetDictionary()->Get("Name");
4378 490 : if (poType &&
4379 490 : poType->GetType() == PDFObjectType_Name &&
4380 980 : poType->GetName() == "OCG" && poName &&
4381 245 : poName->GetType() == PDFObjectType_String)
4382 : {
4383 : m_oMapOCGNumGenToPages
4384 245 : [std::pair(poObj->GetRefNum().toInt(),
4385 490 : poObj->GetRefGen())]
4386 245 : .push_back(iPage + 1);
4387 : }
4388 : }
4389 : }
4390 : }
4391 : }
4392 : }
4393 : }
4394 : }
4395 : }
4396 :
4397 : /************************************************************************/
4398 : /* FindLayerOCG() */
4399 : /************************************************************************/
4400 :
4401 197 : CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary *poPageDict,
4402 : const char *pszLayerName)
4403 : {
4404 : GDALPDFObject *poProperties =
4405 197 : poPageDict->LookupObject("Resources.Properties");
4406 240 : if (poProperties != nullptr &&
4407 43 : poProperties->GetType() == PDFObjectType_Dictionary)
4408 : {
4409 43 : const auto &oMap = poProperties->GetDictionary()->GetValues();
4410 122 : for (const auto &[osKey, poObj] : oMap)
4411 : {
4412 157 : if (poObj->GetRefNum().toBool() &&
4413 78 : poObj->GetType() == PDFObjectType_Dictionary)
4414 : {
4415 78 : GDALPDFObject *poType = poObj->GetDictionary()->Get("Type");
4416 78 : GDALPDFObject *poName = poObj->GetDictionary()->Get("Name");
4417 156 : if (poType != nullptr &&
4418 156 : poType->GetType() == PDFObjectType_Name &&
4419 312 : poType->GetName() == "OCG" && poName != nullptr &&
4420 78 : poName->GetType() == PDFObjectType_String)
4421 : {
4422 78 : if (poName->GetString() == pszLayerName)
4423 0 : return osKey;
4424 : }
4425 : }
4426 : }
4427 : }
4428 197 : return "";
4429 : }
4430 :
4431 : /************************************************************************/
4432 : /* FindLayersGeneric() */
4433 : /************************************************************************/
4434 :
4435 0 : void PDFDataset::FindLayersGeneric(GDALPDFDictionary *poPageDict)
4436 : {
4437 : GDALPDFObject *poProperties =
4438 0 : poPageDict->LookupObject("Resources.Properties");
4439 0 : if (poProperties != nullptr &&
4440 0 : poProperties->GetType() == PDFObjectType_Dictionary)
4441 : {
4442 0 : const auto &oMap = poProperties->GetDictionary()->GetValues();
4443 0 : for (const auto &[osKey, poObj] : oMap)
4444 : {
4445 0 : if (poObj->GetRefNum().toBool() &&
4446 0 : poObj->GetType() == PDFObjectType_Dictionary)
4447 : {
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 : {
4455 : m_aoLayerWithRef.emplace_back(
4456 0 : PDFSanitizeLayerName(poName->GetString().c_str())
4457 0 : .c_str(),
4458 0 : poObj->GetRefNum(), poObj->GetRefGen());
4459 : }
4460 : }
4461 : }
4462 : }
4463 0 : }
4464 :
4465 : /************************************************************************/
4466 : /* Open() */
4467 : /************************************************************************/
4468 :
4469 229 : PDFDataset *PDFDataset::Open(GDALOpenInfo *poOpenInfo)
4470 :
4471 : {
4472 229 : if (!PDFDatasetIdentify(poOpenInfo))
4473 1 : return nullptr;
4474 :
4475 : const char *pszUserPwd =
4476 228 : GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", nullptr);
4477 :
4478 228 : const bool bOpenSubdataset = STARTS_WITH(poOpenInfo->pszFilename, "PDF:");
4479 228 : const bool bOpenSubdatasetImage =
4480 228 : STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:");
4481 228 : int iPage = -1;
4482 228 : int nImageNum = -1;
4483 456 : std::string osSubdatasetName;
4484 228 : const char *pszFilename = poOpenInfo->pszFilename;
4485 :
4486 228 : if (bOpenSubdataset)
4487 : {
4488 15 : iPage = atoi(pszFilename + 4);
4489 15 : if (iPage <= 0)
4490 1 : return nullptr;
4491 14 : pszFilename = strchr(pszFilename + 4, ':');
4492 14 : if (pszFilename == nullptr)
4493 0 : return nullptr;
4494 14 : pszFilename++;
4495 14 : osSubdatasetName = CPLSPrintf("Page %d", iPage);
4496 : }
4497 213 : else if (bOpenSubdatasetImage)
4498 : {
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 : }
4514 : else
4515 213 : iPage = 1;
4516 :
4517 227 : std::bitset<PDFLIB_COUNT> bHasLib;
4518 227 : bHasLib.reset();
4519 : // Each library set their flag
4520 : #if defined(HAVE_POPPLER)
4521 227 : bHasLib.set(PDFLIB_POPPLER);
4522 : #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 227 : std::bitset<PDFLIB_COUNT> bUseLib;
4531 :
4532 : // More than one library available
4533 : // Detect which one
4534 227 : if (bHasLib.count() != 1)
4535 : {
4536 0 : const char *pszDefaultLib = bHasLib.test(PDFLIB_PDFIUM) ? "PDFIUM"
4537 0 : : bHasLib.test(PDFLIB_POPPLER) ? "POPPLER"
4538 0 : : "PODOFO";
4539 : const char *pszPDFLib =
4540 0 : GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", pszDefaultLib);
4541 : while (true)
4542 : {
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 : {
4552 0 : CPLDebug("PDF",
4553 : "Invalid value for GDAL_PDF_LIB config option: %s. "
4554 : "Fallback to %s",
4555 : pszPDFLib, pszDefaultLib);
4556 0 : pszPDFLib = pszDefaultLib;
4557 0 : bUseLib.reset();
4558 : }
4559 : else
4560 0 : break;
4561 : }
4562 : }
4563 : else
4564 227 : bUseLib = bHasLib;
4565 :
4566 227 : GDALPDFObject *poPageObj = nullptr;
4567 : #ifdef HAVE_POPPLER
4568 227 : PDFDoc *poDocPoppler = nullptr;
4569 227 : Page *poPagePoppler = nullptr;
4570 227 : Catalog *poCatalogPoppler = nullptr;
4571 : #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 227 : int nPages = 0;
4581 227 : VSIVirtualHandleUniquePtr fp;
4582 :
4583 : #ifdef HAVE_POPPLER
4584 227 : if (bUseLib.test(PDFLIB_POPPLER))
4585 : {
4586 : static bool globalParamsCreatedByGDAL = false;
4587 : {
4588 454 : CPLMutexHolderD(&hGlobalParamsMutex);
4589 : /* poppler global variable */
4590 227 : if (globalParams == nullptr)
4591 : {
4592 6 : globalParamsCreatedByGDAL = true;
4593 6 : globalParams.reset(new GlobalParams());
4594 : }
4595 :
4596 227 : globalParams->setPrintCommands(CPLTestBool(
4597 : CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE")));
4598 : }
4599 :
4600 456 : const auto registerErrorCallback = []()
4601 : {
4602 : /* Set custom error handler for poppler errors */
4603 456 : setErrorCallback(PDFDatasetErrorFunction);
4604 456 : assert(globalParams); // avoid CSA false positive
4605 456 : globalParams->setErrQuiet(false);
4606 456 : };
4607 :
4608 227 : fp.reset(VSIFOpenL(pszFilename, "rb"));
4609 227 : if (!fp)
4610 13 : return nullptr;
4611 :
4612 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
4613 : {
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 : GByte *pabyRet = nullptr;
4618 : vsi_l_offset nSize = 0;
4619 : if (VSIIngestFile(fp.get(), pszFilename, &pabyRet, &nSize,
4620 : 10 * 1024 * 1024))
4621 : {
4622 : // Replace nul byte by something else so that strstr() works
4623 : for (size_t i = 0; i < nSize; i++)
4624 : {
4625 : if (pabyRet[i] == 0)
4626 : pabyRet[i] = ' ';
4627 : }
4628 : if (strstr(reinterpret_cast<const char *>(pabyRet),
4629 : "/JBIG2Decode"))
4630 : {
4631 : CPLError(CE_Failure, CPLE_AppDefined,
4632 : "/JBIG2Decode found. Giving up due to potential "
4633 : "very long processing time.");
4634 : CPLFree(pabyRet);
4635 : return nullptr;
4636 : }
4637 : }
4638 : CPLFree(pabyRet);
4639 : }
4640 : #endif
4641 :
4642 226 : fp.reset(VSICreateBufferedReaderHandle(fp.release()));
4643 : while (true)
4644 : {
4645 228 : fp->Seek(0, SEEK_SET);
4646 228 : g_nPopplerErrors = 0;
4647 228 : if (globalParamsCreatedByGDAL)
4648 228 : registerErrorCallback();
4649 228 : Object oObj;
4650 : auto poStream =
4651 228 : new VSIPDFFileStream(fp.get(), pszFilename, std::move(oObj));
4652 : #if POPPLER_MAJOR_VERSION > 22 || \
4653 : (POPPLER_MAJOR_VERSION == 22 && POPPLER_MINOR_VERSION > 2)
4654 : std::optional<GooString> osUserPwd;
4655 : if (pszUserPwd)
4656 : osUserPwd = std::optional<GooString>(pszUserPwd);
4657 : try
4658 : {
4659 : poDocPoppler =
4660 : new PDFDoc(poStream, std::optional<GooString>(), osUserPwd);
4661 : }
4662 : catch (const std::exception &e)
4663 : {
4664 : CPLError(CE_Failure, CPLE_AppDefined,
4665 : "PDFDoc::PDFDoc() failed with %s", e.what());
4666 : return nullptr;
4667 : }
4668 : #else
4669 228 : GooString *poUserPwd = nullptr;
4670 228 : if (pszUserPwd)
4671 6 : poUserPwd = new GooString(pszUserPwd);
4672 228 : poDocPoppler = new PDFDoc(poStream, nullptr, poUserPwd);
4673 228 : delete poUserPwd;
4674 : #endif
4675 228 : if (globalParamsCreatedByGDAL)
4676 228 : registerErrorCallback();
4677 228 : if (g_nPopplerErrors >= MAX_POPPLER_ERRORS)
4678 : {
4679 0 : PDFFreeDoc(poDocPoppler);
4680 0 : return nullptr;
4681 : }
4682 :
4683 228 : if (!poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0)
4684 : {
4685 13 : if (poDocPoppler->getErrorCode() == errEncrypted)
4686 : {
4687 5 : if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
4688 : {
4689 : pszUserPwd =
4690 2 : PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd);
4691 2 : 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 2 : CPLErrorReset();
4697 :
4698 2 : continue;
4699 : }
4700 3 : else if (pszUserPwd == nullptr)
4701 : {
4702 1 : CPLError(CE_Failure, CPLE_AppDefined,
4703 : "A password is needed. You can specify it "
4704 : "through the PDF_USER_PWD "
4705 : "configuration option / USER_PWD open option "
4706 : "(that can be set to ASK_INTERACTIVE)");
4707 : }
4708 : else
4709 : {
4710 2 : CPLError(CE_Failure, CPLE_AppDefined,
4711 : "Invalid password");
4712 : }
4713 : }
4714 : else
4715 : {
4716 8 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4717 : }
4718 :
4719 11 : PDFFreeDoc(poDocPoppler);
4720 11 : return nullptr;
4721 : }
4722 215 : else if (poDocPoppler->isLinearized() &&
4723 0 : !poStream->FoundLinearizedHint())
4724 : {
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 : }
4737 : else
4738 : {
4739 215 : break;
4740 : }
4741 2 : }
4742 :
4743 215 : poCatalogPoppler = poDocPoppler->getCatalog();
4744 215 : if (poCatalogPoppler == nullptr || !poCatalogPoppler->isOk())
4745 : {
4746 0 : CPLError(CE_Failure, CPLE_AppDefined,
4747 : "Invalid PDF : invalid catalog");
4748 0 : PDFFreeDoc(poDocPoppler);
4749 0 : return nullptr;
4750 : }
4751 :
4752 215 : nPages = poDocPoppler->getNumPages();
4753 :
4754 215 : if (iPage == 1 && nPages > 10000 &&
4755 0 : CPLTestBool(CPLGetConfigOption("GDAL_PDF_LIMIT_PAGE_COUNT", "YES")))
4756 : {
4757 0 : CPLError(CE_Warning, CPLE_AppDefined,
4758 : "This PDF document reports %d pages. "
4759 : "Limiting count to 10000 for performance reasons. "
4760 : "You may remove this limit by setting the "
4761 : "GDAL_PDF_LIMIT_PAGE_COUNT configuration option to NO",
4762 : nPages);
4763 0 : nPages = 10000;
4764 : }
4765 :
4766 215 : if (iPage < 1 || iPage > nPages)
4767 : {
4768 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
4769 : iPage, nPages);
4770 1 : PDFFreeDoc(poDocPoppler);
4771 1 : return nullptr;
4772 : }
4773 :
4774 : /* Sanity check to validate page count */
4775 214 : if (iPage > 1 && nPages <= 10000 && iPage != nPages)
4776 : {
4777 4 : poPagePoppler = poCatalogPoppler->getPage(nPages);
4778 4 : if (poPagePoppler == nullptr || !poPagePoppler->isOk())
4779 : {
4780 0 : CPLError(CE_Failure, CPLE_AppDefined,
4781 : "Invalid PDF : invalid page count");
4782 0 : PDFFreeDoc(poDocPoppler);
4783 0 : return nullptr;
4784 : }
4785 : }
4786 :
4787 214 : poPagePoppler = poCatalogPoppler->getPage(iPage);
4788 214 : if (poPagePoppler == nullptr || !poPagePoppler->isOk())
4789 : {
4790 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
4791 0 : PDFFreeDoc(poDocPoppler);
4792 0 : return nullptr;
4793 : }
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 214 : const Object &oPageObj = poPagePoppler->pageObj;
4802 : #endif
4803 214 : if (!oPageObj.isDict())
4804 : {
4805 0 : CPLError(CE_Failure, CPLE_AppDefined,
4806 : "Invalid PDF : !oPageObj.isDict()");
4807 0 : PDFFreeDoc(poDocPoppler);
4808 0 : return nullptr;
4809 : }
4810 :
4811 214 : poPageObj = new GDALPDFObjectPoppler(&oPageObj);
4812 214 : Ref *poPageRef = poCatalogPoppler->getPageRef(iPage);
4813 214 : if (poPageRef != nullptr)
4814 : {
4815 428 : cpl::down_cast<GDALPDFObjectPoppler *>(poPageObj)->SetRefNumAndGen(
4816 428 : GDALPDFObjectNum(poPageRef->num), poPageRef->gen);
4817 : }
4818 : }
4819 : #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 214 : if (poPageObj == nullptr)
4992 0 : return nullptr;
4993 214 : GDALPDFDictionary *poPageDict = poPageObj->GetDictionary();
4994 214 : if (poPageDict == nullptr)
4995 : {
4996 0 : delete poPageObj;
4997 :
4998 0 : CPLError(CE_Failure, CPLE_AppDefined,
4999 : "Invalid PDF : poPageDict == nullptr");
5000 : #ifdef HAVE_POPPLER
5001 0 : if (bUseLib.test(PDFLIB_POPPLER))
5002 0 : PDFFreeDoc(poDocPoppler);
5003 : #endif
5004 : #ifdef HAVE_PDFIUM
5005 : if (bUseLib.test(PDFLIB_PDFIUM))
5006 : {
5007 : UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium);
5008 : }
5009 : #endif
5010 0 : return nullptr;
5011 : }
5012 :
5013 214 : const char *pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", nullptr);
5014 214 : if (pszDumpObject != nullptr)
5015 : {
5016 2 : GDALPDFDumper oDumper(pszFilename, pszDumpObject);
5017 1 : oDumper.Dump(poPageObj);
5018 : }
5019 :
5020 214 : PDFDataset *poDS = new PDFDataset();
5021 214 : poDS->m_fp = std::move(fp);
5022 214 : poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
5023 214 : poDS->m_bUseLib = bUseLib;
5024 214 : poDS->m_osFilename = pszFilename;
5025 214 : poDS->eAccess = poOpenInfo->eAccess;
5026 :
5027 214 : if (nPages > 1 && !bOpenSubdataset)
5028 : {
5029 : int i;
5030 4 : CPLStringList aosList;
5031 8 : for (i = 0; i < nPages; i++)
5032 : {
5033 : char szKey[32];
5034 6 : snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_NAME", i + 1);
5035 : aosList.AddNameValue(
5036 6 : szKey, CPLSPrintf("PDF:%d:%s", i + 1, poOpenInfo->pszFilename));
5037 6 : snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_DESC", i + 1);
5038 : aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i + 1,
5039 6 : poOpenInfo->pszFilename));
5040 : }
5041 2 : poDS->SetMetadata(aosList.List(), "SUBDATASETS");
5042 : }
5043 :
5044 : #ifdef HAVE_POPPLER
5045 214 : poDS->m_poDocPoppler = poDocPoppler;
5046 : #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 214 : poDS->m_poPageObj = poPageObj;
5055 214 : poDS->m_osUserPwd = pszUserPwd ? pszUserPwd : "";
5056 214 : poDS->m_iPage = iPage;
5057 :
5058 : const char *pszDumpCatalog =
5059 214 : CPLGetConfigOption("PDF_DUMP_CATALOG", nullptr);
5060 214 : if (pszDumpCatalog != nullptr)
5061 : {
5062 0 : GDALPDFDumper oDumper(pszFilename, pszDumpCatalog);
5063 0 : auto poCatalog = poDS->GetCatalog();
5064 0 : if (poCatalog)
5065 0 : oDumper.Dump(poCatalog);
5066 : }
5067 :
5068 214 : int nBandsGuessed = 0;
5069 214 : if (nImageNum < 0)
5070 : {
5071 214 : poDS->GuessDPI(poPageDict, &nBandsGuessed);
5072 214 : if (nBandsGuessed < 4)
5073 207 : nBandsGuessed = 0;
5074 : }
5075 : else
5076 : {
5077 : const char *pszDPI =
5078 0 : GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr);
5079 0 : if (pszDPI != nullptr)
5080 : {
5081 : // coverity[tainted_data]
5082 0 : poDS->m_dfDPI = CPLAtof(pszDPI);
5083 : }
5084 : }
5085 :
5086 214 : double dfX1 = 0.0;
5087 214 : double dfY1 = 0.0;
5088 214 : double dfX2 = 0.0;
5089 214 : double dfY2 = 0.0;
5090 :
5091 : #ifdef HAVE_POPPLER
5092 214 : if (bUseLib.test(PDFLIB_POPPLER))
5093 : {
5094 214 : const auto *psMediaBox = poPagePoppler->getMediaBox();
5095 214 : dfX1 = psMediaBox->x1;
5096 214 : dfY1 = psMediaBox->y1;
5097 214 : dfX2 = psMediaBox->x2;
5098 214 : dfY2 = psMediaBox->y2;
5099 : }
5100 : #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 214 : double dfUserUnit = poDS->m_dfDPI * USER_UNIT_IN_INCH;
5133 214 : poDS->m_dfPageWidth = dfX2 - dfX1;
5134 214 : poDS->m_dfPageHeight = dfY2 - dfY1;
5135 : // CPLDebug("PDF", "left=%f right=%f bottom=%f top=%f", dfX1, dfX2, dfY1,
5136 : // dfY2);
5137 214 : const double dfXSize = floor((dfX2 - dfX1) * dfUserUnit + 0.5);
5138 214 : const double dfYSize = floor((dfY2 - dfY1) * dfUserUnit + 0.5);
5139 214 : if (!(dfXSize >= 0 && dfXSize <= INT_MAX && dfYSize >= 0 &&
5140 214 : dfYSize <= INT_MAX))
5141 : {
5142 0 : delete poDS;
5143 0 : return nullptr;
5144 : }
5145 214 : poDS->nRasterXSize = static_cast<int>(dfXSize);
5146 214 : poDS->nRasterYSize = static_cast<int>(dfYSize);
5147 :
5148 214 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
5149 : {
5150 0 : delete poDS;
5151 0 : return nullptr;
5152 : }
5153 :
5154 214 : double dfRotation = 0;
5155 : #ifdef HAVE_POPPLER
5156 214 : if (bUseLib.test(PDFLIB_POPPLER))
5157 214 : dfRotation = poDocPoppler->getPageRotate(iPage);
5158 : #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 214 : if (dfRotation == 90 || dfRotation == -90 || dfRotation == 270)
5183 : {
5184 : /* FIXME: the podofo case should be implemented. This needs to rotate */
5185 : /* the output of pdftoppm */
5186 : #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
5187 0 : if (bUseLib.test(PDFLIB_POPPLER) || bUseLib.test(PDFLIB_PDFIUM))
5188 : {
5189 0 : int nTmp = poDS->nRasterXSize;
5190 0 : poDS->nRasterXSize = poDS->nRasterYSize;
5191 0 : poDS->nRasterYSize = nTmp;
5192 : }
5193 : #endif
5194 : }
5195 :
5196 214 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW"))
5197 : {
5198 2 : poDS->m_nBlockXSize = 512;
5199 2 : poDS->m_nBlockYSize = 512;
5200 : }
5201 : /* Check if the PDF is only made of regularly tiled images */
5202 : /* (like some USGS GeoPDF production) */
5203 370 : else if (dfRotation == 0.0 && !poDS->m_asTiles.empty() &&
5204 158 : EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"),
5205 : "ALL"))
5206 : {
5207 158 : poDS->CheckTiledRaster();
5208 158 : if (!poDS->m_aiTiles.empty())
5209 9 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
5210 : }
5211 :
5212 214 : GDALPDFObject *poLGIDict = nullptr;
5213 214 : GDALPDFObject *poVP = nullptr;
5214 214 : int bIsOGCBP = FALSE;
5215 214 : if ((poLGIDict = poPageDict->Get("LGIDict")) != nullptr && nImageNum < 0)
5216 : {
5217 : /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */
5218 0 : CPLDebug("PDF", "OGC Encoding Best Practice style detected");
5219 0 : if (poDS->ParseLGIDictObject(poLGIDict))
5220 : {
5221 0 : if (poDS->m_bHasCTM)
5222 : {
5223 0 : if (dfRotation == 90)
5224 : {
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 : }
5232 0 : else if (dfRotation == -90 || dfRotation == 270)
5233 : {
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 : }
5245 : else
5246 : {
5247 0 : poDS->m_gt[0] = poDS->m_adfCTM[4] +
5248 0 : poDS->m_adfCTM[2] * dfY2 +
5249 0 : poDS->m_adfCTM[0] * dfX1;
5250 0 : poDS->m_gt[1] = poDS->m_adfCTM[0] / dfUserUnit;
5251 0 : poDS->m_gt[2] = -poDS->m_adfCTM[2] / dfUserUnit;
5252 0 : poDS->m_gt[3] = poDS->m_adfCTM[5] +
5253 0 : poDS->m_adfCTM[3] * dfY2 +
5254 0 : poDS->m_adfCTM[1] * dfX1;
5255 0 : poDS->m_gt[4] = poDS->m_adfCTM[1] / dfUserUnit;
5256 0 : poDS->m_gt[5] = -poDS->m_adfCTM[3] / dfUserUnit;
5257 : }
5258 :
5259 0 : poDS->m_bGeoTransformValid = true;
5260 : }
5261 :
5262 0 : bIsOGCBP = TRUE;
5263 :
5264 : int i;
5265 0 : for (i = 0; i < poDS->m_nGCPCount; i++)
5266 : {
5267 0 : if (dfRotation == 90)
5268 : {
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 : }
5276 0 : else if (dfRotation == -90 || dfRotation == 270)
5277 : {
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 : }
5287 : else
5288 : {
5289 0 : poDS->m_pasGCPList[i].dfGCPPixel =
5290 0 : (-dfX1 + poDS->m_pasGCPList[i].dfGCPPixel) * dfUserUnit;
5291 0 : poDS->m_pasGCPList[i].dfGCPLine =
5292 0 : (dfY2 - poDS->m_pasGCPList[i].dfGCPLine) * dfUserUnit;
5293 : }
5294 : }
5295 : }
5296 : }
5297 214 : else if ((poVP = poPageDict->Get("VP")) != nullptr && nImageNum < 0)
5298 : {
5299 : /* Cf adobe_supplement_iso32000.pdf */
5300 155 : CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?");
5301 155 : if (dfX1 != 0 || dfY1 != 0)
5302 : {
5303 0 : CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case...");
5304 : }
5305 155 : poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1);
5306 : }
5307 : else
5308 : {
5309 : GDALPDFObject *poXObject =
5310 59 : poPageDict->LookupObject("Resources.XObject");
5311 :
5312 116 : if (poXObject != nullptr &&
5313 57 : poXObject->GetType() == PDFObjectType_Dictionary)
5314 : {
5315 57 : GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary();
5316 57 : const auto &oMap = poXObjectDict->GetValues();
5317 57 : int nSubDataset = 0;
5318 231 : for (const auto &[osKey, poObj] : oMap)
5319 : {
5320 174 : if (poObj->GetType() == PDFObjectType_Dictionary)
5321 : {
5322 174 : GDALPDFDictionary *poDict = poObj->GetDictionary();
5323 174 : GDALPDFObject *poSubtype = nullptr;
5324 174 : GDALPDFObject *poMeasure = nullptr;
5325 174 : GDALPDFObject *poWidth = nullptr;
5326 174 : GDALPDFObject *poHeight = nullptr;
5327 174 : int nW = 0;
5328 174 : int nH = 0;
5329 174 : if ((poSubtype = poDict->Get("Subtype")) != nullptr &&
5330 348 : poSubtype->GetType() == PDFObjectType_Name &&
5331 174 : poSubtype->GetName() == "Image" &&
5332 129 : (poMeasure = poDict->Get("Measure")) != nullptr &&
5333 0 : 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 348 : poHeight->GetType() == PDFObjectType_Int &&
5339 0 : (nH = poHeight->GetInt()) > 0)
5340 : {
5341 0 : if (nImageNum < 0)
5342 0 : CPLDebug("PDF",
5343 : "Measure found on Image object (%d)",
5344 0 : poObj->GetRefNum().toInt());
5345 :
5346 0 : GDALPDFObject *poColorSpace = poDict->Get("ColorSpace");
5347 : 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 : {
5360 0 : if (nImageNum < 0)
5361 : {
5362 0 : nSubDataset++;
5363 0 : poDS->SetMetadataItem(
5364 : CPLSPrintf("SUBDATASET_%d_NAME",
5365 : nSubDataset),
5366 : CPLSPrintf("PDF_IMAGE:%d:%d:%s", iPage,
5367 0 : poObj->GetRefNum().toInt(),
5368 : pszFilename),
5369 : "SUBDATASETS");
5370 0 : poDS->SetMetadataItem(
5371 : CPLSPrintf("SUBDATASET_%d_DESC",
5372 : nSubDataset),
5373 : CPLSPrintf("Georeferenced image of size "
5374 : "%dx%d of page %d of %s",
5375 : nW, nH, iPage, pszFilename),
5376 : "SUBDATASETS");
5377 : }
5378 0 : else if (poObj->GetRefNum().toInt() == nImageNum)
5379 : {
5380 0 : poDS->nRasterXSize = nW;
5381 0 : poDS->nRasterYSize = nH;
5382 0 : poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW,
5383 : 0);
5384 0 : poDS->m_poImageObj = poObj;
5385 0 : if (poColorSpace->GetName() == "DeviceGray")
5386 0 : nBandsGuessed = 1;
5387 0 : break;
5388 : }
5389 : }
5390 : }
5391 : }
5392 : }
5393 : }
5394 :
5395 59 : if (nImageNum >= 0 && poDS->m_poImageObj == nullptr)
5396 : {
5397 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d",
5398 : nImageNum);
5399 0 : delete poDS;
5400 0 : return nullptr;
5401 : }
5402 :
5403 : /* Not a geospatial PDF doc */
5404 : }
5405 :
5406 : /* If pixel size or top left coordinates are very close to an int, round
5407 : * them to the int */
5408 : double dfEps =
5409 214 : (fabs(poDS->m_gt[0]) > 1e5 && fabs(poDS->m_gt[3]) > 1e5) ? 1e-5 : 1e-8;
5410 214 : poDS->m_gt[0] = ROUND_IF_CLOSE(poDS->m_gt[0], dfEps);
5411 214 : poDS->m_gt[1] = ROUND_IF_CLOSE(poDS->m_gt[1]);
5412 214 : poDS->m_gt[3] = ROUND_IF_CLOSE(poDS->m_gt[3], dfEps);
5413 214 : poDS->m_gt[5] = ROUND_IF_CLOSE(poDS->m_gt[5]);
5414 :
5415 214 : if (bUseLib.test(PDFLIB_PDFIUM))
5416 : {
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 : {
5429 0 : for (int i = 0; i < 6; i++)
5430 : {
5431 0 : poDS->m_gt[i] = std::round(poDS->m_gt[i]);
5432 : }
5433 : }
5434 : }
5435 :
5436 214 : if (poDS->m_poNeatLine)
5437 : {
5438 154 : char *pszNeatLineWkt = nullptr;
5439 154 : OGRLinearRing *poRing = poDS->m_poNeatLine->getExteriorRing();
5440 : /* Adobe style is already in target SRS units */
5441 154 : if (bIsOGCBP)
5442 : {
5443 0 : int nPoints = poRing->getNumPoints();
5444 : int i;
5445 :
5446 0 : for (i = 0; i < nPoints; i++)
5447 : {
5448 : double x, y;
5449 0 : if (dfRotation == 90.0)
5450 : {
5451 0 : x = poRing->getY(i) * dfUserUnit;
5452 0 : y = poRing->getX(i) * dfUserUnit;
5453 : }
5454 0 : else if (dfRotation == -90.0 || dfRotation == 270.0)
5455 : {
5456 0 : x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit;
5457 0 : y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit;
5458 : }
5459 : else
5460 : {
5461 0 : x = (-dfX1 + poRing->getX(i)) * dfUserUnit;
5462 0 : y = (dfY2 - poRing->getY(i)) * dfUserUnit;
5463 : }
5464 : double X =
5465 0 : poDS->m_gt[0] + x * poDS->m_gt[1] + y * poDS->m_gt[2];
5466 : double Y =
5467 0 : poDS->m_gt[3] + x * poDS->m_gt[4] + y * poDS->m_gt[5];
5468 0 : poRing->setPoint(i, X, Y);
5469 : }
5470 : }
5471 154 : poRing->closeRings();
5472 :
5473 154 : poDS->m_poNeatLine->exportToWkt(&pszNeatLineWkt);
5474 154 : if (nImageNum < 0)
5475 154 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
5476 154 : CPLFree(pszNeatLineWkt);
5477 : }
5478 :
5479 214 : poDS->MapOCGsToPages();
5480 :
5481 : #ifdef HAVE_POPPLER
5482 214 : if (bUseLib.test(PDFLIB_POPPLER))
5483 : {
5484 214 : auto poMetadata = poCatalogPoppler->readMetadata();
5485 214 : if (poMetadata)
5486 : {
5487 19 : const char *pszContent = poMetadata->c_str();
5488 19 : if (pszContent != nullptr &&
5489 19 : STARTS_WITH(pszContent, "<?xpacket begin="))
5490 : {
5491 19 : const char *const apszMDList[2] = {pszContent, nullptr};
5492 19 : poDS->SetMetadata(const_cast<char **>(apszMDList), "xml:XMP");
5493 : }
5494 : #if (POPPLER_MAJOR_VERSION < 21 || \
5495 : (POPPLER_MAJOR_VERSION == 21 && POPPLER_MINOR_VERSION < 10))
5496 19 : delete poMetadata;
5497 : #endif
5498 : }
5499 :
5500 : /* Read Info object */
5501 : /* The test is necessary since with some corrupted PDFs
5502 : * poDocPoppler->getDocInfo() */
5503 : /* might abort() */
5504 214 : if (poDocPoppler->getXRef()->isOk())
5505 : {
5506 428 : Object oInfo = poDocPoppler->getDocInfo();
5507 428 : GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE);
5508 214 : poDS->ParseInfo(&oInfoObjPoppler);
5509 : }
5510 :
5511 : /* Find layers */
5512 416 : poDS->FindLayersPoppler(
5513 202 : (bOpenSubdataset || bOpenSubdatasetImage) ? iPage : 0);
5514 :
5515 : /* Turn user specified layers on or off */
5516 214 : poDS->TurnLayersOnOffPoppler();
5517 : }
5518 : #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 214 : 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 214 : if (nBandsGuessed)
5609 7 : nBands = nBandsGuessed;
5610 : const char *pszPDFBands =
5611 214 : GetOption(poOpenInfo->papszOpenOptions, "BANDS", nullptr);
5612 214 : if (pszPDFBands)
5613 : {
5614 2 : nBands = atoi(pszPDFBands);
5615 2 : if (nBands != 3 && nBands != 4)
5616 : {
5617 0 : CPLError(CE_Warning, CPLE_NotSupported,
5618 : "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback");
5619 0 : nBands = 3;
5620 : }
5621 : }
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 : int iBand;
5634 863 : for (iBand = 1; iBand <= nBands; iBand++)
5635 : {
5636 649 : if (poDS->m_poImageObj != nullptr)
5637 0 : poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand));
5638 : else
5639 649 : poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand, 0));
5640 : }
5641 :
5642 : /* Check if this is a raster-only PDF file and that we are */
5643 : /* opened in vector-only mode */
5644 492 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
5645 227 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
5646 13 : !poDS->OpenVectorLayers(poPageDict))
5647 : {
5648 0 : CPLDebug("PDF", "This is a raster-only PDF dataset, "
5649 : "but it has been opened in vector-only mode");
5650 : /* Clear dirty flag */
5651 0 : poDS->m_bProjDirty = false;
5652 0 : poDS->m_bNeatLineDirty = false;
5653 0 : poDS->m_bInfoDirty = false;
5654 0 : poDS->m_bXMPDirty = false;
5655 0 : delete poDS;
5656 0 : return nullptr;
5657 : }
5658 :
5659 : /* -------------------------------------------------------------------- */
5660 : /* Initialize any PAM information. */
5661 : /* -------------------------------------------------------------------- */
5662 214 : if (bOpenSubdataset || bOpenSubdatasetImage)
5663 : {
5664 12 : poDS->SetPhysicalFilename(pszFilename);
5665 12 : poDS->SetSubdatasetName(osSubdatasetName.c_str());
5666 : }
5667 : else
5668 : {
5669 202 : poDS->SetDescription(poOpenInfo->pszFilename);
5670 : }
5671 :
5672 214 : poDS->TryLoadXML();
5673 :
5674 : /* -------------------------------------------------------------------- */
5675 : /* Support overviews. */
5676 : /* -------------------------------------------------------------------- */
5677 214 : if (!CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW"))
5678 : {
5679 212 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
5680 : }
5681 :
5682 : /* Clear dirty flag */
5683 214 : poDS->m_bProjDirty = false;
5684 214 : poDS->m_bNeatLineDirty = false;
5685 214 : poDS->m_bInfoDirty = false;
5686 214 : poDS->m_bXMPDirty = false;
5687 :
5688 214 : return (poDS);
5689 : }
5690 :
5691 : /************************************************************************/
5692 : /* ParseLGIDictObject() */
5693 : /************************************************************************/
5694 :
5695 0 : int PDFDataset::ParseLGIDictObject(GDALPDFObject *poLGIDict)
5696 : {
5697 0 : bool bOK = false;
5698 0 : if (poLGIDict->GetType() == PDFObjectType_Array)
5699 : {
5700 0 : GDALPDFArray *poArray = poLGIDict->GetArray();
5701 0 : int nArrayLength = poArray->GetLength();
5702 0 : int iMax = -1;
5703 0 : GDALPDFObject *poArrayElt = nullptr;
5704 0 : for (int i = 0; i < nArrayLength; i++)
5705 : {
5706 0 : if ((poArrayElt = poArray->Get(i)) == nullptr ||
5707 0 : poArrayElt->GetType() != PDFObjectType_Dictionary)
5708 : {
5709 0 : CPLError(CE_Failure, CPLE_AppDefined,
5710 : "LGIDict[%d] is not a dictionary", i);
5711 0 : return FALSE;
5712 : }
5713 :
5714 0 : int bIsBestCandidate = FALSE;
5715 0 : if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(),
5716 0 : &bIsBestCandidate))
5717 : {
5718 0 : if (bIsBestCandidate || iMax < 0)
5719 0 : iMax = i;
5720 : }
5721 : }
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 : }
5730 0 : else if (poLGIDict->GetType() == PDFObjectType_Dictionary)
5731 : {
5732 0 : bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) &&
5733 0 : ParseLGIDictDictSecondPass(poLGIDict->GetDictionary());
5734 : }
5735 : else
5736 : {
5737 0 : CPLError(CE_Failure, CPLE_AppDefined, "LGIDict is of type %s",
5738 0 : poLGIDict->GetTypeName());
5739 : }
5740 :
5741 0 : return bOK;
5742 : }
5743 :
5744 : /************************************************************************/
5745 : /* Get() */
5746 : /************************************************************************/
5747 :
5748 10995 : static double Get(GDALPDFObject *poObj, int nIndice)
5749 : {
5750 10995 : if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0)
5751 : {
5752 4916 : poObj = poObj->GetArray()->Get(nIndice);
5753 4916 : if (poObj == nullptr)
5754 0 : return 0;
5755 4916 : return Get(poObj);
5756 : }
5757 6079 : else if (poObj->GetType() == PDFObjectType_Int)
5758 4871 : return poObj->GetInt();
5759 1208 : else if (poObj->GetType() == PDFObjectType_Real)
5760 1208 : return poObj->GetReal();
5761 0 : else if (poObj->GetType() == PDFObjectType_String)
5762 : {
5763 0 : const char *pszStr = poObj->GetString().c_str();
5764 0 : size_t nLen = strlen(pszStr);
5765 0 : if (nLen == 0)
5766 0 : return 0;
5767 : /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W"
5768 : */
5769 0 : char chLast = pszStr[nLen - 1];
5770 0 : if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
5771 : {
5772 0 : double dfDeg = CPLAtof(pszStr);
5773 0 : double dfMin = 0.0;
5774 0 : double dfSec = 0.0;
5775 0 : const char *pszNext = strchr(pszStr, ' ');
5776 0 : if (pszNext)
5777 0 : pszNext++;
5778 0 : if (pszNext)
5779 0 : dfMin = CPLAtof(pszNext);
5780 0 : if (pszNext)
5781 0 : pszNext = strchr(pszNext, ' ');
5782 0 : if (pszNext)
5783 0 : pszNext++;
5784 0 : if (pszNext)
5785 0 : dfSec = CPLAtof(pszNext);
5786 0 : double dfVal = dfDeg + dfMin / 60 + dfSec / 3600;
5787 0 : if (chLast == 'W' || chLast == 'S')
5788 0 : return -dfVal;
5789 : else
5790 0 : return dfVal;
5791 : }
5792 0 : return CPLAtof(pszStr);
5793 : }
5794 : else
5795 : {
5796 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s",
5797 0 : poObj->GetTypeName());
5798 0 : return 0;
5799 : }
5800 : }
5801 :
5802 : /************************************************************************/
5803 : /* Get() */
5804 : /************************************************************************/
5805 :
5806 0 : static double Get(GDALPDFDictionary *poDict, const char *pszName)
5807 : {
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 : }
5814 :
5815 : /************************************************************************/
5816 : /* ParseLGIDictDictFirstPass() */
5817 : /************************************************************************/
5818 :
5819 0 : int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary *poLGIDict,
5820 : int *pbIsBestCandidate)
5821 : {
5822 0 : if (pbIsBestCandidate)
5823 0 : *pbIsBestCandidate = FALSE;
5824 :
5825 0 : if (poLGIDict == nullptr)
5826 0 : return FALSE;
5827 :
5828 : /* -------------------------------------------------------------------- */
5829 : /* Extract Type attribute */
5830 : /* -------------------------------------------------------------------- */
5831 0 : GDALPDFObject *poType = poLGIDict->Get("Type");
5832 0 : if (poType == nullptr)
5833 : {
5834 0 : CPLError(CE_Failure, CPLE_AppDefined,
5835 : "Cannot find Type of LGIDict object");
5836 0 : return FALSE;
5837 : }
5838 :
5839 0 : if (poType->GetType() != PDFObjectType_Name)
5840 : {
5841 0 : CPLError(CE_Failure, CPLE_AppDefined,
5842 : "Invalid type for Type of LGIDict object");
5843 0 : return FALSE;
5844 : }
5845 :
5846 0 : if (strcmp(poType->GetName().c_str(), "LGIDict") != 0)
5847 : {
5848 0 : CPLError(CE_Failure, CPLE_AppDefined,
5849 : "Invalid value for Type of LGIDict object : %s",
5850 0 : poType->GetName().c_str());
5851 0 : return FALSE;
5852 : }
5853 :
5854 : /* -------------------------------------------------------------------- */
5855 : /* Extract Version attribute */
5856 : /* -------------------------------------------------------------------- */
5857 0 : GDALPDFObject *poVersion = poLGIDict->Get("Version");
5858 0 : if (poVersion == nullptr)
5859 : {
5860 0 : CPLError(CE_Failure, CPLE_AppDefined,
5861 : "Cannot find Version of LGIDict object");
5862 0 : return FALSE;
5863 : }
5864 :
5865 0 : if (poVersion->GetType() == PDFObjectType_String)
5866 : {
5867 : /* OGC best practice is 2.1 */
5868 0 : CPLDebug("PDF", "LGIDict Version : %s", poVersion->GetString().c_str());
5869 : }
5870 0 : else if (poVersion->GetType() == PDFObjectType_Int)
5871 : {
5872 : /* Old TerraGo is 2 */
5873 0 : CPLDebug("PDF", "LGIDict Version : %d", poVersion->GetInt());
5874 : }
5875 :
5876 : /* USGS PDF maps have several LGIDict. Keep the one whose description */
5877 : /* is "Map Layers" by default */
5878 : const char *pszNeatlineToSelect =
5879 0 : GetOption(papszOpenOptions, "NEATLINE", "Map Layers");
5880 :
5881 : /* -------------------------------------------------------------------- */
5882 : /* Extract Neatline attribute */
5883 : /* -------------------------------------------------------------------- */
5884 0 : GDALPDFObject *poNeatline = poLGIDict->Get("Neatline");
5885 0 : if (poNeatline != nullptr && poNeatline->GetType() == PDFObjectType_Array)
5886 : {
5887 0 : int nLength = poNeatline->GetArray()->GetLength();
5888 0 : if ((nLength % 2) != 0 || nLength < 4)
5889 : {
5890 0 : CPLError(CE_Failure, CPLE_AppDefined,
5891 : "Invalid length for Neatline");
5892 0 : return FALSE;
5893 : }
5894 :
5895 0 : GDALPDFObject *poDescription = poLGIDict->Get("Description");
5896 0 : bool bIsAskedNeatline = false;
5897 0 : if (poDescription != nullptr &&
5898 0 : poDescription->GetType() == PDFObjectType_String)
5899 : {
5900 0 : CPLDebug("PDF", "Description = %s",
5901 0 : poDescription->GetString().c_str());
5902 :
5903 0 : if (EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect))
5904 : {
5905 0 : m_dfMaxArea = 1e300;
5906 0 : bIsAskedNeatline = true;
5907 : }
5908 : }
5909 :
5910 0 : if (!bIsAskedNeatline)
5911 : {
5912 0 : double dfMinX = 0.0;
5913 0 : double dfMinY = 0.0;
5914 0 : double dfMaxX = 0.0;
5915 0 : double dfMaxY = 0.0;
5916 0 : for (int i = 0; i < nLength; i += 2)
5917 : {
5918 0 : double dfX = Get(poNeatline, i);
5919 0 : double dfY = Get(poNeatline, i + 1);
5920 0 : if (i == 0 || dfX < dfMinX)
5921 0 : dfMinX = dfX;
5922 0 : if (i == 0 || dfY < dfMinY)
5923 0 : dfMinY = dfY;
5924 0 : if (i == 0 || dfX > dfMaxX)
5925 0 : dfMaxX = dfX;
5926 0 : if (i == 0 || dfY > dfMaxY)
5927 0 : dfMaxY = dfY;
5928 : }
5929 0 : double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY);
5930 0 : if (dfArea < m_dfMaxArea)
5931 : {
5932 0 : CPLDebug("PDF", "Not the largest neatline. Skipping it");
5933 0 : return TRUE;
5934 : }
5935 :
5936 0 : CPLDebug("PDF", "This is the largest neatline for now");
5937 0 : m_dfMaxArea = dfArea;
5938 : }
5939 : else
5940 0 : CPLDebug("PDF", "The \"%s\" registration will be selected",
5941 : pszNeatlineToSelect);
5942 :
5943 0 : if (pbIsBestCandidate)
5944 0 : *pbIsBestCandidate = TRUE;
5945 :
5946 0 : delete m_poNeatLine;
5947 0 : m_poNeatLine = new OGRPolygon();
5948 0 : OGRLinearRing *poRing = new OGRLinearRing();
5949 0 : if (nLength == 4)
5950 : {
5951 : /* 2 points only ? They are the bounding box */
5952 0 : double dfX1 = Get(poNeatline, 0);
5953 0 : double dfY1 = Get(poNeatline, 1);
5954 0 : double dfX2 = Get(poNeatline, 2);
5955 0 : double dfY2 = Get(poNeatline, 3);
5956 0 : poRing->addPoint(dfX1, dfY1);
5957 0 : poRing->addPoint(dfX2, dfY1);
5958 0 : poRing->addPoint(dfX2, dfY2);
5959 0 : poRing->addPoint(dfX1, dfY2);
5960 : }
5961 : else
5962 : {
5963 0 : for (int i = 0; i < nLength; i += 2)
5964 : {
5965 0 : double dfX = Get(poNeatline, i);
5966 0 : double dfY = Get(poNeatline, i + 1);
5967 0 : poRing->addPoint(dfX, dfY);
5968 : }
5969 : }
5970 0 : poRing->closeRings();
5971 0 : m_poNeatLine->addRingDirectly(poRing);
5972 : }
5973 :
5974 0 : return TRUE;
5975 : }
5976 :
5977 : /************************************************************************/
5978 : /* ParseLGIDictDictSecondPass() */
5979 : /************************************************************************/
5980 :
5981 0 : int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary *poLGIDict)
5982 : {
5983 : int i;
5984 :
5985 : /* -------------------------------------------------------------------- */
5986 : /* Extract Description attribute */
5987 : /* -------------------------------------------------------------------- */
5988 0 : GDALPDFObject *poDescription = poLGIDict->Get("Description");
5989 0 : if (poDescription != nullptr &&
5990 0 : poDescription->GetType() == PDFObjectType_String)
5991 : {
5992 0 : CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str());
5993 : }
5994 :
5995 : /* -------------------------------------------------------------------- */
5996 : /* Extract CTM attribute */
5997 : /* -------------------------------------------------------------------- */
5998 0 : GDALPDFObject *poCTM = poLGIDict->Get("CTM");
5999 0 : m_bHasCTM = false;
6000 0 : if (poCTM != nullptr && poCTM->GetType() == PDFObjectType_Array &&
6001 0 : CPLTestBool(CPLGetConfigOption("PDF_USE_CTM", "YES")))
6002 : {
6003 0 : int nLength = poCTM->GetArray()->GetLength();
6004 0 : if (nLength != 6)
6005 : {
6006 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for CTM");
6007 0 : return FALSE;
6008 : }
6009 :
6010 0 : m_bHasCTM = true;
6011 0 : for (i = 0; i < nLength; i++)
6012 : {
6013 0 : m_adfCTM[i] = Get(poCTM, i);
6014 : /* Nullify rotation terms that are significantly smaller than */
6015 : /* scaling terms. */
6016 0 : if ((i == 1 || i == 2) &&
6017 0 : fabs(m_adfCTM[i]) < fabs(m_adfCTM[0]) * 1e-10)
6018 0 : m_adfCTM[i] = 0;
6019 0 : CPLDebug("PDF", "CTM[%d] = %.16g", i, m_adfCTM[i]);
6020 : }
6021 : }
6022 :
6023 : /* -------------------------------------------------------------------- */
6024 : /* Extract Registration attribute */
6025 : /* -------------------------------------------------------------------- */
6026 0 : GDALPDFObject *poRegistration = poLGIDict->Get("Registration");
6027 0 : if (poRegistration != nullptr &&
6028 0 : poRegistration->GetType() == PDFObjectType_Array)
6029 : {
6030 0 : GDALPDFArray *poRegistrationArray = poRegistration->GetArray();
6031 0 : int nLength = poRegistrationArray->GetLength();
6032 0 : if (nLength > 4 || (!m_bHasCTM && nLength >= 2) ||
6033 0 : CPLTestBool(CPLGetConfigOption("PDF_REPORT_GCPS", "NO")))
6034 : {
6035 0 : m_nGCPCount = 0;
6036 0 : m_pasGCPList =
6037 0 : static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nLength));
6038 :
6039 0 : for (i = 0; i < nLength; i++)
6040 : {
6041 0 : GDALPDFObject *poGCP = poRegistrationArray->Get(i);
6042 0 : if (poGCP != nullptr &&
6043 0 : poGCP->GetType() == PDFObjectType_Array &&
6044 0 : poGCP->GetArray()->GetLength() == 4)
6045 : {
6046 0 : double dfUserX = Get(poGCP, 0);
6047 0 : double dfUserY = Get(poGCP, 1);
6048 0 : double dfX = Get(poGCP, 2);
6049 0 : double dfY = Get(poGCP, 3);
6050 0 : CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX);
6051 0 : CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY);
6052 0 : CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX);
6053 0 : CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY);
6054 :
6055 : char szID[32];
6056 0 : snprintf(szID, sizeof(szID), "%d", m_nGCPCount + 1);
6057 0 : m_pasGCPList[m_nGCPCount].pszId = CPLStrdup(szID);
6058 0 : m_pasGCPList[m_nGCPCount].pszInfo = CPLStrdup("");
6059 0 : m_pasGCPList[m_nGCPCount].dfGCPPixel = dfUserX;
6060 0 : m_pasGCPList[m_nGCPCount].dfGCPLine = dfUserY;
6061 0 : m_pasGCPList[m_nGCPCount].dfGCPX = dfX;
6062 0 : m_pasGCPList[m_nGCPCount].dfGCPY = dfY;
6063 0 : m_nGCPCount++;
6064 : }
6065 : }
6066 :
6067 0 : if (m_nGCPCount == 0)
6068 : {
6069 0 : CPLFree(m_pasGCPList);
6070 0 : m_pasGCPList = nullptr;
6071 : }
6072 : }
6073 : }
6074 :
6075 0 : if (!m_bHasCTM && m_nGCPCount == 0)
6076 : {
6077 0 : CPLDebug("PDF", "Neither CTM nor Registration found");
6078 0 : return FALSE;
6079 : }
6080 :
6081 : /* -------------------------------------------------------------------- */
6082 : /* Extract Projection attribute */
6083 : /* -------------------------------------------------------------------- */
6084 0 : GDALPDFObject *poProjection = poLGIDict->Get("Projection");
6085 0 : if (poProjection == nullptr ||
6086 0 : poProjection->GetType() != PDFObjectType_Dictionary)
6087 : {
6088 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection");
6089 0 : return FALSE;
6090 : }
6091 :
6092 0 : return ParseProjDict(poProjection->GetDictionary());
6093 : }
6094 :
6095 : /************************************************************************/
6096 : /* ParseProjDict() */
6097 : /************************************************************************/
6098 :
6099 0 : int PDFDataset::ParseProjDict(GDALPDFDictionary *poProjDict)
6100 : {
6101 0 : if (poProjDict == nullptr)
6102 0 : return FALSE;
6103 0 : OGRSpatialReference oSRS;
6104 0 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
6105 :
6106 : /* -------------------------------------------------------------------- */
6107 : /* Extract WKT attribute (GDAL extension) */
6108 : /* -------------------------------------------------------------------- */
6109 0 : GDALPDFObject *poWKT = poProjDict->Get("WKT");
6110 0 : if (poWKT != nullptr && poWKT->GetType() == PDFObjectType_String &&
6111 0 : CPLTestBool(CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE")))
6112 : {
6113 0 : CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it");
6114 0 : const char *pszWKTRead = poWKT->GetString().c_str();
6115 0 : if (pszWKTRead[0] != 0)
6116 0 : m_oSRS.importFromWkt(pszWKTRead);
6117 0 : return TRUE;
6118 : }
6119 :
6120 : /* -------------------------------------------------------------------- */
6121 : /* Extract Type attribute */
6122 : /* -------------------------------------------------------------------- */
6123 0 : GDALPDFObject *poType = poProjDict->Get("Type");
6124 0 : if (poType == nullptr)
6125 : {
6126 0 : CPLError(CE_Failure, CPLE_AppDefined,
6127 : "Cannot find Type of Projection object");
6128 0 : return FALSE;
6129 : }
6130 :
6131 0 : if (poType->GetType() != PDFObjectType_Name)
6132 : {
6133 0 : CPLError(CE_Failure, CPLE_AppDefined,
6134 : "Invalid type for Type of Projection object");
6135 0 : return FALSE;
6136 : }
6137 :
6138 0 : if (strcmp(poType->GetName().c_str(), "Projection") != 0)
6139 : {
6140 0 : CPLError(CE_Failure, CPLE_AppDefined,
6141 : "Invalid value for Type of Projection object : %s",
6142 0 : poType->GetName().c_str());
6143 0 : return FALSE;
6144 : }
6145 :
6146 : /* -------------------------------------------------------------------- */
6147 : /* Extract Datum attribute */
6148 : /* -------------------------------------------------------------------- */
6149 0 : int bIsWGS84 = FALSE;
6150 0 : int bIsNAD83 = FALSE;
6151 : /* int bIsNAD27 = FALSE; */
6152 :
6153 0 : GDALPDFObject *poDatum = poProjDict->Get("Datum");
6154 0 : if (poDatum != nullptr)
6155 : {
6156 0 : if (poDatum->GetType() == PDFObjectType_String)
6157 : {
6158 : /* Using Annex A of
6159 : * http://portal.opengeospatial.org/files/?artifact_id=40537 */
6160 0 : const char *pszDatum = poDatum->GetString().c_str();
6161 0 : CPLDebug("PDF", "Datum = %s", pszDatum);
6162 0 : if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE"))
6163 : {
6164 0 : bIsWGS84 = TRUE;
6165 0 : oSRS.SetWellKnownGeogCS("WGS84");
6166 : }
6167 0 : else if (EQUAL(pszDatum, "NAR") || STARTS_WITH_CI(pszDatum, "NAR-"))
6168 : {
6169 0 : bIsNAD83 = TRUE;
6170 0 : oSRS.SetWellKnownGeogCS("NAD83");
6171 : }
6172 0 : else if (EQUAL(pszDatum, "NAS") || STARTS_WITH_CI(pszDatum, "NAS-"))
6173 : {
6174 : /* bIsNAD27 = TRUE; */
6175 0 : oSRS.SetWellKnownGeogCS("NAD27");
6176 : }
6177 0 : else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */
6178 : {
6179 0 : oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/,
6180 : "unknown" /*const char * pszDatumName */,
6181 : "International 1924", 6378388, 297);
6182 0 : oSRS.SetTOWGS84(-333, -222, 114);
6183 : }
6184 0 : else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */
6185 : {
6186 0 : oSRS.importFromEPSG(4131);
6187 : }
6188 0 : else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */
6189 : {
6190 0 : oSRS.importFromEPSG(4283);
6191 : }
6192 0 : else if (STARTS_WITH_CI(pszDatum, "OHA-")) /* Old Hawaiian */
6193 : {
6194 0 : oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */
6195 0 : if (!EQUAL(pszDatum, "OHA-M"))
6196 : {
6197 0 : CPLError(CE_Warning, CPLE_AppDefined,
6198 : "Using OHA-M (Old Hawaiian Mean) definition for "
6199 : "%s. Potential issue with datum shift parameters",
6200 : 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 : {
6208 0 : poNode = poNode->GetChild(iChild);
6209 0 : iChild = poNode->FindChild("AUTHORITY");
6210 0 : if (iChild != -1)
6211 0 : poNode->DestroyChild(iChild);
6212 : }
6213 : }
6214 : }
6215 : else
6216 : {
6217 0 : CPLError(CE_Warning, CPLE_AppDefined,
6218 : "Unhandled (yet) value for Datum : %s. Defaulting to "
6219 : "WGS84...",
6220 : pszDatum);
6221 0 : oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/,
6222 : "unknown" /*const char * pszDatumName */,
6223 : "unknown", 6378137, 298.257223563);
6224 : }
6225 : }
6226 0 : else if (poDatum->GetType() == PDFObjectType_Dictionary)
6227 : {
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 : {
6242 0 : CPLError(
6243 : CE_Warning, CPLE_AppDefined,
6244 : "Cannot find Ellipsoid in Datum. Defaulting to WGS84...");
6245 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, "unknown",
6246 : 6378137, 298.257223563);
6247 : }
6248 0 : else if (poEllipsoid->GetType() == PDFObjectType_String)
6249 : {
6250 0 : const char *pszEllipsoid = poEllipsoid->GetString().c_str();
6251 0 : CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid);
6252 0 : if (EQUAL(pszEllipsoid, "WE"))
6253 : {
6254 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, "WGS 84",
6255 : 6378137, 298.257223563);
6256 : }
6257 : else
6258 : {
6259 0 : CPLError(CE_Warning, CPLE_AppDefined,
6260 : "Unhandled (yet) value for Ellipsoid : %s. "
6261 : "Defaulting to WGS84...",
6262 : pszEllipsoid);
6263 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, pszEllipsoid,
6264 : 6378137, 298.257223563);
6265 : }
6266 : }
6267 : else // if (poEllipsoid->GetType() == PDFObjectType_Dictionary)
6268 : {
6269 : GDALPDFDictionary *poEllipsoidDict =
6270 0 : poEllipsoid->GetDictionary();
6271 :
6272 : 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 : pszEllipsoidDescription =
6278 0 : poEllipsoidDescription->GetString().c_str();
6279 0 : CPLDebug("PDF", "Datum.Ellipsoid.Description = %s",
6280 : pszEllipsoidDescription);
6281 :
6282 0 : double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis");
6283 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g",
6284 : dfSemiMajor);
6285 0 : double dfInvFlattening = -1.0;
6286 :
6287 0 : if (poEllipsoidDict->Get("InvFlattening"))
6288 : {
6289 0 : dfInvFlattening = Get(poEllipsoidDict, "InvFlattening");
6290 0 : CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g",
6291 : dfInvFlattening);
6292 : }
6293 0 : else if (poEllipsoidDict->Get("SemiMinorAxis"))
6294 : {
6295 0 : double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis");
6296 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g",
6297 : dfSemiMinor);
6298 : dfInvFlattening =
6299 0 : OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
6300 : }
6301 :
6302 0 : if (dfSemiMajor != 0.0 && dfInvFlattening != -1.0)
6303 : {
6304 0 : oSRS.SetGeogCS("unknown", pszDatumDescription,
6305 : pszEllipsoidDescription, dfSemiMajor,
6306 : dfInvFlattening);
6307 : }
6308 : else
6309 : {
6310 0 : CPLError(
6311 : CE_Warning, CPLE_AppDefined,
6312 : "Invalid Ellipsoid object. Defaulting to WGS84...");
6313 0 : oSRS.SetGeogCS("unknown", pszDatumDescription,
6314 : pszEllipsoidDescription, 6378137,
6315 : 298.257223563);
6316 : }
6317 : }
6318 :
6319 0 : GDALPDFObject *poTOWGS84 = poDatumDict->Get("ToWGS84");
6320 0 : if (poTOWGS84 != nullptr &&
6321 0 : poTOWGS84->GetType() == PDFObjectType_Dictionary)
6322 : {
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 : {
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 : }
6336 : else
6337 : {
6338 0 : oSRS.SetTOWGS84(dx, dy, dz);
6339 : }
6340 : }
6341 : }
6342 : }
6343 :
6344 : /* -------------------------------------------------------------------- */
6345 : /* Extract Hemisphere attribute */
6346 : /* -------------------------------------------------------------------- */
6347 0 : CPLString osHemisphere;
6348 0 : GDALPDFObject *poHemisphere = poProjDict->Get("Hemisphere");
6349 0 : if (poHemisphere != nullptr &&
6350 0 : poHemisphere->GetType() == PDFObjectType_String)
6351 : {
6352 0 : osHemisphere = poHemisphere->GetString();
6353 : }
6354 :
6355 : /* -------------------------------------------------------------------- */
6356 : /* Extract ProjectionType attribute */
6357 : /* -------------------------------------------------------------------- */
6358 0 : GDALPDFObject *poProjectionType = poProjDict->Get("ProjectionType");
6359 0 : if (poProjectionType == nullptr ||
6360 0 : poProjectionType->GetType() != PDFObjectType_String)
6361 : {
6362 0 : CPLError(CE_Failure, CPLE_AppDefined,
6363 : "Cannot find ProjectionType of Projection object");
6364 0 : return FALSE;
6365 : }
6366 0 : CPLString osProjectionType(poProjectionType->GetString());
6367 0 : CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str());
6368 :
6369 : /* Unhandled: NONE, GEODETIC */
6370 :
6371 0 : if (EQUAL(osProjectionType, "GEOGRAPHIC"))
6372 : {
6373 : /* Nothing to do */
6374 : }
6375 :
6376 : /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */
6377 :
6378 0 : else if (EQUAL(osProjectionType, "UT")) /* UTM */
6379 : {
6380 0 : const double dfZone = Get(poProjDict, "Zone");
6381 0 : if (dfZone >= 1 && dfZone <= 60)
6382 : {
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 : else
6388 0 : oSRS.SetUTM(nZone, bNorth);
6389 : }
6390 : }
6391 :
6392 0 : else if (EQUAL(osProjectionType,
6393 : "UP")) /* Universal Polar Stereographic (UPS) */
6394 : {
6395 0 : int bNorth = EQUAL(osHemisphere, "N");
6396 0 : if (bIsWGS84)
6397 0 : oSRS.importFromEPSG((bNorth) ? 32661 : 32761);
6398 : else
6399 0 : oSRS.SetPS((bNorth) ? 90 : -90, 0, 0.994, 200000, 200000);
6400 : }
6401 :
6402 0 : else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */
6403 : {
6404 0 : const double dfZone = Get(poProjDict, "Zone");
6405 0 : if (dfZone >= 0 && dfZone <= INT_MAX)
6406 : {
6407 0 : int nZone = static_cast<int>(dfZone);
6408 0 : oSRS.SetStatePlane(nZone, bIsNAD83);
6409 : }
6410 : }
6411 :
6412 0 : else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */
6413 : {
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 : dfFalseEasting, dfFalseNorthing);
6422 : }
6423 :
6424 0 : else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */
6425 : {
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 : }
6432 :
6433 0 : else if (EQUAL(osProjectionType, "BF")) /* Bonne */
6434 : {
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 : dfFalseNorthing);
6441 : }
6442 :
6443 0 : else if (EQUAL(osProjectionType, "CS")) /* Cassini */
6444 : {
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 : }
6451 :
6452 0 : else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */
6453 : {
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 : dfFalseNorthing);
6460 : }
6461 :
6462 0 : else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */
6463 : {
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 : }
6469 :
6470 0 : else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */
6471 : {
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 : }
6477 :
6478 0 : else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */
6479 : {
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 : dfFalseNorthing);
6486 : }
6487 :
6488 0 : else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */
6489 : {
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 : dfFalseNorthing);
6496 : }
6497 :
6498 0 : else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */
6499 : {
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 : dfFalseNorthing);
6508 : }
6509 :
6510 0 : else if (EQUAL(osProjectionType, "MC")) /* Mercator */
6511 : {
6512 : #ifdef not_supported
6513 : if (poProjDict->Get("StandardParallelOne") == nullptr)
6514 : #endif
6515 : {
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 : dfFalseNorthing);
6523 : }
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 : }
6539 :
6540 0 : else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */
6541 : {
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 : }
6548 :
6549 0 : else if (EQUAL(osProjectionType, "MP")) /* Mollweide */
6550 : {
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 : }
6556 :
6557 : /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */
6558 :
6559 0 : else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */
6560 : {
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 : dfFalseNorthing);
6569 : }
6570 :
6571 0 : else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */
6572 : {
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 : dfFalseEasting, dfFalseNorthing);
6583 : }
6584 :
6585 0 : else if (EQUAL(osProjectionType, "OD")) /* Orthographic */
6586 : {
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 : dfFalseNorthing);
6593 : }
6594 :
6595 0 : else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */
6596 : {
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 : dfFalseNorthing);
6604 : }
6605 :
6606 0 : else if (EQUAL(osProjectionType, "PH")) /* Polyconic */
6607 : {
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 : dfFalseNorthing);
6614 : }
6615 :
6616 0 : else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */
6617 : {
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 : }
6623 :
6624 0 : else if (EQUAL(osProjectionType, "SD")) /* Stereographic */
6625 : {
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 : dfFalseEasting, dfFalseNorthing);
6633 : }
6634 :
6635 0 : else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */
6636 : {
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 : {
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 : else
6654 0 : oSRS.SetUTM(nZone, bNorth);
6655 : }
6656 : else
6657 : {
6658 0 : oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6659 : dfFalseNorthing);
6660 : }
6661 : }
6662 :
6663 : /* Unhandled TX : Transverse Cylindrical Equal Area */
6664 :
6665 0 : else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */
6666 : {
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 : }
6672 :
6673 : else
6674 : {
6675 0 : CPLError(CE_Failure, CPLE_AppDefined,
6676 : "Unhandled (yet) value for ProjectionType : %s",
6677 : osProjectionType.c_str());
6678 0 : return FALSE;
6679 : }
6680 :
6681 : /* -------------------------------------------------------------------- */
6682 : /* Extract Units attribute */
6683 : /* -------------------------------------------------------------------- */
6684 0 : CPLString osUnits;
6685 0 : GDALPDFObject *poUnits = poProjDict->Get("Units");
6686 0 : if (poUnits != nullptr && poUnits->GetType() == PDFObjectType_String &&
6687 0 : !EQUAL(osProjectionType, "GEOGRAPHIC"))
6688 : {
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 : {
6700 0 : oSRS.SetLinearUnits("foot", 0.3048);
6701 0 : oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0);
6702 : }
6703 0 : else if (EQUAL(osUnits, "USSF"))
6704 : {
6705 0 : oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV));
6706 0 : oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0);
6707 : }
6708 : else
6709 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unhandled unit: %s",
6710 : osUnits.c_str());
6711 : }
6712 :
6713 : /* -------------------------------------------------------------------- */
6714 : /* Export SpatialRef */
6715 : /* -------------------------------------------------------------------- */
6716 0 : m_oSRS = std::move(oSRS);
6717 :
6718 0 : return TRUE;
6719 : }
6720 :
6721 : /************************************************************************/
6722 : /* ParseVP() */
6723 : /************************************************************************/
6724 :
6725 155 : int PDFDataset::ParseVP(GDALPDFObject *poVP, double dfMediaBoxWidth,
6726 : double dfMediaBoxHeight)
6727 : {
6728 : int i;
6729 :
6730 155 : if (poVP->GetType() != PDFObjectType_Array)
6731 0 : return FALSE;
6732 :
6733 155 : GDALPDFArray *poVPArray = poVP->GetArray();
6734 :
6735 155 : int nLength = poVPArray->GetLength();
6736 155 : CPLDebug("PDF", "VP length = %d", nLength);
6737 155 : if (nLength < 1)
6738 0 : return FALSE;
6739 :
6740 : /* -------------------------------------------------------------------- */
6741 : /* Find the largest BBox */
6742 : /* -------------------------------------------------------------------- */
6743 : const char *pszNeatlineToSelect =
6744 155 : GetOption(papszOpenOptions, "NEATLINE", "Map Layers");
6745 :
6746 155 : int iLargest = 0;
6747 155 : int iRequestedVP = -1;
6748 155 : double dfLargestArea = 0;
6749 :
6750 321 : for (i = 0; i < nLength; i++)
6751 : {
6752 166 : GDALPDFObject *poVPElt = poVPArray->Get(i);
6753 332 : if (poVPElt == nullptr ||
6754 166 : poVPElt->GetType() != PDFObjectType_Dictionary)
6755 : {
6756 0 : return FALSE;
6757 : }
6758 :
6759 166 : GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary();
6760 :
6761 166 : GDALPDFObject *poMeasure = poVPEltDict->Get("Measure");
6762 332 : if (poMeasure == nullptr ||
6763 166 : poMeasure->GetType() != PDFObjectType_Dictionary)
6764 : {
6765 0 : continue;
6766 : }
6767 : /* --------------------------------------------------------------------
6768 : */
6769 : /* Extract Subtype attribute */
6770 : /* --------------------------------------------------------------------
6771 : */
6772 166 : GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary();
6773 166 : GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype");
6774 166 : if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name)
6775 : {
6776 0 : continue;
6777 : }
6778 :
6779 166 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
6780 166 : if (!EQUAL(poSubtype->GetName().c_str(), "GEO"))
6781 : {
6782 0 : continue;
6783 : }
6784 :
6785 166 : GDALPDFObject *poName = poVPEltDict->Get("Name");
6786 166 : if (poName != nullptr && poName->GetType() == PDFObjectType_String)
6787 : {
6788 164 : CPLDebug("PDF", "Name = %s", poName->GetString().c_str());
6789 164 : if (EQUAL(poName->GetString().c_str(), pszNeatlineToSelect))
6790 : {
6791 0 : iRequestedVP = i;
6792 : }
6793 : }
6794 :
6795 166 : GDALPDFObject *poBBox = poVPEltDict->Get("BBox");
6796 166 : if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array)
6797 : {
6798 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object");
6799 0 : return FALSE;
6800 : }
6801 :
6802 166 : int nBboxLength = poBBox->GetArray()->GetLength();
6803 166 : if (nBboxLength != 4)
6804 : {
6805 0 : CPLError(CE_Failure, CPLE_AppDefined,
6806 : "Invalid length for Bbox object");
6807 0 : return FALSE;
6808 : }
6809 :
6810 : double adfBBox[4];
6811 166 : adfBBox[0] = Get(poBBox, 0);
6812 166 : adfBBox[1] = Get(poBBox, 1);
6813 166 : adfBBox[2] = Get(poBBox, 2);
6814 166 : adfBBox[3] = Get(poBBox, 3);
6815 166 : double dfArea =
6816 166 : fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]);
6817 166 : if (dfArea > dfLargestArea)
6818 : {
6819 155 : iLargest = i;
6820 155 : dfLargestArea = dfArea;
6821 : }
6822 : }
6823 :
6824 155 : if (nLength > 1)
6825 : {
6826 11 : CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest);
6827 : }
6828 :
6829 155 : GDALPDFObject *poVPElt = nullptr;
6830 :
6831 155 : if (iRequestedVP > -1)
6832 : {
6833 0 : CPLDebug("PDF", "Requested NEATLINE BBox in VP array is element %d",
6834 : iRequestedVP);
6835 0 : poVPElt = poVPArray->Get(iRequestedVP);
6836 : }
6837 : else
6838 : {
6839 155 : poVPElt = poVPArray->Get(iLargest);
6840 : }
6841 :
6842 155 : if (poVPElt == nullptr || poVPElt->GetType() != PDFObjectType_Dictionary)
6843 : {
6844 0 : return FALSE;
6845 : }
6846 :
6847 155 : GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary();
6848 :
6849 155 : GDALPDFObject *poBBox = poVPEltDict->Get("BBox");
6850 155 : if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array)
6851 : {
6852 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object");
6853 0 : return FALSE;
6854 : }
6855 :
6856 155 : int nBboxLength = poBBox->GetArray()->GetLength();
6857 155 : if (nBboxLength != 4)
6858 : {
6859 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for Bbox object");
6860 0 : return FALSE;
6861 : }
6862 :
6863 155 : double dfULX = Get(poBBox, 0);
6864 155 : double dfULY = dfMediaBoxHeight - Get(poBBox, 1);
6865 155 : double dfLRX = Get(poBBox, 2);
6866 155 : double dfLRY = dfMediaBoxHeight - Get(poBBox, 3);
6867 :
6868 : /* -------------------------------------------------------------------- */
6869 : /* Extract Measure attribute */
6870 : /* -------------------------------------------------------------------- */
6871 155 : GDALPDFObject *poMeasure = poVPEltDict->Get("Measure");
6872 310 : if (poMeasure == nullptr ||
6873 155 : poMeasure->GetType() != PDFObjectType_Dictionary)
6874 : {
6875 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Measure object");
6876 0 : return FALSE;
6877 : }
6878 :
6879 155 : int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, dfULX,
6880 : dfULY, dfLRX, dfLRY);
6881 :
6882 : /* -------------------------------------------------------------------- */
6883 : /* Extract PointData attribute */
6884 : /* -------------------------------------------------------------------- */
6885 155 : GDALPDFObject *poPointData = poVPEltDict->Get("PtData");
6886 155 : if (poPointData != nullptr &&
6887 0 : poPointData->GetType() == PDFObjectType_Dictionary)
6888 : {
6889 0 : CPLDebug("PDF", "Found PointData");
6890 : }
6891 :
6892 155 : return bRet;
6893 : }
6894 :
6895 : /************************************************************************/
6896 : /* ParseMeasure() */
6897 : /************************************************************************/
6898 :
6899 155 : int PDFDataset::ParseMeasure(GDALPDFObject *poMeasure, double dfMediaBoxWidth,
6900 : double dfMediaBoxHeight, double dfULX,
6901 : double dfULY, double dfLRX, double dfLRY)
6902 : {
6903 155 : GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary();
6904 :
6905 : /* -------------------------------------------------------------------- */
6906 : /* Extract Subtype attribute */
6907 : /* -------------------------------------------------------------------- */
6908 155 : GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype");
6909 155 : if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name)
6910 : {
6911 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Subtype object");
6912 0 : return FALSE;
6913 : }
6914 :
6915 155 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
6916 155 : if (!EQUAL(poSubtype->GetName().c_str(), "GEO"))
6917 0 : 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 155 : GDALPDFObject *poBounds = poMeasureDict->Get("lgit:Bounds");
6929 155 : if (poBounds != nullptr && poBounds->GetType() == PDFObjectType_Array)
6930 : {
6931 0 : CPLDebug("PDF", "Using lgit:Bounds");
6932 : }
6933 308 : else if ((poBounds = poMeasureDict->Get("Bounds")) == nullptr ||
6934 153 : poBounds->GetType() != PDFObjectType_Array)
6935 : {
6936 2 : poBounds = nullptr;
6937 : }
6938 :
6939 155 : if (poBounds != nullptr)
6940 : {
6941 153 : int nBoundsLength = poBounds->GetArray()->GetLength();
6942 153 : if (nBoundsLength == 8)
6943 : {
6944 : double adfBounds[8];
6945 1296 : for (int i = 0; i < 8; i++)
6946 : {
6947 1152 : adfBounds[i] = Get(poBounds, i);
6948 1152 : CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
6949 : }
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 : }
6955 : }
6956 :
6957 : /* -------------------------------------------------------------------- */
6958 : /* Extract GPTS attribute */
6959 : /* -------------------------------------------------------------------- */
6960 155 : GDALPDFObject *poGPTS = poMeasureDict->Get("lgit:GPTS");
6961 155 : if (poGPTS != nullptr && poGPTS->GetType() == PDFObjectType_Array)
6962 : {
6963 0 : CPLDebug("PDF", "Using lgit:GPTS");
6964 : }
6965 310 : else if ((poGPTS = poMeasureDict->Get("GPTS")) == nullptr ||
6966 155 : poGPTS->GetType() != PDFObjectType_Array)
6967 : {
6968 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GPTS object");
6969 0 : return FALSE;
6970 : }
6971 :
6972 155 : int nGPTSLength = poGPTS->GetArray()->GetLength();
6973 155 : if ((nGPTSLength % 2) != 0 || nGPTSLength < 6)
6974 : {
6975 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for GPTS object");
6976 0 : return FALSE;
6977 : }
6978 :
6979 310 : std::vector<double> adfGPTS(nGPTSLength);
6980 1395 : for (int i = 0; i < nGPTSLength; i++)
6981 : {
6982 1240 : adfGPTS[i] = Get(poGPTS, i);
6983 1240 : CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
6984 : }
6985 :
6986 : /* -------------------------------------------------------------------- */
6987 : /* Extract LPTS attribute */
6988 : /* -------------------------------------------------------------------- */
6989 155 : GDALPDFObject *poLPTS = poMeasureDict->Get("lgit:LPTS");
6990 155 : if (poLPTS != nullptr && poLPTS->GetType() == PDFObjectType_Array)
6991 : {
6992 0 : CPLDebug("PDF", "Using lgit:LPTS");
6993 : }
6994 310 : else if ((poLPTS = poMeasureDict->Get("LPTS")) == nullptr ||
6995 155 : poLPTS->GetType() != PDFObjectType_Array)
6996 : {
6997 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find LPTS object");
6998 0 : return FALSE;
6999 : }
7000 :
7001 155 : int nLPTSLength = poLPTS->GetArray()->GetLength();
7002 155 : if (nLPTSLength != nGPTSLength)
7003 : {
7004 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for LPTS object");
7005 0 : return FALSE;
7006 : }
7007 :
7008 310 : std::vector<double> adfLPTS(nLPTSLength);
7009 1395 : for (int i = 0; i < nLPTSLength; i++)
7010 : {
7011 1240 : adfLPTS[i] = Get(poLPTS, i);
7012 1240 : CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
7013 : }
7014 :
7015 : /* -------------------------------------------------------------------- */
7016 : /* Extract GCS attribute */
7017 : /* -------------------------------------------------------------------- */
7018 155 : GDALPDFObject *poGCS = poMeasureDict->Get("GCS");
7019 155 : if (poGCS == nullptr || poGCS->GetType() != PDFObjectType_Dictionary)
7020 : {
7021 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS object");
7022 0 : return FALSE;
7023 : }
7024 :
7025 155 : GDALPDFDictionary *poGCSDict = poGCS->GetDictionary();
7026 :
7027 : /* -------------------------------------------------------------------- */
7028 : /* Extract GCS.Type attribute */
7029 : /* -------------------------------------------------------------------- */
7030 155 : GDALPDFObject *poGCSType = poGCSDict->Get("Type");
7031 155 : if (poGCSType == nullptr || poGCSType->GetType() != PDFObjectType_Name)
7032 : {
7033 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS.Type object");
7034 0 : return FALSE;
7035 : }
7036 :
7037 155 : CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str());
7038 :
7039 : /* -------------------------------------------------------------------- */
7040 : /* Extract EPSG attribute */
7041 : /* -------------------------------------------------------------------- */
7042 155 : GDALPDFObject *poEPSG = poGCSDict->Get("EPSG");
7043 155 : int nEPSGCode = 0;
7044 155 : if (poEPSG != nullptr && poEPSG->GetType() == PDFObjectType_Int)
7045 : {
7046 129 : nEPSGCode = poEPSG->GetInt();
7047 129 : CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode);
7048 : }
7049 :
7050 : /* -------------------------------------------------------------------- */
7051 : /* Extract GCS.WKT attribute */
7052 : /* -------------------------------------------------------------------- */
7053 155 : GDALPDFObject *poGCSWKT = poGCSDict->Get("WKT");
7054 155 : if (poGCSWKT != nullptr && poGCSWKT->GetType() != PDFObjectType_String)
7055 : {
7056 0 : poGCSWKT = nullptr;
7057 : }
7058 :
7059 155 : if (poGCSWKT != nullptr)
7060 153 : CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str());
7061 :
7062 155 : if (nEPSGCode <= 0 && poGCSWKT == nullptr)
7063 : {
7064 0 : CPLError(CE_Failure, CPLE_AppDefined,
7065 : "Cannot find GCS.WKT or GCS.EPSG objects");
7066 0 : return FALSE;
7067 : }
7068 :
7069 155 : if (poGCSWKT != nullptr)
7070 : {
7071 153 : m_oSRS.importFromWkt(poGCSWKT->GetString().c_str());
7072 : }
7073 :
7074 155 : bool bSRSOK = false;
7075 155 : if (nEPSGCode != 0)
7076 : {
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 129 : if (nEPSGCode >= 100000)
7081 : {
7082 4 : CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler);
7083 4 : OGRSpatialReference oSRS_ESRI;
7084 2 : oSRS_ESRI.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
7085 2 : if (oSRS_ESRI.SetFromUserInput(CPLSPrintf("ESRI:%d", nEPSGCode)) ==
7086 : OGRERR_NONE)
7087 : {
7088 2 : bSRSOK = true;
7089 :
7090 : // Check consistency of ESRI:xxxx and WKT definitions
7091 2 : if (poGCSWKT != nullptr)
7092 : {
7093 3 : if (!m_oSRS.GetName() ||
7094 1 : (!EQUAL(oSRS_ESRI.GetName(), m_oSRS.GetName()) &&
7095 0 : !oSRS_ESRI.IsSame(&m_oSRS)))
7096 : {
7097 1 : CPLDebug("PDF",
7098 : "Definition from ESRI:%d and WKT=%s do not "
7099 : "match. Using WKT string",
7100 1 : nEPSGCode, poGCSWKT->GetString().c_str());
7101 1 : bSRSOK = false;
7102 : }
7103 : }
7104 2 : if (bSRSOK)
7105 : {
7106 1 : m_oSRS = std::move(oSRS_ESRI);
7107 : }
7108 : }
7109 : }
7110 127 : else if (m_oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
7111 : {
7112 127 : bSRSOK = true;
7113 : }
7114 : }
7115 :
7116 155 : if (!bSRSOK)
7117 : {
7118 27 : if (poGCSWKT == nullptr)
7119 : {
7120 0 : CPLError(CE_Failure, CPLE_AppDefined,
7121 : "Cannot resolve EPSG object, and GCS.WKT not found");
7122 0 : return FALSE;
7123 : }
7124 :
7125 27 : if (m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()) != OGRERR_NONE)
7126 : {
7127 1 : m_oSRS.Clear();
7128 1 : return FALSE;
7129 : }
7130 : }
7131 :
7132 : /* -------------------------------------------------------------------- */
7133 : /* Compute geotransform */
7134 : /* -------------------------------------------------------------------- */
7135 154 : 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 154 : bool bReproject = true;
7147 154 : if (m_oSRS.IsProjected())
7148 : {
7149 560 : for (int i = 0; i < nGPTSLength / 2; i++)
7150 : {
7151 448 : if (fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361)
7152 : {
7153 0 : CPLDebug("PDF", "GPTS coordinates seems to be in (northing, "
7154 : "easting), which is non-standard");
7155 0 : bReproject = false;
7156 0 : break;
7157 : }
7158 : }
7159 : }
7160 :
7161 154 : OGRCoordinateTransformation *poCT = nullptr;
7162 154 : if (bReproject)
7163 : {
7164 154 : poCT = OGRCreateCoordinateTransformation(poSRSGeog, &m_oSRS);
7165 154 : if (poCT == nullptr)
7166 : {
7167 0 : delete poSRSGeog;
7168 0 : m_oSRS.Clear();
7169 0 : return FALSE;
7170 : }
7171 : }
7172 :
7173 308 : std::vector<GDAL_GCP> asGCPS(nGPTSLength / 2);
7174 :
7175 : /* Create NEATLINE */
7176 154 : OGRLinearRing *poRing = nullptr;
7177 154 : if (nGPTSLength == 8)
7178 : {
7179 154 : m_poNeatLine = new OGRPolygon();
7180 154 : poRing = new OGRLinearRing();
7181 154 : m_poNeatLine->addRingDirectly(poRing);
7182 : }
7183 :
7184 770 : for (int i = 0; i < nGPTSLength / 2; i++)
7185 : {
7186 : /* We probably assume LPTS is 0 or 1 */
7187 1232 : asGCPS[i].dfGCPPixel =
7188 616 : (dfULX * (1 - adfLPTS[2 * i + 0]) + dfLRX * adfLPTS[2 * i + 0]) /
7189 616 : dfMediaBoxWidth * nRasterXSize;
7190 1232 : asGCPS[i].dfGCPLine =
7191 616 : (dfULY * (1 - adfLPTS[2 * i + 1]) + dfLRY * adfLPTS[2 * i + 1]) /
7192 616 : dfMediaBoxHeight * nRasterYSize;
7193 :
7194 616 : double lat = adfGPTS[2 * i];
7195 616 : double lon = adfGPTS[2 * i + 1];
7196 616 : double x = lon;
7197 616 : double y = lat;
7198 616 : if (bReproject)
7199 : {
7200 616 : if (!poCT->Transform(1, &x, &y, nullptr))
7201 : {
7202 0 : CPLError(CE_Failure, CPLE_AppDefined,
7203 : "Cannot reproject (%f, %f)", lon, lat);
7204 0 : delete poSRSGeog;
7205 0 : delete poCT;
7206 0 : m_oSRS.Clear();
7207 0 : return FALSE;
7208 : }
7209 : }
7210 :
7211 616 : x = ROUND_IF_CLOSE(x);
7212 616 : y = ROUND_IF_CLOSE(y);
7213 :
7214 616 : asGCPS[i].dfGCPX = x;
7215 616 : asGCPS[i].dfGCPY = y;
7216 :
7217 616 : if (poRing)
7218 616 : poRing->addPoint(x, y);
7219 : }
7220 :
7221 154 : delete poSRSGeog;
7222 154 : delete poCT;
7223 :
7224 154 : if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(),
7225 : FALSE))
7226 : {
7227 0 : CPLDebug("PDF",
7228 : "Could not compute GT with exact match. Try with approximate");
7229 0 : if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(), m_gt.data(),
7230 : TRUE))
7231 : {
7232 0 : CPLError(CE_Failure, CPLE_AppDefined,
7233 : "Could not compute GT with approximate match.");
7234 0 : return FALSE;
7235 : }
7236 : }
7237 154 : 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 154 : const double dfPixelSize = std::min(fabs(m_gt[1]), fabs(m_gt[5]));
7243 154 : const double dfRotationShearTerm = std::max(fabs(m_gt[2]), fabs(m_gt[4]));
7244 161 : if (dfRotationShearTerm < 1e-5 * dfPixelSize ||
7245 7 : (m_bUseLib.test(PDFLIB_PDFIUM) &&
7246 154 : std::min(fabs(m_gt[2]), fabs(m_gt[4])) < 1e-5 * dfPixelSize))
7247 : {
7248 147 : dfLRX = m_gt[0] + nRasterXSize * m_gt[1] + nRasterYSize * m_gt[2];
7249 147 : dfLRY = m_gt[3] + nRasterXSize * m_gt[4] + nRasterYSize * m_gt[5];
7250 147 : m_gt[1] = (dfLRX - m_gt[0]) / nRasterXSize;
7251 147 : m_gt[5] = (dfLRY - m_gt[3]) / nRasterYSize;
7252 147 : m_gt[2] = m_gt[4] = 0;
7253 : }
7254 :
7255 154 : return TRUE;
7256 : }
7257 :
7258 : /************************************************************************/
7259 : /* GetSpatialRef() */
7260 : /************************************************************************/
7261 :
7262 225 : const OGRSpatialReference *PDFDataset::GetSpatialRef() const
7263 : {
7264 225 : const auto poSRS = GDALPamDataset::GetSpatialRef();
7265 225 : if (poSRS)
7266 200 : return poSRS;
7267 :
7268 25 : if (!m_oSRS.IsEmpty() && m_bGeoTransformValid)
7269 24 : return &m_oSRS;
7270 1 : return nullptr;
7271 : }
7272 :
7273 : /************************************************************************/
7274 : /* GetGeoTransform() */
7275 : /************************************************************************/
7276 :
7277 23 : CPLErr PDFDataset::GetGeoTransform(GDALGeoTransform >) const
7278 :
7279 : {
7280 23 : if (GDALPamDataset::GetGeoTransform(gt) == CE_None)
7281 : {
7282 3 : return CE_None;
7283 : }
7284 :
7285 20 : gt = m_gt;
7286 20 : return ((m_bGeoTransformValid) ? CE_None : CE_Failure);
7287 : }
7288 :
7289 : /************************************************************************/
7290 : /* SetSpatialRef() */
7291 : /************************************************************************/
7292 :
7293 6 : CPLErr PDFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
7294 : {
7295 6 : if (eAccess == GA_ReadOnly)
7296 2 : GDALPamDataset::SetSpatialRef(poSRS);
7297 :
7298 6 : m_oSRS.Clear();
7299 6 : if (poSRS)
7300 5 : m_oSRS = *poSRS;
7301 6 : m_bProjDirty = true;
7302 6 : return CE_None;
7303 : }
7304 :
7305 : /************************************************************************/
7306 : /* SetGeoTransform() */
7307 : /************************************************************************/
7308 :
7309 5 : CPLErr PDFDataset::SetGeoTransform(const GDALGeoTransform >)
7310 : {
7311 5 : if (eAccess == GA_ReadOnly)
7312 2 : GDALPamDataset::SetGeoTransform(gt);
7313 :
7314 5 : m_gt = gt;
7315 5 : m_bGeoTransformValid = true;
7316 5 : m_bProjDirty = true;
7317 :
7318 : /* Reset NEATLINE if not explicitly set by the user */
7319 5 : if (!m_bNeatLineDirty)
7320 5 : SetMetadataItem("NEATLINE", nullptr);
7321 5 : return CE_None;
7322 : }
7323 :
7324 : /************************************************************************/
7325 : /* GetMetadataDomainList() */
7326 : /************************************************************************/
7327 :
7328 1 : char **PDFDataset::GetMetadataDomainList()
7329 : {
7330 1 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
7331 : TRUE, "xml:XMP", "LAYERS",
7332 1 : "EMBEDDED_METADATA", nullptr);
7333 : }
7334 :
7335 : /************************************************************************/
7336 : /* GetMetadata() */
7337 : /************************************************************************/
7338 :
7339 1898 : char **PDFDataset::GetMetadata(const char *pszDomain)
7340 : {
7341 1898 : if (pszDomain != nullptr && EQUAL(pszDomain, "EMBEDDED_METADATA"))
7342 : {
7343 1 : char **papszRet = m_oMDMD_PDF.GetMetadata(pszDomain);
7344 1 : if (papszRet)
7345 0 : return papszRet;
7346 :
7347 1 : GDALPDFObject *poCatalog = GetCatalog();
7348 1 : if (poCatalog == nullptr)
7349 0 : return nullptr;
7350 : GDALPDFObject *poFirstElt =
7351 1 : poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]");
7352 : GDALPDFObject *poF =
7353 1 : poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F");
7354 :
7355 1 : if (poFirstElt == nullptr ||
7356 1 : poFirstElt->GetType() != PDFObjectType_String ||
7357 0 : poFirstElt->GetString() != "Metadata")
7358 1 : 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 : }
7371 1897 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7372 : {
7373 115 : char **papszPAMMD = GDALPamDataset::GetMetadata(pszDomain);
7374 118 : for (char **papszIter = papszPAMMD; papszIter && *papszIter;
7375 : ++papszIter)
7376 : {
7377 3 : char *pszKey = nullptr;
7378 3 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
7379 3 : if (pszKey && pszValue)
7380 : {
7381 3 : if (m_oMDMD_PDF.GetMetadataItem(pszKey, pszDomain) == nullptr)
7382 2 : m_oMDMD_PDF.SetMetadataItem(pszKey, pszValue, pszDomain);
7383 : }
7384 3 : CPLFree(pszKey);
7385 : }
7386 115 : return m_oMDMD_PDF.GetMetadata(pszDomain);
7387 : }
7388 1782 : if (EQUAL(pszDomain, "LAYERS") || EQUAL(pszDomain, "xml:XMP") ||
7389 1754 : EQUAL(pszDomain, "SUBDATASETS"))
7390 : {
7391 29 : return m_oMDMD_PDF.GetMetadata(pszDomain);
7392 : }
7393 1753 : return GDALPamDataset::GetMetadata(pszDomain);
7394 : }
7395 :
7396 : /************************************************************************/
7397 : /* SetMetadata() */
7398 : /************************************************************************/
7399 :
7400 70 : CPLErr PDFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
7401 : {
7402 70 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7403 : {
7404 46 : char **papszMetadataDup = CSLDuplicate(papszMetadata);
7405 46 : m_oMDMD_PDF.SetMetadata(nullptr, pszDomain);
7406 :
7407 144 : for (char **papszIter = papszMetadataDup; papszIter && *papszIter;
7408 : ++papszIter)
7409 : {
7410 98 : char *pszKey = nullptr;
7411 98 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
7412 98 : if (pszKey && pszValue)
7413 : {
7414 95 : SetMetadataItem(pszKey, pszValue, pszDomain);
7415 : }
7416 98 : CPLFree(pszKey);
7417 : }
7418 46 : CSLDestroy(papszMetadataDup);
7419 46 : return CE_None;
7420 : }
7421 24 : else if (EQUAL(pszDomain, "xml:XMP"))
7422 : {
7423 22 : m_bXMPDirty = true;
7424 22 : return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain);
7425 : }
7426 2 : else if (EQUAL(pszDomain, "SUBDATASETS"))
7427 : {
7428 2 : return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain);
7429 : }
7430 : else
7431 : {
7432 0 : return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
7433 : }
7434 : }
7435 :
7436 : /************************************************************************/
7437 : /* GetMetadataItem() */
7438 : /************************************************************************/
7439 :
7440 1805 : const char *PDFDataset::GetMetadataItem(const char *pszName,
7441 : const char *pszDomain)
7442 : {
7443 1805 : if (pszDomain != nullptr && EQUAL(pszDomain, "_INTERNAL_") &&
7444 0 : pszName != nullptr && EQUAL(pszName, "PDF_LIB"))
7445 : {
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 : }
7453 1805 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
7454 : }
7455 :
7456 : /************************************************************************/
7457 : /* SetMetadataItem() */
7458 : /************************************************************************/
7459 :
7460 724 : CPLErr PDFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
7461 : const char *pszDomain)
7462 : {
7463 724 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7464 : {
7465 676 : if (EQUAL(pszName, "NEATLINE"))
7466 : {
7467 : const char *pszOldValue =
7468 194 : m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain);
7469 194 : if ((pszValue == nullptr && pszOldValue != nullptr) ||
7470 192 : (pszValue != nullptr && pszOldValue == nullptr) ||
7471 1 : (pszValue != nullptr && pszOldValue != nullptr &&
7472 1 : strcmp(pszValue, pszOldValue) != 0))
7473 : {
7474 190 : m_bProjDirty = true;
7475 190 : m_bNeatLineDirty = true;
7476 : }
7477 194 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7478 : }
7479 : else
7480 : {
7481 482 : if (EQUAL(pszName, "AUTHOR") || EQUAL(pszName, "PRODUCER") ||
7482 459 : EQUAL(pszName, "CREATOR") || EQUAL(pszName, "CREATION_DATE") ||
7483 405 : EQUAL(pszName, "SUBJECT") || EQUAL(pszName, "TITLE") ||
7484 386 : EQUAL(pszName, "KEYWORDS"))
7485 : {
7486 102 : if (pszValue == nullptr)
7487 1 : pszValue = "";
7488 : const char *pszOldValue =
7489 102 : m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain);
7490 102 : if (pszOldValue == nullptr ||
7491 2 : strcmp(pszValue, pszOldValue) != 0)
7492 : {
7493 102 : m_bInfoDirty = true;
7494 : }
7495 102 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue,
7496 102 : pszDomain);
7497 : }
7498 380 : else if (EQUAL(pszName, "DPI"))
7499 : {
7500 378 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue,
7501 378 : pszDomain);
7502 : }
7503 : else
7504 : {
7505 2 : m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7506 2 : return GDALPamDataset::SetMetadataItem(pszName, pszValue,
7507 2 : pszDomain);
7508 : }
7509 : }
7510 : }
7511 48 : else if (EQUAL(pszDomain, "xml:XMP"))
7512 : {
7513 0 : m_bXMPDirty = true;
7514 0 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7515 : }
7516 48 : else if (EQUAL(pszDomain, "SUBDATASETS"))
7517 : {
7518 0 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7519 : }
7520 : else
7521 : {
7522 48 : return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
7523 : }
7524 : }
7525 :
7526 : /************************************************************************/
7527 : /* GetGCPCount() */
7528 : /************************************************************************/
7529 :
7530 11 : int PDFDataset::GetGCPCount()
7531 : {
7532 11 : return m_nGCPCount;
7533 : }
7534 :
7535 : /************************************************************************/
7536 : /* GetGCPSpatialRef() */
7537 : /************************************************************************/
7538 :
7539 2 : const OGRSpatialReference *PDFDataset::GetGCPSpatialRef() const
7540 : {
7541 2 : if (!m_oSRS.IsEmpty() && m_nGCPCount != 0)
7542 1 : return &m_oSRS;
7543 1 : return nullptr;
7544 : }
7545 :
7546 : /************************************************************************/
7547 : /* GetGCPs() */
7548 : /************************************************************************/
7549 :
7550 2 : const GDAL_GCP *PDFDataset::GetGCPs()
7551 : {
7552 2 : return m_pasGCPList;
7553 : }
7554 :
7555 : /************************************************************************/
7556 : /* SetGCPs() */
7557 : /************************************************************************/
7558 :
7559 1 : CPLErr PDFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
7560 : const OGRSpatialReference *poSRS)
7561 : {
7562 : const char *pszGEO_ENCODING =
7563 1 : CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
7564 1 : if (nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000"))
7565 : {
7566 0 : CPLError(CE_Failure, CPLE_NotSupported,
7567 : "PDF driver only supports writing 4 GCPs when "
7568 : "GDAL_PDF_GEO_ENCODING=ISO32000.");
7569 0 : return CE_Failure;
7570 : }
7571 :
7572 : /* Free previous GCPs */
7573 1 : GDALDeinitGCPs(m_nGCPCount, m_pasGCPList);
7574 1 : CPLFree(m_pasGCPList);
7575 :
7576 : /* Duplicate in GCPs */
7577 1 : m_nGCPCount = nGCPCountIn;
7578 1 : m_pasGCPList = GDALDuplicateGCPs(m_nGCPCount, pasGCPListIn);
7579 :
7580 1 : m_oSRS.Clear();
7581 1 : if (poSRS)
7582 1 : m_oSRS = *poSRS;
7583 :
7584 1 : m_bProjDirty = true;
7585 :
7586 : /* Reset NEATLINE if not explicitly set by the user */
7587 1 : if (!m_bNeatLineDirty)
7588 1 : SetMetadataItem("NEATLINE", nullptr);
7589 :
7590 1 : return CE_None;
7591 : }
7592 :
7593 : #endif // #ifdef HAVE_PDF_READ_SUPPORT
7594 :
7595 : /************************************************************************/
7596 : /* GDALPDFOpen() */
7597 : /************************************************************************/
7598 :
7599 53 : 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 : {
7607 : #ifdef HAVE_PDF_READ_SUPPORT
7608 106 : GDALOpenInfo oOpenInfo(pszFilename, eAccess);
7609 106 : return PDFDataset::Open(&oOpenInfo);
7610 : #else
7611 : return nullptr;
7612 : #endif
7613 : }
7614 :
7615 : /************************************************************************/
7616 : /* GDALPDFUnloadDriver() */
7617 : /************************************************************************/
7618 :
7619 8 : static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver *poDriver)
7620 : {
7621 : #ifdef HAVE_POPPLER
7622 8 : if (hGlobalParamsMutex != nullptr)
7623 3 : CPLDestroyMutex(hGlobalParamsMutex);
7624 : #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 8 : }
7672 :
7673 : /************************************************************************/
7674 : /* PDFSanitizeLayerName() */
7675 : /************************************************************************/
7676 :
7677 660 : CPLString PDFSanitizeLayerName(const char *pszName)
7678 : {
7679 660 : if (!CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES")))
7680 0 : return pszName;
7681 :
7682 1320 : CPLString osName;
7683 17194 : for (int i = 0; pszName[i] != '\0'; i++)
7684 : {
7685 16534 : if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',')
7686 976 : osName += "_";
7687 15558 : else if (pszName[i] != '"')
7688 15558 : osName += pszName[i];
7689 : }
7690 660 : if (osName.empty())
7691 2 : osName = "unnamed";
7692 660 : return osName;
7693 : }
7694 :
7695 : /************************************************************************/
7696 : /* GDALPDFListLayersAlgorithm */
7697 : /************************************************************************/
7698 :
7699 : #ifdef HAVE_PDF_READ_SUPPORT
7700 :
7701 : class GDALPDFListLayersAlgorithm final : public GDALAlgorithm
7702 : {
7703 : public:
7704 25 : GDALPDFListLayersAlgorithm()
7705 25 : : GDALAlgorithm("list-layers",
7706 50 : std::string("List layers of a PDF dataset"),
7707 75 : "/drivers/raster/pdf.html")
7708 : {
7709 25 : AddInputDatasetArg(&m_dataset, GDAL_OF_RASTER | GDAL_OF_VECTOR);
7710 25 : AddOutputFormatArg(&m_format).SetDefault(m_format).SetChoices("json",
7711 25 : "text");
7712 25 : AddOutputStringArg(&m_output);
7713 25 : }
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 3 : bool GDALPDFListLayersAlgorithm::RunImpl(GDALProgressFunc, void *)
7725 : {
7726 3 : auto poDS = dynamic_cast<PDFDataset *>(m_dataset.GetDatasetRef());
7727 3 : if (!poDS)
7728 : {
7729 1 : ReportError(CE_Failure, CPLE_AppDefined, "%s is not a PDF",
7730 1 : m_dataset.GetName().c_str());
7731 1 : return false;
7732 : }
7733 2 : if (m_format == "json")
7734 : {
7735 2 : CPLJSonStreamingWriter oWriter(nullptr, nullptr);
7736 1 : oWriter.StartArray();
7737 10 : for (const auto &[key, value] : cpl::IterateNameValue(
7738 11 : const_cast<CSLConstList>(poDS->GetMetadata("LAYERS"))))
7739 : {
7740 5 : CPL_IGNORE_RET_VAL(key);
7741 5 : oWriter.Add(value);
7742 : }
7743 1 : oWriter.EndArray();
7744 1 : m_output = oWriter.GetString();
7745 1 : m_output += '\n';
7746 : }
7747 : else
7748 : {
7749 10 : for (const auto &[key, value] : cpl::IterateNameValue(
7750 11 : const_cast<CSLConstList>(poDS->GetMetadata("LAYERS"))))
7751 : {
7752 5 : CPL_IGNORE_RET_VAL(key);
7753 5 : m_output += value;
7754 5 : m_output += '\n';
7755 : }
7756 : }
7757 2 : return true;
7758 : }
7759 :
7760 : /************************************************************************/
7761 : /* GDALPDFInstantiateAlgorithm() */
7762 : /************************************************************************/
7763 :
7764 : static GDALAlgorithm *
7765 25 : GDALPDFInstantiateAlgorithm(const std::vector<std::string> &aosPath)
7766 : {
7767 25 : if (aosPath.size() == 1 && aosPath[0] == "list-layers")
7768 : {
7769 25 : return std::make_unique<GDALPDFListLayersAlgorithm>().release();
7770 : }
7771 : else
7772 : {
7773 0 : return nullptr;
7774 : }
7775 : }
7776 :
7777 : #endif // HAVE_PDF_READ_SUPPORT
7778 :
7779 : /************************************************************************/
7780 : /* GDALRegister_PDF() */
7781 : /************************************************************************/
7782 :
7783 17 : void GDALRegister_PDF()
7784 :
7785 : {
7786 17 : if (!GDAL_CHECK_VERSION("PDF driver"))
7787 0 : return;
7788 :
7789 17 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
7790 0 : return;
7791 :
7792 17 : GDALDriver *poDriver = new GDALDriver();
7793 17 : PDFDriverSetCommonMetadata(poDriver);
7794 :
7795 : #ifdef HAVE_PDF_READ_SUPPORT
7796 17 : poDriver->pfnOpen = PDFDataset::OpenWrapper;
7797 17 : poDriver->pfnInstantiateAlgorithm = GDALPDFInstantiateAlgorithm;
7798 : #endif // HAVE_PDF_READ_SUPPORT
7799 :
7800 17 : poDriver->pfnCreateCopy = GDALPDFCreateCopy;
7801 17 : poDriver->pfnCreate = PDFWritableVectorDataset::Create;
7802 17 : poDriver->pfnUnloadDriver = GDALPDFUnloadDriver;
7803 :
7804 17 : GetGDALDriverManager()->RegisterDriver(poDriver);
7805 : }
|