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_vsi_virtual.h"
23 : #include "cpl_spawn.h"
24 : #include "cpl_string.h"
25 : #include "gdal_frmts.h"
26 : #include "ogr_spatialref.h"
27 : #include "ogr_geometry.h"
28 :
29 : #ifdef HAVE_POPPLER
30 : #include "cpl_multiproc.h"
31 : #include "pdfio.h"
32 : #endif // HAVE_POPPLER
33 :
34 : #include "pdfcreatecopy.h"
35 :
36 : #include "pdfdrivercore.h"
37 :
38 : #include <algorithm>
39 : #include <cassert>
40 : #include <limits>
41 : #include <set>
42 :
43 : #ifdef HAVE_PDFIUM
44 : // To be able to use
45 : // https://github.com/rouault/pdfium_build_gdal_3_5/releases/download/v1_pdfium_5106/install-win10-vs2019-x64-rev5106.zip
46 : // with newer Visual Studio versions.
47 : // Trick from https://github.com/conan-io/conan-center-index/issues/4826
48 : #if _MSC_VER >= 1932 // Visual Studio 2022 version 17.2+
49 : #pragma comment( \
50 : linker, \
51 : "/alternatename:__imp___std_init_once_complete=__imp_InitOnceComplete")
52 : #pragma comment( \
53 : linker, \
54 : "/alternatename:__imp___std_init_once_begin_initialize=__imp_InitOnceBeginInitialize")
55 : #endif
56 : #endif
57 :
58 : /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport
59 : * -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */
60 :
61 : #ifdef HAVE_PDF_READ_SUPPORT
62 :
63 : static double Get(GDALPDFObject *poObj, int nIndice = -1);
64 :
65 : #ifdef HAVE_POPPLER
66 :
67 : static CPLMutex *hGlobalParamsMutex = nullptr;
68 :
69 : /************************************************************************/
70 : /* GDALPDFOutputDev */
71 : /************************************************************************/
72 :
73 : class GDALPDFOutputDev : public SplashOutputDev
74 : {
75 : private:
76 : int bEnableVector;
77 : int bEnableText;
78 : int bEnableBitmap;
79 :
80 0 : void skipBytes(Stream *str, int width, int height, int nComps, int nBits)
81 : {
82 0 : int nVals = width * nComps;
83 0 : int nLineSize = (nVals * nBits + 7) >> 3;
84 0 : int nBytes = nLineSize * height;
85 0 : for (int i = 0; i < nBytes; i++)
86 : {
87 0 : if (str->getChar() == EOF)
88 0 : break;
89 : }
90 0 : }
91 :
92 : public:
93 47 : GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
94 : bool reverseVideoA, SplashColorPtr paperColorA)
95 47 : : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA,
96 : paperColorA),
97 47 : bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE)
98 : {
99 47 : }
100 :
101 11 : void SetEnableVector(int bFlag)
102 : {
103 11 : bEnableVector = bFlag;
104 11 : }
105 :
106 11 : void SetEnableText(int bFlag)
107 : {
108 11 : bEnableText = bFlag;
109 11 : }
110 :
111 11 : void SetEnableBitmap(int bFlag)
112 : {
113 11 : bEnableBitmap = bFlag;
114 11 : }
115 :
116 47 : virtual void startPage(int pageNum, GfxState *state, XRef *xrefIn) override
117 : {
118 47 : SplashOutputDev::startPage(pageNum, state, xrefIn);
119 47 : SplashBitmap *poBitmap = getBitmap();
120 94 : memset(poBitmap->getDataPtr(), 255,
121 47 : static_cast<size_t>(poBitmap->getRowSize()) *
122 47 : poBitmap->getHeight());
123 47 : }
124 :
125 1673 : virtual void stroke(GfxState *state) override
126 : {
127 1673 : if (bEnableVector)
128 1664 : SplashOutputDev::stroke(state);
129 1673 : }
130 :
131 8 : virtual void fill(GfxState *state) override
132 : {
133 8 : if (bEnableVector)
134 8 : SplashOutputDev::fill(state);
135 8 : }
136 :
137 38 : virtual void eoFill(GfxState *state) override
138 : {
139 38 : if (bEnableVector)
140 32 : SplashOutputDev::eoFill(state);
141 38 : }
142 :
143 4295 : virtual void drawChar(GfxState *state, double x, double y, double dx,
144 : double dy, double originX, double originY,
145 : CharCode code, int nBytes, const Unicode *u,
146 : int uLen) override
147 : {
148 4295 : if (bEnableText)
149 4259 : SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY,
150 : code, nBytes, u, uLen);
151 4295 : }
152 :
153 681 : virtual void beginTextObject(GfxState *state) override
154 : {
155 681 : if (bEnableText)
156 678 : SplashOutputDev::beginTextObject(state);
157 681 : }
158 :
159 681 : virtual void endTextObject(GfxState *state) override
160 : {
161 681 : if (bEnableText)
162 678 : SplashOutputDev::endTextObject(state);
163 681 : }
164 :
165 0 : virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
166 : int width, int height, bool invert,
167 : bool interpolate, bool inlineImg) override
168 : {
169 0 : if (bEnableBitmap)
170 0 : SplashOutputDev::drawImageMask(state, ref, str, width, height,
171 : invert, interpolate, inlineImg);
172 : else
173 : {
174 0 : str->reset();
175 0 : if (inlineImg)
176 : {
177 0 : skipBytes(str, width, height, 1, 1);
178 : }
179 0 : str->close();
180 : }
181 0 : }
182 :
183 0 : virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref,
184 : Stream *str, int width, int height,
185 : bool invert, bool inlineImg,
186 : double *baseMatrix) override
187 : {
188 0 : if (bEnableBitmap)
189 0 : SplashOutputDev::setSoftMaskFromImageMask(
190 : state, ref, str, width, height, invert, inlineImg, baseMatrix);
191 : else
192 0 : str->close();
193 0 : }
194 :
195 0 : virtual void unsetSoftMaskFromImageMask(GfxState *state,
196 : double *baseMatrix) override
197 : {
198 0 : if (bEnableBitmap)
199 0 : SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix);
200 0 : }
201 :
202 34 : virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width,
203 : int height, GfxImageColorMap *colorMap,
204 : bool interpolate, const int *maskColors,
205 : bool inlineImg) override
206 : {
207 34 : if (bEnableBitmap)
208 31 : SplashOutputDev::drawImage(state, ref, str, width, height, colorMap,
209 : interpolate, maskColors, inlineImg);
210 : else
211 : {
212 3 : str->reset();
213 3 : if (inlineImg)
214 : {
215 0 : skipBytes(str, width, height, colorMap->getNumPixelComps(),
216 : colorMap->getBits());
217 : }
218 3 : str->close();
219 : }
220 34 : }
221 :
222 0 : virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
223 : int width, int height,
224 : GfxImageColorMap *colorMap, bool interpolate,
225 : Stream *maskStr, int maskWidth, int maskHeight,
226 : bool maskInvert, bool maskInterpolate) override
227 : {
228 0 : if (bEnableBitmap)
229 0 : SplashOutputDev::drawMaskedImage(
230 : state, ref, str, width, height, colorMap, interpolate, maskStr,
231 : maskWidth, maskHeight, maskInvert, maskInterpolate);
232 : else
233 0 : str->close();
234 0 : }
235 :
236 2 : virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
237 : int width, int height,
238 : GfxImageColorMap *colorMap,
239 : bool interpolate, Stream *maskStr,
240 : int maskWidth, int maskHeight,
241 : GfxImageColorMap *maskColorMap,
242 : bool maskInterpolate) override
243 : {
244 2 : if (bEnableBitmap)
245 : {
246 2 : if (maskColorMap->getBits() <=
247 : 0) /* workaround poppler bug (robustness) */
248 : {
249 0 : str->close();
250 0 : return;
251 : }
252 2 : SplashOutputDev::drawSoftMaskedImage(
253 : state, ref, str, width, height, colorMap, interpolate, maskStr,
254 : maskWidth, maskHeight, maskColorMap, maskInterpolate);
255 : }
256 : else
257 0 : str->close();
258 : }
259 : };
260 :
261 : #endif // ~ HAVE_POPPLER
262 :
263 : /************************************************************************/
264 : /* Dump routines */
265 : /************************************************************************/
266 :
267 : class GDALPDFDumper
268 : {
269 : private:
270 : FILE *f = nullptr;
271 : const int nDepthLimit;
272 : std::set<int> aoSetObjectExplored{};
273 : const bool bDumpParent;
274 :
275 : void DumpSimplified(GDALPDFObject *poObj);
276 :
277 : CPL_DISALLOW_COPY_ASSIGN(GDALPDFDumper)
278 :
279 : public:
280 2 : GDALPDFDumper(const char *pszFilename, const char *pszDumpFile,
281 : int nDepthLimitIn = -1)
282 2 : : nDepthLimit(nDepthLimitIn),
283 2 : bDumpParent(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE"))
284 : {
285 2 : if (strcmp(pszDumpFile, "stderr") == 0)
286 0 : f = stderr;
287 2 : else if (EQUAL(pszDumpFile, "YES"))
288 0 : f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)),
289 : "wt");
290 : else
291 2 : f = fopen(pszDumpFile, "wt");
292 2 : if (f == nullptr)
293 0 : f = stderr;
294 2 : }
295 :
296 2 : ~GDALPDFDumper()
297 2 : {
298 2 : if (f != stderr)
299 2 : fclose(f);
300 2 : }
301 :
302 : void Dump(GDALPDFObject *poObj, int nDepth = 0);
303 : void Dump(GDALPDFDictionary *poDict, int nDepth = 0);
304 : void Dump(GDALPDFArray *poArray, int nDepth = 0);
305 : };
306 :
307 6 : void GDALPDFDumper::Dump(GDALPDFArray *poArray, int nDepth)
308 : {
309 6 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
310 0 : return;
311 :
312 6 : int nLength = poArray->GetLength();
313 : int i;
314 12 : CPLString osIndent;
315 28 : for (i = 0; i < nDepth; i++)
316 22 : osIndent += " ";
317 16 : for (i = 0; i < nLength; i++)
318 : {
319 10 : fprintf(f, "%sItem[%d]:", osIndent.c_str(), i);
320 10 : GDALPDFObject *poObj = nullptr;
321 10 : if ((poObj = poArray->Get(i)) != nullptr)
322 : {
323 10 : if (poObj->GetType() == PDFObjectType_String ||
324 10 : poObj->GetType() == PDFObjectType_Null ||
325 10 : poObj->GetType() == PDFObjectType_Bool ||
326 10 : poObj->GetType() == PDFObjectType_Int ||
327 22 : poObj->GetType() == PDFObjectType_Real ||
328 2 : poObj->GetType() == PDFObjectType_Name)
329 : {
330 8 : fprintf(f, " ");
331 8 : DumpSimplified(poObj);
332 8 : fprintf(f, "\n");
333 : }
334 : else
335 : {
336 2 : fprintf(f, "\n");
337 2 : Dump(poObj, nDepth + 1);
338 : }
339 : }
340 : }
341 : }
342 :
343 986 : void GDALPDFDumper::DumpSimplified(GDALPDFObject *poObj)
344 : {
345 986 : switch (poObj->GetType())
346 : {
347 0 : case PDFObjectType_String:
348 0 : fprintf(f, "%s (string)", poObj->GetString().c_str());
349 0 : break;
350 :
351 0 : case PDFObjectType_Null:
352 0 : fprintf(f, "null");
353 0 : break;
354 :
355 0 : case PDFObjectType_Bool:
356 0 : fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false");
357 0 : break;
358 :
359 494 : case PDFObjectType_Int:
360 494 : fprintf(f, "%d (int)", poObj->GetInt());
361 494 : break;
362 :
363 0 : case PDFObjectType_Real:
364 0 : fprintf(f, "%f (real)", poObj->GetReal());
365 0 : break;
366 :
367 492 : case PDFObjectType_Name:
368 492 : fprintf(f, "%s (name)", poObj->GetName().c_str());
369 492 : break;
370 :
371 0 : default:
372 0 : fprintf(f, "unknown !");
373 0 : break;
374 : }
375 986 : }
376 :
377 140 : void GDALPDFDumper::Dump(GDALPDFObject *poObj, int nDepth)
378 : {
379 140 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
380 2 : return;
381 :
382 : int i;
383 140 : CPLString osIndent;
384 1032 : for (i = 0; i < nDepth; i++)
385 892 : osIndent += " ";
386 140 : fprintf(f, "%sType = %s", osIndent.c_str(), poObj->GetTypeName());
387 140 : int nRefNum = poObj->GetRefNum().toInt();
388 140 : if (nRefNum != 0)
389 132 : fprintf(f, ", Num = %d, Gen = %d", nRefNum, poObj->GetRefGen());
390 140 : fprintf(f, "\n");
391 :
392 140 : if (nRefNum != 0)
393 : {
394 132 : if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end())
395 2 : return;
396 130 : aoSetObjectExplored.insert(nRefNum);
397 : }
398 :
399 138 : switch (poObj->GetType())
400 : {
401 6 : case PDFObjectType_Array:
402 6 : Dump(poObj->GetArray(), nDepth + 1);
403 6 : break;
404 :
405 132 : case PDFObjectType_Dictionary:
406 132 : Dump(poObj->GetDictionary(), nDepth + 1);
407 132 : break;
408 :
409 0 : case PDFObjectType_String:
410 : case PDFObjectType_Null:
411 : case PDFObjectType_Bool:
412 : case PDFObjectType_Int:
413 : case PDFObjectType_Real:
414 : case PDFObjectType_Name:
415 0 : fprintf(f, "%s", osIndent.c_str());
416 0 : DumpSimplified(poObj);
417 0 : fprintf(f, "\n");
418 0 : break;
419 :
420 0 : default:
421 0 : fprintf(f, "%s", osIndent.c_str());
422 0 : fprintf(f, "unknown !\n");
423 0 : break;
424 : }
425 :
426 138 : GDALPDFStream *poStream = poObj->GetStream();
427 138 : if (poStream != nullptr)
428 : {
429 122 : fprintf(f,
430 : "%sHas stream (" CPL_FRMT_GIB
431 : " uncompressed bytes, " CPL_FRMT_GIB " raw bytes)\n",
432 122 : osIndent.c_str(), static_cast<GIntBig>(poStream->GetLength()),
433 122 : static_cast<GIntBig>(poStream->GetRawLength()));
434 : }
435 : }
436 :
437 132 : void GDALPDFDumper::Dump(GDALPDFDictionary *poDict, int nDepth)
438 : {
439 132 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
440 0 : return;
441 :
442 264 : CPLString osIndent;
443 1128 : for (int i = 0; i < nDepth; i++)
444 996 : osIndent += " ";
445 132 : int i = 0;
446 132 : const auto &oMap = poDict->GetValues();
447 1246 : for (const auto &[osKey, poObj] : oMap)
448 : {
449 1114 : fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, osKey.c_str());
450 1114 : ++i;
451 1114 : if (osKey == "Parent" && !bDumpParent)
452 : {
453 0 : if (poObj->GetRefNum().toBool())
454 0 : fprintf(f, ", Num = %d, Gen = %d", poObj->GetRefNum().toInt(),
455 0 : poObj->GetRefGen());
456 0 : fprintf(f, "\n");
457 0 : continue;
458 : }
459 1114 : if (poObj != nullptr)
460 : {
461 1114 : if (poObj->GetType() == PDFObjectType_String ||
462 1114 : poObj->GetType() == PDFObjectType_Null ||
463 1114 : poObj->GetType() == PDFObjectType_Bool ||
464 1114 : poObj->GetType() == PDFObjectType_Int ||
465 2856 : poObj->GetType() == PDFObjectType_Real ||
466 628 : poObj->GetType() == PDFObjectType_Name)
467 : {
468 978 : fprintf(f, " = ");
469 978 : DumpSimplified(poObj);
470 978 : fprintf(f, "\n");
471 : }
472 : else
473 : {
474 136 : fprintf(f, "\n");
475 136 : Dump(poObj, nDepth + 1);
476 : }
477 : }
478 : }
479 : }
480 :
481 : /************************************************************************/
482 : /* PDFRasterBand() */
483 : /************************************************************************/
484 :
485 1359 : PDFRasterBand::PDFRasterBand(PDFDataset *poDSIn, int nBandIn,
486 1359 : int nResolutionLevelIn)
487 1359 : : nResolutionLevel(nResolutionLevelIn)
488 : {
489 1359 : poDS = poDSIn;
490 1359 : nBand = nBandIn;
491 :
492 1359 : eDataType = GDT_Byte;
493 :
494 1359 : if (nResolutionLevel > 0)
495 : {
496 8 : nBlockXSize = 256;
497 8 : nBlockYSize = 256;
498 8 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
499 : }
500 1351 : else if (poDSIn->m_nBlockXSize)
501 : {
502 72 : nBlockXSize = poDSIn->m_nBlockXSize;
503 72 : nBlockYSize = poDSIn->m_nBlockYSize;
504 72 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
505 : }
506 1279 : else if (poDSIn->GetRasterXSize() <
507 1279 : 64 * 1024 * 1024 / poDSIn->GetRasterYSize())
508 : {
509 1275 : nBlockXSize = poDSIn->GetRasterXSize();
510 1275 : nBlockYSize = 1;
511 : }
512 : else
513 : {
514 4 : nBlockXSize = std::min(1024, poDSIn->GetRasterXSize());
515 4 : nBlockYSize = std::min(1024, poDSIn->GetRasterYSize());
516 4 : poDSIn->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
517 : }
518 1359 : }
519 :
520 : /************************************************************************/
521 : /* InitOverviews() */
522 : /************************************************************************/
523 :
524 10 : void PDFDataset::InitOverviews()
525 : {
526 : #ifdef HAVE_PDFIUM
527 : // Only if used pdfium, make "arbitrary overviews"
528 : // Blocks are 256x256
529 14 : if (m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() &&
530 4 : m_apoOvrDSBackup.empty())
531 : {
532 3 : int nXSize = nRasterXSize;
533 3 : int nYSize = nRasterYSize;
534 3 : constexpr int minSize = 256;
535 3 : int nDiscard = 1;
536 5 : while (nXSize > minSize || nYSize > minSize)
537 : {
538 2 : nXSize = (nXSize + 1) / 2;
539 2 : nYSize = (nYSize + 1) / 2;
540 :
541 2 : auto poOvrDS = std::make_unique<PDFDataset>(this, nXSize, nYSize);
542 :
543 10 : for (int i = 0; i < nBands; i++)
544 16 : poOvrDS->SetBand(
545 8 : i + 1, new PDFRasterBand(poOvrDS.get(), i + 1, nDiscard));
546 :
547 2 : m_apoOvrDS.emplace_back(std::move(poOvrDS));
548 2 : ++nDiscard;
549 : }
550 : }
551 : #endif
552 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
553 16 : if (!m_bUseLib.test(PDFLIB_PDFIUM) && m_apoOvrDS.empty() &&
554 16 : m_apoOvrDSBackup.empty() && m_osUserPwd != "ASK_INTERACTIVE")
555 : {
556 1 : int nXSize = nRasterXSize;
557 1 : int nYSize = nRasterYSize;
558 1 : constexpr int minSize = 256;
559 1 : double dfDPI = m_dfDPI;
560 3 : while (nXSize > minSize || nYSize > minSize)
561 : {
562 2 : nXSize = (nXSize + 1) / 2;
563 2 : nYSize = (nYSize + 1) / 2;
564 2 : dfDPI /= 2;
565 :
566 2 : GDALOpenInfo oOpenInfo(GetDescription(), GA_ReadOnly);
567 2 : CPLStringList aosOpenOptions(CSLDuplicate(papszOpenOptions));
568 2 : aosOpenOptions.SetNameValue("DPI", CPLSPrintf("%g", dfDPI));
569 2 : aosOpenOptions.SetNameValue("BANDS", CPLSPrintf("%d", nBands));
570 2 : aosOpenOptions.SetNameValue("@OPEN_FOR_OVERVIEW", "YES");
571 2 : if (!m_osUserPwd.empty())
572 0 : aosOpenOptions.SetNameValue("USER_PWD", m_osUserPwd.c_str());
573 2 : oOpenInfo.papszOpenOptions = aosOpenOptions.List();
574 2 : auto poOvrDS = std::unique_ptr<PDFDataset>(Open(&oOpenInfo));
575 2 : if (!poOvrDS || poOvrDS->nBands != nBands)
576 0 : break;
577 2 : poOvrDS->m_bIsOvrDS = true;
578 2 : m_apoOvrDS.emplace_back(std::move(poOvrDS));
579 : }
580 : }
581 : #endif
582 10 : }
583 :
584 : /************************************************************************/
585 : /* GetColorInterpretation() */
586 : /************************************************************************/
587 :
588 60 : GDALColorInterp PDFRasterBand::GetColorInterpretation()
589 : {
590 60 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
591 60 : if (poGDS->nBands == 1)
592 0 : return GCI_GrayIndex;
593 : else
594 60 : return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1));
595 : }
596 :
597 : /************************************************************************/
598 : /* GetOverviewCount() */
599 : /************************************************************************/
600 :
601 19 : int PDFRasterBand::GetOverviewCount()
602 : {
603 19 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
604 19 : if (poGDS->m_bIsOvrDS)
605 0 : return 0;
606 19 : if (GDALPamRasterBand::GetOverviewCount() > 0)
607 9 : return GDALPamRasterBand::GetOverviewCount();
608 : else
609 : {
610 10 : poGDS->InitOverviews();
611 10 : return static_cast<int>(poGDS->m_apoOvrDS.size());
612 : }
613 : }
614 :
615 : /************************************************************************/
616 : /* GetOverview() */
617 : /************************************************************************/
618 :
619 8 : GDALRasterBand *PDFRasterBand::GetOverview(int iOverviewIndex)
620 : {
621 8 : if (GDALPamRasterBand::GetOverviewCount() > 0)
622 2 : return GDALPamRasterBand::GetOverview(iOverviewIndex);
623 :
624 6 : else if (iOverviewIndex < 0 || iOverviewIndex >= GetOverviewCount())
625 4 : return nullptr;
626 : else
627 : {
628 2 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
629 2 : return poGDS->m_apoOvrDS[iOverviewIndex]->GetRasterBand(nBand);
630 : }
631 : }
632 :
633 : /************************************************************************/
634 : /* ~PDFRasterBand() */
635 : /************************************************************************/
636 :
637 2718 : PDFRasterBand::~PDFRasterBand()
638 : {
639 2718 : }
640 :
641 : /************************************************************************/
642 : /* IReadBlockFromTile() */
643 : /************************************************************************/
644 :
645 320 : CPLErr PDFRasterBand::IReadBlockFromTile(int nBlockXOff, int nBlockYOff,
646 : void *pImage)
647 :
648 : {
649 320 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
650 :
651 320 : int nReqXSize = nBlockXSize;
652 320 : int nReqYSize = nBlockYSize;
653 320 : if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
654 40 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
655 320 : if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
656 48 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
657 :
658 320 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
659 320 : int iTile = poGDS->m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff];
660 320 : if (iTile < 0)
661 : {
662 0 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
663 0 : return CE_None;
664 : }
665 :
666 320 : GDALPDFTileDesc &sTile = poGDS->m_asTiles[iTile];
667 320 : GDALPDFObject *poImage = sTile.poImage;
668 :
669 320 : if (nBand == 4)
670 : {
671 60 : GDALPDFDictionary *poImageDict = poImage->GetDictionary();
672 60 : GDALPDFObject *poSMask = poImageDict->Get("SMask");
673 120 : if (poSMask != nullptr &&
674 60 : poSMask->GetType() == PDFObjectType_Dictionary)
675 : {
676 60 : GDALPDFDictionary *poSMaskDict = poSMask->GetDictionary();
677 60 : GDALPDFObject *poWidth = poSMaskDict->Get("Width");
678 60 : GDALPDFObject *poHeight = poSMaskDict->Get("Height");
679 60 : GDALPDFObject *poColorSpace = poSMaskDict->Get("ColorSpace");
680 : GDALPDFObject *poBitsPerComponent =
681 60 : poSMaskDict->Get("BitsPerComponent");
682 60 : double dfBits = 0;
683 60 : if (poBitsPerComponent)
684 60 : dfBits = Get(poBitsPerComponent);
685 60 : if (poWidth && Get(poWidth) == nReqXSize && poHeight &&
686 60 : Get(poHeight) == nReqYSize && poColorSpace &&
687 120 : poColorSpace->GetType() == PDFObjectType_Name &&
688 224 : poColorSpace->GetName() == "DeviceGray" &&
689 44 : (dfBits == 1 || dfBits == 8))
690 : {
691 60 : GDALPDFStream *poStream = poSMask->GetStream();
692 60 : GByte *pabyStream = nullptr;
693 :
694 60 : if (poStream == nullptr)
695 0 : return CE_Failure;
696 :
697 60 : pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes());
698 60 : if (pabyStream == nullptr)
699 0 : return CE_Failure;
700 :
701 60 : const int nReqXSize1 = (nReqXSize + 7) / 8;
702 104 : if ((dfBits == 8 &&
703 44 : static_cast<size_t>(poStream->GetLength()) !=
704 120 : static_cast<size_t>(nReqXSize) * nReqYSize) ||
705 16 : (dfBits == 1 &&
706 16 : static_cast<size_t>(poStream->GetLength()) !=
707 16 : static_cast<size_t>(nReqXSize1) * nReqYSize))
708 : {
709 0 : VSIFree(pabyStream);
710 0 : return CE_Failure;
711 : }
712 :
713 60 : GByte *pabyData = static_cast<GByte *>(pImage);
714 60 : if (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize)
715 : {
716 20 : memset(pabyData, 0,
717 20 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
718 : }
719 :
720 60 : if (dfBits == 8)
721 : {
722 1372 : for (int j = 0; j < nReqYSize; j++)
723 : {
724 43824 : for (int i = 0; i < nReqXSize; i++)
725 : {
726 42496 : pabyData[j * nBlockXSize + i] =
727 42496 : pabyStream[j * nReqXSize + i];
728 : }
729 : }
730 : }
731 : else
732 : {
733 488 : for (int j = 0; j < nReqYSize; j++)
734 : {
735 6576 : for (int i = 0; i < nReqXSize; i++)
736 : {
737 6104 : if (pabyStream[j * nReqXSize1 + i / 8] &
738 6104 : (1 << (7 - (i % 8))))
739 1792 : pabyData[j * nBlockXSize + i] = 255;
740 : else
741 4312 : pabyData[j * nBlockXSize + i] = 0;
742 : }
743 : }
744 : }
745 :
746 60 : VSIFree(pabyStream);
747 60 : return CE_None;
748 : }
749 : }
750 :
751 0 : memset(pImage, 255, static_cast<size_t>(nBlockXSize) * nBlockYSize);
752 0 : return CE_None;
753 : }
754 :
755 260 : if (poGDS->m_nLastBlockXOff == nBlockXOff &&
756 0 : poGDS->m_nLastBlockYOff == nBlockYOff &&
757 0 : poGDS->m_pabyCachedData != nullptr)
758 : {
759 : #ifdef DEBUG
760 0 : CPLDebug("PDF", "Using cached block (%d, %d)", nBlockXOff, nBlockYOff);
761 : #endif
762 : // do nothing
763 : }
764 : else
765 : {
766 260 : if (!poGDS->m_bTried)
767 : {
768 10 : poGDS->m_bTried = true;
769 10 : poGDS->m_pabyCachedData =
770 10 : static_cast<GByte *>(VSIMalloc3(3, nBlockXSize, nBlockYSize));
771 : }
772 260 : if (poGDS->m_pabyCachedData == nullptr)
773 0 : return CE_Failure;
774 :
775 260 : GDALPDFStream *poStream = poImage->GetStream();
776 260 : GByte *pabyStream = nullptr;
777 :
778 260 : if (poStream == nullptr)
779 0 : return CE_Failure;
780 :
781 260 : pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes());
782 260 : if (pabyStream == nullptr)
783 0 : return CE_Failure;
784 :
785 260 : if (static_cast<size_t>(poStream->GetLength()) !=
786 260 : static_cast<size_t>(sTile.nBands) * nReqXSize * nReqYSize)
787 : {
788 0 : VSIFree(pabyStream);
789 0 : return CE_Failure;
790 : }
791 :
792 260 : memcpy(poGDS->m_pabyCachedData, pabyStream,
793 260 : static_cast<size_t>(poStream->GetLength()));
794 260 : VSIFree(pabyStream);
795 260 : poGDS->m_nLastBlockXOff = nBlockXOff;
796 260 : poGDS->m_nLastBlockYOff = nBlockYOff;
797 : }
798 :
799 260 : GByte *pabyData = static_cast<GByte *>(pImage);
800 260 : if (nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize))
801 : {
802 60 : memset(pabyData, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
803 : }
804 :
805 260 : if (poGDS->nBands >= 3 && sTile.nBands == 3)
806 : {
807 5580 : for (int j = 0; j < nReqYSize; j++)
808 : {
809 151200 : for (int i = 0; i < nReqXSize; i++)
810 : {
811 145800 : pabyData[j * nBlockXSize + i] =
812 : poGDS
813 145800 : ->m_pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1];
814 : }
815 180 : }
816 : }
817 80 : else if (sTile.nBands == 1)
818 : {
819 12368 : for (int j = 0; j < nReqYSize; j++)
820 : {
821 2109440 : for (int i = 0; i < nReqXSize; i++)
822 : {
823 2097150 : pabyData[j * nBlockXSize + i] =
824 2097150 : poGDS->m_pabyCachedData[j * nReqXSize + i];
825 : }
826 : }
827 : }
828 :
829 260 : return CE_None;
830 : }
831 :
832 : /************************************************************************/
833 : /* GetSuggestedBlockAccessPattern() */
834 : /************************************************************************/
835 :
836 : GDALSuggestedBlockAccessPattern
837 2 : PDFRasterBand::GetSuggestedBlockAccessPattern() const
838 : {
839 2 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
840 2 : if (!poGDS->m_aiTiles.empty())
841 0 : return GSBAP_RANDOM;
842 2 : return GSBAP_LARGEST_CHUNK_POSSIBLE;
843 : }
844 :
845 : /************************************************************************/
846 : /* IReadBlock() */
847 : /************************************************************************/
848 :
849 50185 : CPLErr PDFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
850 :
851 : {
852 50185 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
853 :
854 50185 : if (!poGDS->m_aiTiles.empty())
855 : {
856 320 : if (IReadBlockFromTile(nBlockXOff, nBlockYOff, pImage) == CE_None)
857 : {
858 320 : return CE_None;
859 : }
860 : else
861 : {
862 0 : poGDS->m_aiTiles.resize(0);
863 0 : poGDS->m_bTried = false;
864 0 : CPLFree(poGDS->m_pabyCachedData);
865 0 : poGDS->m_pabyCachedData = nullptr;
866 0 : poGDS->m_nLastBlockXOff = -1;
867 0 : poGDS->m_nLastBlockYOff = -1;
868 : }
869 : }
870 :
871 49865 : int nReqXSize = nBlockXSize;
872 49865 : int nReqYSize = nBlockYSize;
873 49865 : if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
874 0 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
875 49865 : if (nBlockYSize == 1)
876 49860 : nReqYSize = nRasterYSize;
877 5 : else if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
878 0 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
879 :
880 49865 : if (!poGDS->m_bTried)
881 : {
882 102 : poGDS->m_bTried = true;
883 102 : if (nBlockYSize == 1)
884 300 : poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3(
885 100 : std::max(3, poGDS->nBands), nRasterXSize, nRasterYSize));
886 : else
887 6 : poGDS->m_pabyCachedData = static_cast<GByte *>(VSIMalloc3(
888 2 : std::max(3, poGDS->nBands), nBlockXSize, nBlockYSize));
889 : }
890 49865 : if (poGDS->m_pabyCachedData == nullptr)
891 0 : return CE_Failure;
892 :
893 49865 : if (poGDS->m_nLastBlockXOff == nBlockXOff &&
894 49760 : (nBlockYSize == 1 || poGDS->m_nLastBlockYOff == nBlockYOff) &&
895 49760 : poGDS->m_pabyCachedData != nullptr)
896 : {
897 : /*CPLDebug("PDF", "Using cached block (%d, %d)",
898 : nBlockXOff, nBlockYOff);*/
899 : // do nothing
900 : }
901 : else
902 : {
903 : #ifdef HAVE_PODOFO
904 : if (poGDS->m_bUseLib.test(PDFLIB_PODOFO) && nBand == 4)
905 : {
906 : memset(pImage, 255, nBlockXSize * nBlockYSize);
907 : return CE_None;
908 : }
909 : #endif
910 :
911 105 : const int nReqXOff = nBlockXOff * nBlockXSize;
912 105 : const int nReqYOff = (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize;
913 105 : const GSpacing nPixelSpace = 1;
914 105 : const GSpacing nLineSpace = nBlockXSize;
915 105 : const GSpacing nBandSpace =
916 105 : static_cast<GSpacing>(nBlockXSize) *
917 105 : ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize);
918 :
919 105 : CPLErr eErr = poGDS->ReadPixels(nReqXOff, nReqYOff, nReqXSize,
920 : nReqYSize, nPixelSpace, nLineSpace,
921 : nBandSpace, poGDS->m_pabyCachedData);
922 :
923 105 : if (eErr == CE_None)
924 : {
925 105 : poGDS->m_nLastBlockXOff = nBlockXOff;
926 105 : poGDS->m_nLastBlockYOff = nBlockYOff;
927 : }
928 : else
929 : {
930 0 : CPLFree(poGDS->m_pabyCachedData);
931 0 : poGDS->m_pabyCachedData = nullptr;
932 : }
933 : }
934 49865 : if (poGDS->m_pabyCachedData == nullptr)
935 0 : return CE_Failure;
936 :
937 49865 : if (nBlockYSize == 1)
938 49860 : memcpy(pImage,
939 49860 : poGDS->m_pabyCachedData +
940 49860 : (nBand - 1) * nBlockXSize * nRasterYSize +
941 49860 : nBlockYOff * nBlockXSize,
942 49860 : nBlockXSize);
943 : else
944 : {
945 5 : memcpy(pImage,
946 5 : poGDS->m_pabyCachedData +
947 5 : static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize,
948 5 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
949 :
950 5 : if (poGDS->m_bCacheBlocksForOtherBands && nBand == 1)
951 : {
952 19 : for (int iBand = 2; iBand <= poGDS->nBands; ++iBand)
953 : {
954 28 : auto poOtherBand = cpl::down_cast<PDFRasterBand *>(
955 14 : poGDS->papoBands[iBand - 1]);
956 : GDALRasterBlock *poBlock =
957 14 : poOtherBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
958 14 : if (poBlock)
959 : {
960 0 : poBlock->DropLock();
961 : }
962 : else
963 : {
964 28 : poBlock = poOtherBand->GetLockedBlockRef(nBlockXOff,
965 14 : nBlockYOff, TRUE);
966 14 : if (poBlock)
967 : {
968 28 : memcpy(poBlock->GetDataRef(),
969 14 : poGDS->m_pabyCachedData +
970 14 : static_cast<size_t>(iBand - 1) *
971 14 : nBlockXSize * nBlockYSize,
972 14 : static_cast<size_t>(nBlockXSize) * nBlockYSize);
973 14 : poBlock->DropLock();
974 : }
975 : }
976 : }
977 : }
978 : }
979 :
980 49865 : return CE_None;
981 : }
982 :
983 : /************************************************************************/
984 : /* PDFEnterPasswordFromConsoleIfNeeded() */
985 : /************************************************************************/
986 :
987 6 : static const char *PDFEnterPasswordFromConsoleIfNeeded(const char *pszUserPwd)
988 : {
989 6 : if (EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
990 : {
991 : static char szPassword[81];
992 4 : printf("Enter password (will be echo'ed in the console): "); /*ok*/
993 4 : if (nullptr == fgets(szPassword, sizeof(szPassword), stdin))
994 : {
995 0 : fprintf(stderr, "WARNING: Error getting password.\n"); /*ok*/
996 : }
997 4 : szPassword[sizeof(szPassword) - 1] = 0;
998 4 : char *sz10 = strchr(szPassword, '\n');
999 4 : if (sz10)
1000 0 : *sz10 = 0;
1001 4 : return szPassword;
1002 : }
1003 2 : return pszUserPwd;
1004 : }
1005 :
1006 : #ifdef HAVE_PDFIUM
1007 :
1008 : /************************************************************************/
1009 : /* Pdfium Load/Unload */
1010 : /* Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/) */
1011 : /* Author: Martin Mikita <martin.mikita@klokantech.com> */
1012 : /************************************************************************/
1013 :
1014 : // Flag for calling PDFium Init and Destroy methods
1015 : bool PDFDataset::g_bPdfiumInit = false;
1016 :
1017 : // Pdfium global read mutex - Pdfium is not multi-thread
1018 : static CPLMutex *g_oPdfiumReadMutex = nullptr;
1019 : static CPLMutex *g_oPdfiumLoadDocMutex = nullptr;
1020 :
1021 : // Comparison of char* for std::map
1022 : struct cmp_str
1023 : {
1024 441 : bool operator()(char const *a, char const *b) const
1025 : {
1026 441 : return strcmp(a, b) < 0;
1027 : }
1028 : };
1029 :
1030 5526 : static int GDALPdfiumGetBlock(void *param, unsigned long position,
1031 : unsigned char *pBuf, unsigned long size)
1032 : {
1033 5526 : VSILFILE *fp = static_cast<VSILFILE *>(param);
1034 5526 : VSIFSeekL(fp, position, SEEK_SET);
1035 5526 : return VSIFReadL(pBuf, size, 1, fp) == 1;
1036 : }
1037 :
1038 : // List of all PDF datasets
1039 : typedef std::map<const char *, TPdfiumDocumentStruct *, cmp_str>
1040 : TMapPdfiumDatasets;
1041 : static TMapPdfiumDatasets g_mPdfiumDatasets;
1042 :
1043 : /**
1044 : * Loading PDFIUM page
1045 : * - multithreading requires "mutex"
1046 : * - one page can require too much RAM
1047 : * - we will have one document per filename and one object per page
1048 : */
1049 :
1050 225 : static int LoadPdfiumDocumentPage(const char *pszFilename,
1051 : const char *pszUserPwd, int pageNum,
1052 : TPdfiumDocumentStruct **doc,
1053 : TPdfiumPageStruct **page, int *pnPageCount)
1054 : {
1055 : // Prepare nullptr for error returning
1056 225 : if (doc)
1057 225 : *doc = nullptr;
1058 225 : if (page)
1059 225 : *page = nullptr;
1060 225 : if (pnPageCount)
1061 225 : *pnPageCount = 0;
1062 :
1063 : // Loading document and page must be only in one thread!
1064 225 : CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT);
1065 :
1066 : // Library can be destroyed if every PDF dataset was closed!
1067 225 : if (!PDFDataset::g_bPdfiumInit)
1068 : {
1069 201 : FPDF_InitLibrary();
1070 201 : PDFDataset::g_bPdfiumInit = TRUE;
1071 : }
1072 :
1073 225 : TMapPdfiumDatasets::iterator it;
1074 225 : it = g_mPdfiumDatasets.find(pszFilename);
1075 225 : TPdfiumDocumentStruct *poDoc = nullptr;
1076 : // Load new document if missing
1077 225 : if (it == g_mPdfiumDatasets.end())
1078 : {
1079 : // Try without password (if PDF not requires password it can fail)
1080 :
1081 215 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1082 215 : if (fp == nullptr)
1083 : {
1084 1 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1085 1 : return FALSE;
1086 : }
1087 214 : VSIFSeekL(fp, 0, SEEK_END);
1088 214 : const auto nFileLen64 = VSIFTellL(fp);
1089 : if constexpr (LONG_MAX < std::numeric_limits<vsi_l_offset>::max())
1090 : {
1091 214 : if (nFileLen64 > LONG_MAX)
1092 : {
1093 0 : VSIFCloseL(fp);
1094 0 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1095 0 : return FALSE;
1096 : }
1097 : }
1098 :
1099 214 : FPDF_FILEACCESS *psFileAccess = new FPDF_FILEACCESS;
1100 214 : psFileAccess->m_Param = fp;
1101 214 : psFileAccess->m_FileLen = static_cast<unsigned long>(nFileLen64);
1102 214 : psFileAccess->m_GetBlock = GDALPdfiumGetBlock;
1103 214 : CPDF_Document *docPdfium = CPDFDocumentFromFPDFDocument(
1104 : FPDF_LoadCustomDocument(psFileAccess, nullptr));
1105 214 : if (docPdfium == nullptr)
1106 : {
1107 15 : unsigned long err = FPDF_GetLastError();
1108 15 : if (err == FPDF_ERR_PASSWORD)
1109 : {
1110 7 : if (pszUserPwd)
1111 : {
1112 : pszUserPwd =
1113 6 : PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd);
1114 6 : docPdfium = CPDFDocumentFromFPDFDocument(
1115 : FPDF_LoadCustomDocument(psFileAccess, pszUserPwd));
1116 6 : if (docPdfium == nullptr)
1117 3 : err = FPDF_GetLastError();
1118 : else
1119 3 : err = FPDF_ERR_SUCCESS;
1120 : }
1121 : else
1122 : {
1123 1 : CPLError(CE_Failure, CPLE_AppDefined,
1124 : "A password is needed. You can specify it through "
1125 : "the PDF_USER_PWD "
1126 : "configuration option / USER_PWD open option "
1127 : "(that can be set to ASK_INTERACTIVE)");
1128 :
1129 1 : VSIFCloseL(fp);
1130 1 : delete psFileAccess;
1131 1 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1132 1 : return FALSE;
1133 : }
1134 : } // First Error Password [null password given]
1135 14 : if (err != FPDF_ERR_SUCCESS)
1136 : {
1137 11 : if (err == FPDF_ERR_PASSWORD)
1138 3 : CPLError(CE_Failure, CPLE_AppDefined,
1139 : "PDFium Invalid password.");
1140 8 : else if (err == FPDF_ERR_SECURITY)
1141 0 : CPLError(CE_Failure, CPLE_AppDefined,
1142 : "PDFium Unsupported security scheme.");
1143 8 : else if (err == FPDF_ERR_FORMAT)
1144 8 : CPLError(CE_Failure, CPLE_AppDefined,
1145 : "PDFium File not in PDF format or corrupted.");
1146 0 : else if (err == FPDF_ERR_FILE)
1147 0 : CPLError(CE_Failure, CPLE_AppDefined,
1148 : "PDFium File not found or could not be opened.");
1149 : else
1150 0 : CPLError(CE_Failure, CPLE_AppDefined,
1151 : "PDFium Unknown PDF error or invalid PDF.");
1152 :
1153 11 : VSIFCloseL(fp);
1154 11 : delete psFileAccess;
1155 11 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1156 11 : return FALSE;
1157 : }
1158 : } // ~ wrong PDF or password required
1159 :
1160 : // Create new poDoc
1161 202 : poDoc = new TPdfiumDocumentStruct;
1162 202 : if (!poDoc)
1163 : {
1164 0 : CPLError(CE_Failure, CPLE_AppDefined,
1165 : "Not enough memory for Pdfium Document object");
1166 :
1167 0 : VSIFCloseL(fp);
1168 0 : delete psFileAccess;
1169 0 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1170 0 : return FALSE;
1171 : }
1172 202 : poDoc->filename = CPLStrdup(pszFilename);
1173 202 : poDoc->doc = docPdfium;
1174 202 : poDoc->psFileAccess = psFileAccess;
1175 :
1176 202 : g_mPdfiumDatasets[poDoc->filename] = poDoc;
1177 : }
1178 : // Document already loaded
1179 : else
1180 : {
1181 10 : poDoc = it->second;
1182 : }
1183 :
1184 : // Check page num in document
1185 212 : int nPages = poDoc->doc->GetPageCount();
1186 212 : if (pageNum < 1 || pageNum > nPages)
1187 : {
1188 1 : CPLError(CE_Failure, CPLE_AppDefined,
1189 : "PDFium Invalid page number (%d/%d) for document %s", pageNum,
1190 : nPages, pszFilename);
1191 :
1192 1 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1193 1 : return FALSE;
1194 : }
1195 :
1196 : /* Sanity check to validate page count */
1197 211 : if (pageNum != nPages)
1198 : {
1199 11 : if (poDoc->doc->GetPageDictionary(nPages - 1) == nullptr)
1200 : {
1201 0 : CPLError(CE_Failure, CPLE_AppDefined,
1202 : "Invalid PDF : invalid page count");
1203 0 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1204 0 : return FALSE;
1205 : }
1206 : }
1207 :
1208 211 : TMapPdfiumPages::iterator itPage;
1209 211 : itPage = poDoc->pages.find(pageNum);
1210 211 : TPdfiumPageStruct *poPage = nullptr;
1211 : // Page not loaded
1212 211 : if (itPage == poDoc->pages.end())
1213 : {
1214 206 : auto pDict = poDoc->doc->GetPageDictionary(pageNum - 1);
1215 206 : if (pDict == nullptr)
1216 : {
1217 0 : CPLError(CE_Failure, CPLE_AppDefined,
1218 : "Invalid PDFium : invalid page");
1219 :
1220 0 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1221 0 : return FALSE;
1222 : }
1223 : auto pPage = pdfium::MakeRetain<CPDF_Page>(
1224 206 : poDoc->doc,
1225 : // coverity is confused by WrapRetain(), believing that multiple
1226 : // smart pointers manage the same raw pointer. Which is actually
1227 : // true, but a RetainPtr holds a reference counted object. It is
1228 : // thus safe to have several RetainPtr holding it.
1229 : // coverity[multiple_init_smart_ptr]
1230 206 : pdfium::WrapRetain(const_cast<CPDF_Dictionary *>(pDict.Get())));
1231 :
1232 206 : poPage = new TPdfiumPageStruct;
1233 206 : if (!poPage)
1234 : {
1235 0 : CPLError(CE_Failure, CPLE_AppDefined,
1236 : "Not enough memory for Pdfium Page object");
1237 :
1238 0 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1239 0 : return FALSE;
1240 : }
1241 206 : poPage->pageNum = pageNum;
1242 206 : poPage->page = pPage.Leak();
1243 206 : poPage->readMutex = nullptr;
1244 206 : poPage->sharedNum = 0;
1245 :
1246 206 : poDoc->pages[pageNum] = poPage;
1247 : }
1248 : // Page already loaded
1249 : else
1250 : {
1251 5 : poPage = itPage->second;
1252 : }
1253 :
1254 : // Increase number of used
1255 211 : ++poPage->sharedNum;
1256 :
1257 211 : if (doc)
1258 211 : *doc = poDoc;
1259 211 : if (page)
1260 211 : *page = poPage;
1261 211 : if (pnPageCount)
1262 211 : *pnPageCount = nPages;
1263 :
1264 211 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1265 :
1266 211 : return TRUE;
1267 : }
1268 :
1269 : // ~ static int LoadPdfiumDocumentPage()
1270 :
1271 211 : static int UnloadPdfiumDocumentPage(TPdfiumDocumentStruct **doc,
1272 : TPdfiumPageStruct **page)
1273 : {
1274 211 : if (!doc || !page)
1275 0 : return FALSE;
1276 :
1277 211 : TPdfiumPageStruct *pPage = *page;
1278 211 : TPdfiumDocumentStruct *pDoc = *doc;
1279 :
1280 : // Get mutex for loading pdfium
1281 211 : CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT);
1282 :
1283 : // Decrease page use
1284 211 : --pPage->sharedNum;
1285 :
1286 : #ifdef DEBUG
1287 211 : CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: page shared num %d",
1288 : pPage->sharedNum);
1289 : #endif
1290 : // Page is used (also document)
1291 211 : if (pPage->sharedNum != 0)
1292 : {
1293 5 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1294 5 : return TRUE;
1295 : }
1296 :
1297 : // Get mutex, release and destroy it
1298 206 : CPLCreateOrAcquireMutex(&(pPage->readMutex), PDFIUM_MUTEX_TIMEOUT);
1299 206 : CPLReleaseMutex(pPage->readMutex);
1300 206 : CPLDestroyMutex(pPage->readMutex);
1301 : // Close page and remove from map
1302 206 : FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page));
1303 :
1304 206 : pDoc->pages.erase(pPage->pageNum);
1305 206 : delete pPage;
1306 206 : pPage = nullptr;
1307 :
1308 : #ifdef DEBUG
1309 206 : CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: pages %lu",
1310 : pDoc->pages.size());
1311 : #endif
1312 : // Another page is used
1313 206 : if (!pDoc->pages.empty())
1314 : {
1315 4 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1316 4 : return TRUE;
1317 : }
1318 :
1319 : // Close document and remove from map
1320 202 : FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc));
1321 202 : g_mPdfiumDatasets.erase(pDoc->filename);
1322 202 : CPLFree(pDoc->filename);
1323 202 : VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param));
1324 202 : delete pDoc->psFileAccess;
1325 202 : delete pDoc;
1326 202 : pDoc = nullptr;
1327 :
1328 : #ifdef DEBUG
1329 202 : CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: documents %lu",
1330 : g_mPdfiumDatasets.size());
1331 : #endif
1332 : // Another document is used
1333 202 : if (!g_mPdfiumDatasets.empty())
1334 : {
1335 3 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1336 3 : return TRUE;
1337 : }
1338 :
1339 : #ifdef DEBUG
1340 199 : CPLDebug("PDF", "PDFDataset::UnloadPdfiumDocumentPage: Nothing loaded, "
1341 : "destroy Library");
1342 : #endif
1343 : // No document loaded, destroy pdfium
1344 199 : FPDF_DestroyLibrary();
1345 199 : PDFDataset::g_bPdfiumInit = FALSE;
1346 :
1347 199 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
1348 :
1349 199 : return TRUE;
1350 : }
1351 :
1352 : // ~ static int UnloadPdfiumDocumentPage()
1353 :
1354 : #endif // ~ HAVE_PDFIUM
1355 :
1356 : /************************************************************************/
1357 : /* GetOption() */
1358 : /************************************************************************/
1359 :
1360 2431 : const char *PDFDataset::GetOption(char **papszOpenOptionsIn,
1361 : const char *pszOptionName,
1362 : const char *pszDefaultVal)
1363 : {
1364 2431 : CPLErr eLastErrType = CPLGetLastErrorType();
1365 2431 : CPLErrorNum nLastErrno = CPLGetLastErrorNo();
1366 4862 : CPLString osLastErrorMsg(CPLGetLastErrorMsg());
1367 2431 : CPLXMLNode *psNode = CPLParseXMLString(PDFGetOpenOptionList());
1368 2431 : CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg);
1369 2431 : if (psNode == nullptr)
1370 0 : return pszDefaultVal;
1371 2431 : CPLXMLNode *psIter = psNode->psChild;
1372 11093 : while (psIter != nullptr)
1373 : {
1374 11093 : if (EQUAL(CPLGetXMLValue(psIter, "name", ""), pszOptionName))
1375 : {
1376 : const char *pszVal =
1377 2431 : CSLFetchNameValue(papszOpenOptionsIn, pszOptionName);
1378 2431 : if (pszVal != nullptr)
1379 : {
1380 36 : CPLDestroyXMLNode(psNode);
1381 36 : return pszVal;
1382 : }
1383 : const char *pszAltConfigOption =
1384 2395 : CPLGetXMLValue(psIter, "alt_config_option", nullptr);
1385 2395 : if (pszAltConfigOption != nullptr)
1386 : {
1387 2395 : pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal);
1388 2395 : CPLDestroyXMLNode(psNode);
1389 2395 : return pszVal;
1390 : }
1391 0 : CPLDestroyXMLNode(psNode);
1392 0 : return pszDefaultVal;
1393 : }
1394 8662 : psIter = psIter->psNext;
1395 : }
1396 0 : CPLError(CE_Failure, CPLE_AppDefined,
1397 : "Requesting an undocumented open option '%s'", pszOptionName);
1398 0 : CPLDestroyXMLNode(psNode);
1399 0 : return pszDefaultVal;
1400 : }
1401 :
1402 : #ifdef HAVE_PDFIUM
1403 :
1404 : /************************************************************************/
1405 : /* GDALPDFiumOCContext */
1406 : /************************************************************************/
1407 :
1408 : class GDALPDFiumOCContext : public CPDF_OCContextInterface
1409 : {
1410 : PDFDataset *m_poDS;
1411 : RetainPtr<CPDF_OCContext> m_DefaultOCContext;
1412 :
1413 : CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumOCContext)
1414 :
1415 : public:
1416 60 : GDALPDFiumOCContext(PDFDataset *poDS, CPDF_Document *pDoc,
1417 : CPDF_OCContext::UsageType usage)
1418 60 : : m_poDS(poDS),
1419 60 : m_DefaultOCContext(pdfium::MakeRetain<CPDF_OCContext>(pDoc, usage))
1420 : {
1421 60 : }
1422 :
1423 : virtual bool
1424 12996 : CheckOCGDictVisible(const CPDF_Dictionary *pOCGDict) const override
1425 : {
1426 : // CPLDebug("PDF", "CheckOCGDictVisible(%d,%d)",
1427 : // pOCGDict->GetObjNum(), pOCGDict->GetGenNum() );
1428 : PDFDataset::VisibilityState eVisibility =
1429 12996 : m_poDS->GetVisibilityStateForOGCPdfium(pOCGDict->GetObjNum(),
1430 12996 : pOCGDict->GetGenNum());
1431 12996 : if (eVisibility == PDFDataset::VISIBILITY_ON)
1432 3294 : return true;
1433 9702 : if (eVisibility == PDFDataset::VISIBILITY_OFF)
1434 999 : return false;
1435 8703 : return m_DefaultOCContext->CheckOCGDictVisible(pOCGDict);
1436 : }
1437 : };
1438 :
1439 : /************************************************************************/
1440 : /* GDALPDFiumRenderDeviceDriver */
1441 : /************************************************************************/
1442 :
1443 : class GDALPDFiumRenderDeviceDriver : public RenderDeviceDriverIface
1444 : {
1445 : std::unique_ptr<RenderDeviceDriverIface> m_poParent;
1446 : CFX_RenderDevice *m_pDevice;
1447 :
1448 : int bEnableVector;
1449 : int bEnableText;
1450 : int bEnableBitmap;
1451 : int bTemporaryEnableVectorForTextStroking;
1452 :
1453 : CPL_DISALLOW_COPY_ASSIGN(GDALPDFiumRenderDeviceDriver)
1454 :
1455 : public:
1456 6 : GDALPDFiumRenderDeviceDriver(
1457 : std::unique_ptr<RenderDeviceDriverIface> &&poParent,
1458 : CFX_RenderDevice *pDevice)
1459 12 : : m_poParent(std::move(poParent)), m_pDevice(pDevice),
1460 : bEnableVector(TRUE), bEnableText(TRUE), bEnableBitmap(TRUE),
1461 6 : bTemporaryEnableVectorForTextStroking(FALSE)
1462 : {
1463 6 : }
1464 :
1465 12 : virtual ~GDALPDFiumRenderDeviceDriver() = default;
1466 :
1467 6 : void SetEnableVector(int bFlag)
1468 : {
1469 6 : bEnableVector = bFlag;
1470 6 : }
1471 :
1472 6 : void SetEnableText(int bFlag)
1473 : {
1474 6 : bEnableText = bFlag;
1475 6 : }
1476 :
1477 6 : void SetEnableBitmap(int bFlag)
1478 : {
1479 6 : bEnableBitmap = bFlag;
1480 6 : }
1481 :
1482 6 : virtual DeviceType GetDeviceType() const override
1483 : {
1484 6 : return m_poParent->GetDeviceType();
1485 : }
1486 :
1487 24 : virtual int GetDeviceCaps(int caps_id) const override
1488 : {
1489 24 : return m_poParent->GetDeviceCaps(caps_id);
1490 : }
1491 :
1492 36 : virtual void SaveState() override
1493 : {
1494 36 : m_poParent->SaveState();
1495 36 : }
1496 :
1497 54 : virtual void RestoreState(bool bKeepSaved) override
1498 : {
1499 54 : m_poParent->RestoreState(bKeepSaved);
1500 54 : }
1501 :
1502 6 : virtual void SetBaseClip(const FX_RECT &rect) override
1503 : {
1504 6 : m_poParent->SetBaseClip(rect);
1505 6 : }
1506 :
1507 : virtual bool
1508 18 : SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device,
1509 : const CFX_FillRenderOptions &fill_options) override
1510 : {
1511 18 : if (!bEnableVector && !bTemporaryEnableVectorForTextStroking)
1512 9 : return true;
1513 9 : return m_poParent->SetClip_PathFill(path, pObject2Device, fill_options);
1514 : }
1515 :
1516 : virtual bool
1517 0 : SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device,
1518 : const CFX_GraphStateData *pGraphState) override
1519 : {
1520 0 : if (!bEnableVector && !bTemporaryEnableVectorForTextStroking)
1521 0 : return true;
1522 0 : return m_poParent->SetClip_PathStroke(path, pObject2Device,
1523 0 : pGraphState);
1524 : }
1525 :
1526 18 : virtual bool DrawPath(const CFX_Path &path,
1527 : const CFX_Matrix *pObject2Device,
1528 : const CFX_GraphStateData *pGraphState,
1529 : uint32_t fill_color, uint32_t stroke_color,
1530 : const CFX_FillRenderOptions &fill_options,
1531 : BlendMode blend_type) override
1532 : {
1533 18 : if (!bEnableVector && !bTemporaryEnableVectorForTextStroking)
1534 9 : return true;
1535 18 : return m_poParent->DrawPath(path, pObject2Device, pGraphState,
1536 : fill_color, stroke_color, fill_options,
1537 9 : blend_type);
1538 : }
1539 :
1540 0 : virtual bool FillRectWithBlend(const FX_RECT &rect, uint32_t fill_color,
1541 : BlendMode blend_type) override
1542 : {
1543 0 : return m_poParent->FillRectWithBlend(rect, fill_color, blend_type);
1544 : }
1545 :
1546 0 : virtual bool DrawCosmeticLine(const CFX_PointF &ptMoveTo,
1547 : const CFX_PointF &ptLineTo, uint32_t color,
1548 : BlendMode blend_typeL) override
1549 : {
1550 0 : if (!bEnableVector && !bTemporaryEnableVectorForTextStroking)
1551 0 : return TRUE;
1552 0 : return m_poParent->DrawCosmeticLine(ptMoveTo, ptLineTo, color,
1553 0 : blend_typeL);
1554 : }
1555 :
1556 84 : virtual FX_RECT GetClipBox() const override
1557 : {
1558 84 : return m_poParent->GetClipBox();
1559 : }
1560 :
1561 0 : virtual bool GetDIBits(RetainPtr<CFX_DIBitmap> bitmap, int left,
1562 : int top) const override
1563 : {
1564 0 : return m_poParent->GetDIBits(std::move(bitmap), left, top);
1565 : }
1566 :
1567 0 : virtual RetainPtr<const CFX_DIBitmap> GetBackDrop() const override
1568 : {
1569 0 : return m_poParent->GetBackDrop();
1570 : }
1571 :
1572 3 : virtual bool SetDIBits(RetainPtr<const CFX_DIBBase> bitmap, uint32_t color,
1573 : const FX_RECT &src_rect, int dest_left, int dest_top,
1574 : BlendMode blend_type) override
1575 : {
1576 3 : if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking)
1577 0 : return true;
1578 6 : return m_poParent->SetDIBits(std::move(bitmap), color, src_rect,
1579 3 : dest_left, dest_top, blend_type);
1580 : }
1581 :
1582 0 : virtual bool StretchDIBits(RetainPtr<const CFX_DIBBase> bitmap,
1583 : uint32_t color, int dest_left, int dest_top,
1584 : int dest_width, int dest_height,
1585 : const FX_RECT *pClipRect,
1586 : const FXDIB_ResampleOptions &options,
1587 : BlendMode blend_type) override
1588 : {
1589 0 : if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking)
1590 0 : return true;
1591 0 : return m_poParent->StretchDIBits(std::move(bitmap), color, dest_left,
1592 : dest_top, dest_width, dest_height,
1593 0 : pClipRect, options, blend_type);
1594 : }
1595 :
1596 6 : virtual StartResult StartDIBits(RetainPtr<const CFX_DIBBase> bitmap,
1597 : float alpha, uint32_t color,
1598 : const CFX_Matrix &matrix,
1599 : const FXDIB_ResampleOptions &options,
1600 : BlendMode blend_type) override
1601 : {
1602 6 : if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking)
1603 3 : return StartResult(Result::kSuccess, nullptr);
1604 3 : return m_poParent->StartDIBits(std::move(bitmap), alpha, color, matrix,
1605 3 : options, blend_type);
1606 : }
1607 :
1608 3 : virtual bool ContinueDIBits(CFX_AggImageRenderer *handle,
1609 : PauseIndicatorIface *pPause) override
1610 : {
1611 3 : return m_poParent->ContinueDIBits(handle, pPause);
1612 : }
1613 :
1614 9 : virtual bool DrawDeviceText(const pdfium::span<const TextCharPos> &pCharPos,
1615 : CFX_Font *pFont,
1616 : const CFX_Matrix &mtObject2Device,
1617 : float font_size, uint32_t color,
1618 : const CFX_TextRenderOptions &options) override
1619 : {
1620 9 : if (bEnableText)
1621 : {
1622 : // This is quite tricky. We call again the guy who called us
1623 : // (CFX_RenderDevice::DrawNormalText()) but we set a special flag to
1624 : // allow vector&raster operations so that the rendering will happen
1625 : // in the next phase
1626 6 : if (bTemporaryEnableVectorForTextStroking)
1627 3 : return FALSE; // this is the default behavior of the parent
1628 3 : bTemporaryEnableVectorForTextStroking = true;
1629 3 : bool bRet = m_pDevice->DrawNormalText(
1630 : pCharPos, pFont, font_size, mtObject2Device, color, options);
1631 3 : bTemporaryEnableVectorForTextStroking = FALSE;
1632 3 : return bRet;
1633 : }
1634 : else
1635 3 : return true; // pretend that we did the job
1636 : }
1637 :
1638 0 : virtual int GetDriverType() const override
1639 : {
1640 0 : return m_poParent->GetDriverType();
1641 : }
1642 :
1643 : #if defined(_SKIA_SUPPORT_)
1644 : virtual bool DrawShading(const CPDF_ShadingPattern &pattern,
1645 : const CFX_Matrix &matrix, const FX_RECT &clip_rect,
1646 : int alpha) override
1647 : {
1648 : if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking)
1649 : return true;
1650 : return m_poParent->DrawShading(pattern, matrix, clip_rect, alpha);
1651 : }
1652 : #endif
1653 :
1654 0 : bool MultiplyAlpha(float alpha) override
1655 : {
1656 0 : return m_poParent->MultiplyAlpha(alpha);
1657 : }
1658 :
1659 0 : bool MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) override
1660 : {
1661 0 : return m_poParent->MultiplyAlphaMask(std::move(mask));
1662 : }
1663 :
1664 : #if defined(_SKIA_SUPPORT_)
1665 : virtual bool SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap,
1666 : RetainPtr<const CFX_DIBBase> mask, int left,
1667 : int top, float alpha,
1668 : BlendMode blend_type) override
1669 : {
1670 : if (!bEnableBitmap && !bTemporaryEnableVectorForTextStroking)
1671 : return true;
1672 : return m_poParent->SetBitsWithMask(std::move(bitmap), std::move(mask),
1673 : left, top, alpha, blend_type);
1674 : }
1675 :
1676 : virtual void SetGroupKnockout(bool group_knockout) override
1677 : {
1678 : m_poParent->SetGroupKnockout(group_knockout);
1679 : }
1680 : #endif
1681 : #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
1682 : virtual void Flush() override
1683 : {
1684 : return m_poParent->Flush();
1685 : }
1686 : #endif
1687 : };
1688 :
1689 : /************************************************************************/
1690 : /* PDFiumRenderPageBitmap() */
1691 : /************************************************************************/
1692 :
1693 : /* This method is a customization of RenderPageImpl()
1694 : from pdfium/fpdfsdk/cpdfsdk_renderpage.cpp to allow selection of which OGC/layer are
1695 : active. Thus it inherits the following license */
1696 : // Copyright 2014-2020 PDFium Authors. All rights reserved.
1697 : //
1698 : // Redistribution and use in source and binary forms, with or without
1699 : // modification, are permitted provided that the following conditions are
1700 : // met:
1701 : //
1702 : // * Redistributions of source code must retain the above copyright
1703 : // notice, this list of conditions and the following disclaimer.
1704 : // * Redistributions in binary form must reproduce the above
1705 : // copyright notice, this list of conditions and the following disclaimer
1706 : // in the documentation and/or other materials provided with the
1707 : // distribution.
1708 : // * Neither the name of Google Inc. nor the names of its
1709 : // contributors may be used to endorse or promote products derived from
1710 : // this software without specific prior written permission.
1711 : //
1712 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1713 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1714 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1715 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1716 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1717 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1718 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1719 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1720 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1721 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1722 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1723 :
1724 60 : static void myRenderPageImpl(PDFDataset *poDS, CPDF_PageRenderContext *pContext,
1725 : CPDF_Page *pPage, const CFX_Matrix &matrix,
1726 : const FX_RECT &clipping_rect, int flags,
1727 : const FPDF_COLORSCHEME *color_scheme,
1728 : bool bNeedToRestore, CPDFSDK_PauseAdapter *pause)
1729 : {
1730 60 : if (!pContext->m_pOptions)
1731 60 : pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
1732 :
1733 60 : auto &options = pContext->m_pOptions->GetOptions();
1734 60 : options.bClearType = !!(flags & FPDF_LCD_TEXT);
1735 60 : options.bNoNativeText = !!(flags & FPDF_NO_NATIVETEXT);
1736 60 : options.bLimitedImageCache = !!(flags & FPDF_RENDER_LIMITEDIMAGECACHE);
1737 60 : options.bForceHalftone = !!(flags & FPDF_RENDER_FORCEHALFTONE);
1738 60 : options.bNoTextSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHTEXT);
1739 60 : options.bNoImageSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHIMAGE);
1740 60 : options.bNoPathSmooth = !!(flags & FPDF_RENDER_NO_SMOOTHPATH);
1741 :
1742 : // Grayscale output
1743 60 : if (flags & FPDF_GRAYSCALE)
1744 0 : pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray);
1745 :
1746 60 : if (color_scheme)
1747 : {
1748 0 : pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kForcedColor);
1749 0 : SetColorFromScheme(color_scheme, pContext->m_pOptions.get());
1750 0 : options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE);
1751 : }
1752 :
1753 60 : const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING)
1754 60 : ? CPDF_OCContext::kPrint
1755 : : CPDF_OCContext::kView;
1756 120 : pContext->m_pOptions->SetOCContext(pdfium::MakeRetain<GDALPDFiumOCContext>(
1757 60 : poDS, pPage->GetDocument(), usage));
1758 :
1759 60 : pContext->m_pDevice->SaveState();
1760 60 : pContext->m_pDevice->SetBaseClip(clipping_rect);
1761 60 : pContext->m_pDevice->SetClip_Rect(clipping_rect);
1762 60 : pContext->m_pContext = std::make_unique<CPDF_RenderContext>(
1763 120 : pPage->GetDocument(), pPage->GetMutablePageResources(),
1764 120 : pPage->GetPageImageCache());
1765 :
1766 60 : pContext->m_pContext->AppendLayer(pPage, matrix);
1767 :
1768 60 : if (flags & FPDF_ANNOT)
1769 : {
1770 0 : auto pOwnedList = std::make_unique<CPDF_AnnotList>(pPage);
1771 0 : CPDF_AnnotList *pList = pOwnedList.get();
1772 0 : pContext->m_pAnnots = std::move(pOwnedList);
1773 : bool bPrinting =
1774 0 : pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay;
1775 :
1776 : // TODO(https://crbug.com/pdfium/993) - maybe pass true here.
1777 0 : const bool bShowWidget = false;
1778 0 : pList->DisplayAnnots(pContext->m_pContext.get(), bPrinting, matrix,
1779 : bShowWidget);
1780 : }
1781 :
1782 60 : pContext->m_pRenderer = std::make_unique<CPDF_ProgressiveRenderer>(
1783 60 : pContext->m_pContext.get(), pContext->m_pDevice.get(),
1784 120 : pContext->m_pOptions.get());
1785 60 : pContext->m_pRenderer->Start(pause);
1786 60 : if (bNeedToRestore)
1787 60 : pContext->m_pDevice->RestoreState(false);
1788 60 : }
1789 :
1790 : static void
1791 60 : myRenderPageWithContext(PDFDataset *poDS, CPDF_PageRenderContext *pContext,
1792 : FPDF_PAGE page, int start_x, int start_y, int size_x,
1793 : int size_y, int rotate, int flags,
1794 : const FPDF_COLORSCHEME *color_scheme,
1795 : bool bNeedToRestore, CPDFSDK_PauseAdapter *pause)
1796 : {
1797 60 : CPDF_Page *pPage = CPDFPageFromFPDFPage(page);
1798 60 : if (!pPage)
1799 0 : return;
1800 :
1801 60 : const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
1802 60 : myRenderPageImpl(poDS, pContext, pPage,
1803 120 : pPage->GetDisplayMatrix(rect, rotate), rect, flags,
1804 : color_scheme, bNeedToRestore, pause);
1805 : }
1806 :
1807 : class MyRenderDevice final : public CFX_RenderDevice
1808 : {
1809 :
1810 : public:
1811 : // Substitution for CFX_DefaultRenderDevice::Attach
1812 : bool Attach(const RetainPtr<CFX_DIBitmap> &pBitmap, bool bRgbByteOrder,
1813 : const RetainPtr<CFX_DIBitmap> &pBackdropBitmap,
1814 : bool bGroupKnockout, const char *pszRenderingOptions);
1815 : };
1816 :
1817 60 : bool MyRenderDevice::Attach(const RetainPtr<CFX_DIBitmap> &pBitmap,
1818 : bool bRgbByteOrder,
1819 : const RetainPtr<CFX_DIBitmap> &pBackdropBitmap,
1820 : bool bGroupKnockout,
1821 : const char *pszRenderingOptions)
1822 : {
1823 60 : SetBitmap(pBitmap);
1824 :
1825 : std::unique_ptr<RenderDeviceDriverIface> driver =
1826 60 : std::make_unique<pdfium::CFX_AggDeviceDriver>(
1827 60 : pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout);
1828 60 : if (pszRenderingOptions != nullptr)
1829 : {
1830 7 : int bEnableVector = FALSE;
1831 7 : int bEnableText = FALSE;
1832 7 : int bEnableBitmap = FALSE;
1833 :
1834 7 : char **papszTokens = CSLTokenizeString2(pszRenderingOptions, " ,", 0);
1835 19 : for (int i = 0; papszTokens[i] != nullptr; i++)
1836 : {
1837 12 : if (EQUAL(papszTokens[i], "VECTOR"))
1838 4 : bEnableVector = TRUE;
1839 8 : else if (EQUAL(papszTokens[i], "TEXT"))
1840 4 : bEnableText = TRUE;
1841 4 : else if (EQUAL(papszTokens[i], "RASTER") ||
1842 0 : EQUAL(papszTokens[i], "BITMAP"))
1843 4 : bEnableBitmap = TRUE;
1844 : else
1845 : {
1846 0 : CPLError(CE_Warning, CPLE_NotSupported,
1847 : "Value %s is not a valid value for "
1848 : "GDAL_PDF_RENDERING_OPTIONS",
1849 0 : papszTokens[i]);
1850 : }
1851 : }
1852 7 : CSLDestroy(papszTokens);
1853 :
1854 7 : if (!bEnableVector || !bEnableText || !bEnableBitmap)
1855 : {
1856 : std::unique_ptr<GDALPDFiumRenderDeviceDriver> poGDALRDDriver =
1857 : std::make_unique<GDALPDFiumRenderDeviceDriver>(
1858 12 : std::move(driver), this);
1859 6 : poGDALRDDriver->SetEnableVector(bEnableVector);
1860 6 : poGDALRDDriver->SetEnableText(bEnableText);
1861 6 : poGDALRDDriver->SetEnableBitmap(bEnableBitmap);
1862 6 : driver = std::move(poGDALRDDriver);
1863 : }
1864 : }
1865 :
1866 60 : SetDeviceDriver(std::move(driver));
1867 120 : return true;
1868 : }
1869 :
1870 60 : void PDFDataset::PDFiumRenderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page,
1871 : int start_x, int start_y, int size_x,
1872 : int size_y,
1873 : const char *pszRenderingOptions)
1874 : {
1875 60 : const int rotate = 0;
1876 60 : const int flags = 0;
1877 :
1878 60 : if (!bitmap)
1879 0 : return;
1880 :
1881 60 : CPDF_Page *pPage = CPDFPageFromFPDFPage(page);
1882 60 : if (!pPage)
1883 0 : return;
1884 :
1885 120 : auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
1886 60 : CPDF_PageRenderContext *pContext = pOwnedContext.get();
1887 120 : CPDF_Page::RenderContextClearer clearer(pPage);
1888 60 : pPage->SetRenderContext(std::move(pOwnedContext));
1889 :
1890 120 : auto pOwnedDevice = std::make_unique<MyRenderDevice>();
1891 60 : auto pDevice = pOwnedDevice.get();
1892 60 : pContext->m_pDevice = std::move(pOwnedDevice);
1893 :
1894 120 : RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
1895 :
1896 60 : pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr,
1897 : false, pszRenderingOptions);
1898 :
1899 60 : myRenderPageWithContext(this, pContext, page, start_x, start_y, size_x,
1900 : size_y, rotate, flags,
1901 : /*color_scheme=*/nullptr,
1902 : /*need_to_restore=*/true, /*pause=*/nullptr);
1903 :
1904 : #ifdef _SKIA_SUPPORT_PATHS_
1905 : pDevice->Flush(true);
1906 : pBitmap->UnPreMultiply();
1907 : #endif
1908 : }
1909 :
1910 : #endif /* HAVE_PDFIUM */
1911 :
1912 : /************************************************************************/
1913 : /* ReadPixels() */
1914 : /************************************************************************/
1915 :
1916 107 : CPLErr PDFDataset::ReadPixels(int nReqXOff, int nReqYOff, int nReqXSize,
1917 : int nReqYSize, GSpacing nPixelSpace,
1918 : GSpacing nLineSpace, GSpacing nBandSpace,
1919 : GByte *pabyData)
1920 : {
1921 107 : CPLErr eErr = CE_None;
1922 : const char *pszRenderingOptions =
1923 107 : GetOption(papszOpenOptions, "RENDERING_OPTIONS", nullptr);
1924 :
1925 : #ifdef HAVE_POPPLER
1926 107 : if (m_bUseLib.test(PDFLIB_POPPLER))
1927 : {
1928 : SplashColor sColor;
1929 47 : sColor[0] = 255;
1930 47 : sColor[1] = 255;
1931 47 : sColor[2] = 255;
1932 : GDALPDFOutputDev *poSplashOut = new GDALPDFOutputDev(
1933 47 : (nBands < 4) ? splashModeRGB8 : splashModeXBGR8, 4, false,
1934 47 : (nBands < 4) ? sColor : nullptr);
1935 :
1936 47 : if (pszRenderingOptions != nullptr)
1937 : {
1938 7 : poSplashOut->SetEnableVector(FALSE);
1939 7 : poSplashOut->SetEnableText(FALSE);
1940 7 : poSplashOut->SetEnableBitmap(FALSE);
1941 :
1942 : char **papszTokens =
1943 7 : CSLTokenizeString2(pszRenderingOptions, " ,", 0);
1944 19 : for (int i = 0; papszTokens[i] != nullptr; i++)
1945 : {
1946 12 : if (EQUAL(papszTokens[i], "VECTOR"))
1947 4 : poSplashOut->SetEnableVector(TRUE);
1948 8 : else if (EQUAL(papszTokens[i], "TEXT"))
1949 4 : poSplashOut->SetEnableText(TRUE);
1950 4 : else if (EQUAL(papszTokens[i], "RASTER") ||
1951 0 : EQUAL(papszTokens[i], "BITMAP"))
1952 4 : poSplashOut->SetEnableBitmap(TRUE);
1953 : else
1954 : {
1955 0 : CPLError(CE_Warning, CPLE_NotSupported,
1956 : "Value %s is not a valid value for "
1957 : "GDAL_PDF_RENDERING_OPTIONS",
1958 0 : papszTokens[i]);
1959 : }
1960 : }
1961 7 : CSLDestroy(papszTokens);
1962 : }
1963 :
1964 47 : PDFDoc *poDoc = m_poDocPoppler;
1965 47 : poSplashOut->startDoc(poDoc);
1966 :
1967 : // Note: Poppler 25.2 is certainly not the lowest version where we can
1968 : // avoid the hack.
1969 : #if !(POPPLER_MAJOR_VERSION > 25 || \
1970 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2))
1971 : #define USE_OPTCONTENT_HACK
1972 : #endif
1973 :
1974 : #ifdef USE_OPTCONTENT_HACK
1975 : /* EVIL: we modify a private member... */
1976 : /* poppler (at least 0.12 and 0.14 versions) don't render correctly */
1977 : /* some PDFs and display an error message 'Could not find a OCG with
1978 : * Ref' */
1979 : /* in those cases. This processing of optional content is an addition of
1980 : */
1981 : /* poppler in comparison to original xpdf, which hasn't the issue. All
1982 : * in */
1983 : /* all, nullifying optContent removes the error message and improves the
1984 : * rendering */
1985 47 : Catalog *poCatalog = poDoc->getCatalog();
1986 47 : OCGs *poOldOCGs = poCatalog->optContent;
1987 47 : if (!m_bUseOCG)
1988 40 : poCatalog->optContent = nullptr;
1989 : #endif
1990 : try
1991 : {
1992 47 : poDoc->displayPageSlice(poSplashOut, m_iPage, m_dfDPI, m_dfDPI, 0,
1993 : TRUE, false, false, nReqXOff, nReqYOff,
1994 : nReqXSize, nReqYSize);
1995 : }
1996 0 : catch (const std::exception &e)
1997 : {
1998 0 : CPLError(CE_Failure, CPLE_AppDefined,
1999 0 : "PDFDoc::displayPageSlice() failed with %s", e.what());
2000 :
2001 : #ifdef USE_OPTCONTENT_HACK
2002 : /* Restore back */
2003 0 : poCatalog->optContent = poOldOCGs;
2004 : #endif
2005 0 : delete poSplashOut;
2006 0 : return CE_Failure;
2007 : }
2008 :
2009 : #ifdef USE_OPTCONTENT_HACK
2010 : /* Restore back */
2011 47 : poCatalog->optContent = poOldOCGs;
2012 : #endif
2013 :
2014 47 : SplashBitmap *poBitmap = poSplashOut->getBitmap();
2015 94 : if (poBitmap->getWidth() != nReqXSize ||
2016 47 : poBitmap->getHeight() != nReqYSize)
2017 : {
2018 0 : CPLError(
2019 : CE_Failure, CPLE_AppDefined,
2020 : "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)",
2021 : poBitmap->getWidth(), poBitmap->getHeight(), nReqXSize,
2022 : nReqYSize);
2023 0 : delete poSplashOut;
2024 0 : return CE_Failure;
2025 : }
2026 :
2027 47 : GByte *pabyDataR = pabyData;
2028 47 : GByte *pabyDataG = pabyData + nBandSpace;
2029 47 : GByte *pabyDataB = pabyData + 2 * nBandSpace;
2030 47 : GByte *pabyDataA = pabyData + 3 * nBandSpace;
2031 47 : GByte *pabySrc = poBitmap->getDataPtr();
2032 : GByte *pabyAlphaSrc =
2033 47 : reinterpret_cast<GByte *>(poBitmap->getAlphaPtr());
2034 : int i, j;
2035 19718 : for (j = 0; j < nReqYSize; j++)
2036 : {
2037 19170200 : for (i = 0; i < nReqXSize; i++)
2038 : {
2039 19150500 : if (nBands < 4)
2040 : {
2041 19101900 : pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0];
2042 19101900 : pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1];
2043 19101900 : pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2];
2044 : }
2045 : else
2046 : {
2047 48600 : pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2];
2048 48600 : pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1];
2049 48600 : pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0];
2050 48600 : pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i];
2051 : }
2052 : }
2053 19671 : pabyDataR += nLineSpace;
2054 19671 : pabyDataG += nLineSpace;
2055 19671 : pabyDataB += nLineSpace;
2056 19671 : pabyDataA += nLineSpace;
2057 19671 : pabyAlphaSrc += poBitmap->getAlphaRowSize();
2058 19671 : pabySrc += poBitmap->getRowSize();
2059 : }
2060 47 : delete poSplashOut;
2061 : }
2062 : #endif // HAVE_POPPLER
2063 :
2064 : #ifdef HAVE_PODOFO
2065 : if (m_bUseLib.test(PDFLIB_PODOFO))
2066 : {
2067 : if (m_bPdfToPpmFailed)
2068 : return CE_Failure;
2069 :
2070 : if (pszRenderingOptions != nullptr &&
2071 : !EQUAL(pszRenderingOptions, "RASTER,VECTOR,TEXT"))
2072 : {
2073 : CPLError(CE_Warning, CPLE_NotSupported,
2074 : "GDAL_PDF_RENDERING_OPTIONS only supported "
2075 : "when PDF lib is Poppler.");
2076 : }
2077 :
2078 : CPLString osTmpFilename;
2079 : int nRet;
2080 :
2081 : #ifdef notdef
2082 : int bUseSpawn =
2083 : CPLTestBool(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES"));
2084 : if (!bUseSpawn)
2085 : {
2086 : CPLString osCmd = CPLSPrintf(
2087 : "pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"",
2088 : dfDPI, nReqXOff, nReqYOff, nReqXSize, nReqYSize, iPage, iPage,
2089 : osFilename.c_str());
2090 :
2091 : if (!osUserPwd.empty())
2092 : {
2093 : osCmd += " -upw \"";
2094 : osCmd += osUserPwd;
2095 : osCmd += "\"";
2096 : }
2097 :
2098 : CPLString osTmpFilenamePrefix = CPLGenerateTempFilenameSafe("pdf");
2099 : osTmpFilename =
2100 : CPLSPrintf("%s-%d.ppm", osTmpFilenamePrefix.c_str(), iPage);
2101 : osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str());
2102 :
2103 : CPLDebug("PDF", "Running '%s'", osCmd.c_str());
2104 : nRet = CPLSystem(nullptr, osCmd.c_str());
2105 : }
2106 : else
2107 : #endif // notdef
2108 : {
2109 : char **papszArgs = nullptr;
2110 : papszArgs = CSLAddString(papszArgs, "pdftoppm");
2111 : papszArgs = CSLAddString(papszArgs, "-r");
2112 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", m_dfDPI));
2113 : papszArgs = CSLAddString(papszArgs, "-x");
2114 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff));
2115 : papszArgs = CSLAddString(papszArgs, "-y");
2116 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff));
2117 : papszArgs = CSLAddString(papszArgs, "-W");
2118 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize));
2119 : papszArgs = CSLAddString(papszArgs, "-H");
2120 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize));
2121 : papszArgs = CSLAddString(papszArgs, "-f");
2122 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage));
2123 : papszArgs = CSLAddString(papszArgs, "-l");
2124 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", m_iPage));
2125 : if (!m_osUserPwd.empty())
2126 : {
2127 : papszArgs = CSLAddString(papszArgs, "-upw");
2128 : papszArgs = CSLAddString(papszArgs, m_osUserPwd.c_str());
2129 : }
2130 : papszArgs = CSLAddString(papszArgs, m_osFilename.c_str());
2131 :
2132 : osTmpFilename = VSIMemGenerateHiddenFilename("pdf_temp.ppm");
2133 : VSILFILE *fpOut = VSIFOpenL(osTmpFilename, "wb");
2134 : if (fpOut != nullptr)
2135 : {
2136 : nRet = CPLSpawn(papszArgs, nullptr, fpOut, FALSE);
2137 : VSIFCloseL(fpOut);
2138 : }
2139 : else
2140 : nRet = -1;
2141 :
2142 : CSLDestroy(papszArgs);
2143 : }
2144 :
2145 : if (nRet == 0)
2146 : {
2147 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
2148 : osTmpFilename, GDAL_OF_RASTER, nullptr, nullptr, nullptr));
2149 : if (poDS)
2150 : {
2151 : if (poDS->GetRasterCount() == 3)
2152 : {
2153 : eErr = poDS->RasterIO(GF_Read, 0, 0, nReqXSize, nReqYSize,
2154 : pabyData, nReqXSize, nReqYSize,
2155 : GDT_Byte, 3, nullptr, nPixelSpace,
2156 : nLineSpace, nBandSpace, nullptr);
2157 : }
2158 : }
2159 : }
2160 : else
2161 : {
2162 : CPLDebug("PDF", "Ret code = %d", nRet);
2163 : m_bPdfToPpmFailed = true;
2164 : eErr = CE_Failure;
2165 : }
2166 : VSIUnlink(osTmpFilename);
2167 : }
2168 : #endif // HAVE_PODOFO
2169 : #ifdef HAVE_PDFIUM
2170 107 : if (m_bUseLib.test(PDFLIB_PDFIUM))
2171 : {
2172 60 : if (!m_poPagePdfium)
2173 : {
2174 0 : return CE_Failure;
2175 : }
2176 :
2177 : // Pdfium does not support multithreading
2178 60 : CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex, PDFIUM_MUTEX_TIMEOUT);
2179 :
2180 60 : CPLCreateOrAcquireMutex(&(m_poPagePdfium->readMutex),
2181 : PDFIUM_MUTEX_TIMEOUT);
2182 :
2183 : // Parsing content required before rastering
2184 : // can takes too long for PDF with large number of objects/layers
2185 60 : m_poPagePdfium->page->ParseContent();
2186 :
2187 : FPDF_BITMAP bitmap =
2188 60 : FPDFBitmap_Create(nReqXSize, nReqYSize, nBands == 4 /*alpha*/);
2189 : // As coded now, FPDFBitmap_Create cannot allocate more than 1 GB
2190 60 : if (bitmap == nullptr)
2191 : {
2192 : // Release mutex - following code is thread-safe
2193 0 : CPLReleaseMutex(m_poPagePdfium->readMutex);
2194 0 : CPLReleaseMutex(g_oPdfiumReadMutex);
2195 :
2196 : #ifdef notdef
2197 : // If the requested area is not too small, then try subdividing
2198 : if ((GIntBig)nReqXSize * nReqYSize * 4 > 1024 * 1024)
2199 : {
2200 : #ifdef DEBUG
2201 : CPLDebug(
2202 : "PDF",
2203 : "Subdividing PDFDataset::ReadPixels(%d, %d, %d, %d, "
2204 : "scaleFactor=%d)",
2205 : nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2206 : 1 << ((PDFRasterBand *)GetRasterBand(1))->nResolutionLevel);
2207 : #endif
2208 : if (nReqXSize >= nReqYSize)
2209 : {
2210 : eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize / 2,
2211 : nReqYSize, nPixelSpace, nLineSpace,
2212 : nBandSpace, pabyData);
2213 : if (eErr == CE_None)
2214 : {
2215 : eErr = ReadPixels(
2216 : nReqXSize / 2, nReqYOff, nReqXSize - nReqXSize / 2,
2217 : nReqYSize, nPixelSpace, nLineSpace, nBandSpace,
2218 : pabyData + nPixelSpace * (nReqXSize / 2));
2219 : }
2220 : }
2221 : else
2222 : {
2223 : eErr = ReadPixels(nReqXOff, nReqYOff, nReqXSize,
2224 : nReqYSize - nReqYSize / 2, nPixelSpace,
2225 : nLineSpace, nBandSpace, pabyData);
2226 : if (eErr == CE_None)
2227 : {
2228 : eErr =
2229 : ReadPixels(nReqXOff, nReqYSize / 2, nReqXSize,
2230 : nReqYSize - nReqYSize / 2, nPixelSpace,
2231 : nLineSpace, nBandSpace,
2232 : pabyData + nLineSpace * (nReqYSize / 2));
2233 : }
2234 : }
2235 : return eErr;
2236 : }
2237 : #endif
2238 :
2239 0 : CPLError(CE_Failure, CPLE_AppDefined,
2240 : "FPDFBitmap_Create(%d,%d) failed", nReqXSize, nReqYSize);
2241 :
2242 0 : return CE_Failure;
2243 : }
2244 : // alpha is 0% which is transported to FF if not alpha
2245 : // Default background color is white
2246 60 : FPDF_DWORD color = 0x00FFFFFF; // A,R,G,B
2247 60 : FPDFBitmap_FillRect(bitmap, 0, 0, nReqXSize, nReqYSize, color);
2248 :
2249 : #ifdef DEBUG
2250 : // start_x, start_y, size_x, size_y, rotate, flags
2251 60 : CPLDebug("PDF",
2252 : "PDFDataset::ReadPixels(%d, %d, %d, %d, scaleFactor=%d)",
2253 : nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2254 60 : 1 << cpl::down_cast<PDFRasterBand *>(GetRasterBand(1))
2255 60 : ->nResolutionLevel);
2256 :
2257 60 : CPLDebug("PDF", "FPDF_RenderPageBitmap(%d, %d, %d, %d)", -nReqXOff,
2258 : -nReqYOff, nRasterXSize, nRasterYSize);
2259 : #endif
2260 :
2261 : // Part of PDF is render with -x, -y, page_width, page_height
2262 : // (not requested size!)
2263 60 : PDFiumRenderPageBitmap(
2264 60 : bitmap, FPDFPageFromIPDFPage(m_poPagePdfium->page), -nReqXOff,
2265 : -nReqYOff, nRasterXSize, nRasterYSize, pszRenderingOptions);
2266 :
2267 60 : int stride = FPDFBitmap_GetStride(bitmap);
2268 : const GByte *buffer =
2269 60 : reinterpret_cast<const GByte *>(FPDFBitmap_GetBuffer(bitmap));
2270 :
2271 : // Release mutex - following code is thread-safe
2272 60 : CPLReleaseMutex(m_poPagePdfium->readMutex);
2273 60 : CPLReleaseMutex(g_oPdfiumReadMutex);
2274 :
2275 : // Source data is B, G, R, unused.
2276 : // Destination data is R, G, B (,A if is alpha)
2277 60 : GByte *pabyDataR = pabyData;
2278 60 : GByte *pabyDataG = pabyData + 1 * nBandSpace;
2279 60 : GByte *pabyDataB = pabyData + 2 * nBandSpace;
2280 60 : GByte *pabyDataA = pabyData + 3 * nBandSpace;
2281 : // Copied from Poppler
2282 : int i, j;
2283 23455 : for (j = 0; j < nReqYSize; j++)
2284 : {
2285 20752600 : for (i = 0; i < nReqXSize; i++)
2286 : {
2287 20729200 : pabyDataR[i * nPixelSpace] = buffer[(i * 4) + 2];
2288 20729200 : pabyDataG[i * nPixelSpace] = buffer[(i * 4) + 1];
2289 20729200 : pabyDataB[i * nPixelSpace] = buffer[(i * 4) + 0];
2290 20729200 : if (nBands == 4)
2291 : {
2292 20729200 : pabyDataA[i * nPixelSpace] = buffer[(i * 4) + 3];
2293 : }
2294 : }
2295 23395 : pabyDataR += nLineSpace;
2296 23395 : pabyDataG += nLineSpace;
2297 23395 : pabyDataB += nLineSpace;
2298 23395 : pabyDataA += nLineSpace;
2299 23395 : buffer += stride;
2300 : }
2301 60 : FPDFBitmap_Destroy(bitmap);
2302 : }
2303 : #endif // ~ HAVE_PDFIUM
2304 :
2305 107 : return eErr;
2306 : }
2307 :
2308 : /************************************************************************/
2309 : /* ==================================================================== */
2310 : /* PDFImageRasterBand */
2311 : /* ==================================================================== */
2312 : /************************************************************************/
2313 :
2314 : class PDFImageRasterBand final : public PDFRasterBand
2315 : {
2316 : friend class PDFDataset;
2317 :
2318 : public:
2319 : PDFImageRasterBand(PDFDataset *, int);
2320 :
2321 : virtual CPLErr IReadBlock(int, int, void *) override;
2322 : };
2323 :
2324 : /************************************************************************/
2325 : /* PDFImageRasterBand() */
2326 : /************************************************************************/
2327 :
2328 0 : PDFImageRasterBand::PDFImageRasterBand(PDFDataset *poDSIn, int nBandIn)
2329 0 : : PDFRasterBand(poDSIn, nBandIn, 0)
2330 : {
2331 0 : }
2332 :
2333 : /************************************************************************/
2334 : /* IReadBlock() */
2335 : /************************************************************************/
2336 :
2337 0 : CPLErr PDFImageRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
2338 : void *pImage)
2339 : {
2340 0 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
2341 0 : CPLAssert(poGDS->m_poImageObj != nullptr);
2342 :
2343 0 : if (!poGDS->m_bTried)
2344 : {
2345 0 : int nBands = (poGDS->nBands == 1) ? 1 : 3;
2346 0 : poGDS->m_bTried = true;
2347 0 : if (nBands == 3)
2348 : {
2349 0 : poGDS->m_pabyCachedData = static_cast<GByte *>(
2350 0 : VSIMalloc3(nBands, nRasterXSize, nRasterYSize));
2351 0 : if (poGDS->m_pabyCachedData == nullptr)
2352 0 : return CE_Failure;
2353 : }
2354 :
2355 0 : GDALPDFStream *poStream = poGDS->m_poImageObj->GetStream();
2356 0 : GByte *pabyStream = nullptr;
2357 :
2358 0 : if (poStream == nullptr ||
2359 0 : static_cast<size_t>(poStream->GetLength()) !=
2360 0 : static_cast<size_t>(nBands) * nRasterXSize * nRasterYSize ||
2361 0 : (pabyStream = reinterpret_cast<GByte *>(poStream->GetBytes())) ==
2362 : nullptr)
2363 : {
2364 0 : VSIFree(poGDS->m_pabyCachedData);
2365 0 : poGDS->m_pabyCachedData = nullptr;
2366 0 : return CE_Failure;
2367 : }
2368 :
2369 0 : if (nBands == 3)
2370 : {
2371 : /* pixel interleaved to band interleaved */
2372 0 : for (size_t i = 0;
2373 0 : i < static_cast<size_t>(nRasterXSize) * nRasterYSize; i++)
2374 : {
2375 0 : poGDS->m_pabyCachedData[0 * static_cast<size_t>(nRasterXSize) *
2376 : nRasterYSize +
2377 0 : i] = pabyStream[3 * i + 0];
2378 0 : poGDS->m_pabyCachedData[1 * static_cast<size_t>(nRasterXSize) *
2379 0 : nRasterYSize +
2380 0 : i] = pabyStream[3 * i + 1];
2381 0 : poGDS->m_pabyCachedData[2 * static_cast<size_t>(nRasterXSize) *
2382 0 : nRasterYSize +
2383 0 : i] = pabyStream[3 * i + 2];
2384 : }
2385 0 : VSIFree(pabyStream);
2386 : }
2387 : else
2388 0 : poGDS->m_pabyCachedData = pabyStream;
2389 : }
2390 :
2391 0 : if (poGDS->m_pabyCachedData == nullptr)
2392 0 : return CE_Failure;
2393 :
2394 0 : if (nBand == 4)
2395 0 : memset(pImage, 255, nRasterXSize);
2396 : else
2397 0 : memcpy(pImage,
2398 0 : poGDS->m_pabyCachedData +
2399 0 : static_cast<size_t>(nBand - 1) * nRasterXSize *
2400 0 : nRasterYSize +
2401 0 : static_cast<size_t>(nBlockYOff) * nRasterXSize,
2402 0 : nRasterXSize);
2403 :
2404 0 : return CE_None;
2405 : }
2406 :
2407 : /************************************************************************/
2408 : /* PDFDataset() */
2409 : /************************************************************************/
2410 :
2411 380 : PDFDataset::PDFDataset(PDFDataset *poParentDSIn, int nXSize, int nYSize)
2412 380 : : m_bIsOvrDS(poParentDSIn != nullptr),
2413 : #ifdef HAVE_PDFIUM
2414 380 : m_poDocPdfium(poParentDSIn ? poParentDSIn->m_poDocPdfium : nullptr),
2415 380 : m_poPagePdfium(poParentDSIn ? poParentDSIn->m_poPagePdfium : nullptr),
2416 : #endif
2417 1140 : m_bSetStyle(CPLTestBool(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES")))
2418 : {
2419 380 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2420 380 : nRasterXSize = nXSize;
2421 380 : nRasterYSize = nYSize;
2422 380 : if (poParentDSIn)
2423 2 : m_bUseLib = poParentDSIn->m_bUseLib;
2424 :
2425 380 : InitMapOperators();
2426 380 : }
2427 :
2428 : /************************************************************************/
2429 : /* IBuildOverviews() */
2430 : /************************************************************************/
2431 :
2432 2 : CPLErr PDFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
2433 : const int *panOverviewList, int nListBands,
2434 : const int *panBandList,
2435 : GDALProgressFunc pfnProgress,
2436 : void *pProgressData,
2437 : CSLConstList papszOptions)
2438 :
2439 : {
2440 : /* -------------------------------------------------------------------- */
2441 : /* In order for building external overviews to work properly we */
2442 : /* discard any concept of internal overviews when the user */
2443 : /* first requests to build external overviews. */
2444 : /* -------------------------------------------------------------------- */
2445 2 : if (!m_apoOvrDS.empty())
2446 : {
2447 2 : m_apoOvrDSBackup = std::move(m_apoOvrDS);
2448 2 : m_apoOvrDS.clear();
2449 : }
2450 :
2451 : // Prevents InitOverviews() to run
2452 2 : m_apoOvrDSBackup.emplace_back(nullptr);
2453 2 : const CPLErr eErr = GDALPamDataset::IBuildOverviews(
2454 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
2455 : pfnProgress, pProgressData, papszOptions);
2456 2 : m_apoOvrDSBackup.pop_back();
2457 2 : return eErr;
2458 : }
2459 :
2460 : /************************************************************************/
2461 : /* PDFFreeDoc() */
2462 : /************************************************************************/
2463 :
2464 : #ifdef HAVE_POPPLER
2465 170 : static void PDFFreeDoc(PDFDoc *poDoc)
2466 : {
2467 170 : if (poDoc)
2468 : {
2469 : /* hack to avoid potential cross heap issues on Win32 */
2470 : /* str is the VSIPDFFileStream object passed in the constructor of
2471 : * PDFDoc */
2472 : // NOTE: This is potentially very dangerous. See comment in
2473 : // VSIPDFFileStream::FillBuffer() */
2474 170 : delete poDoc->str;
2475 170 : poDoc->str = nullptr;
2476 :
2477 170 : delete poDoc;
2478 : }
2479 170 : }
2480 : #endif
2481 :
2482 : /************************************************************************/
2483 : /* GetCatalog() */
2484 : /************************************************************************/
2485 :
2486 1206 : GDALPDFObject *PDFDataset::GetCatalog()
2487 : {
2488 1206 : if (m_poCatalogObject)
2489 828 : return m_poCatalogObject;
2490 :
2491 : #ifdef HAVE_POPPLER
2492 378 : if (m_bUseLib.test(PDFLIB_POPPLER) && m_poDocPoppler)
2493 : {
2494 : m_poCatalogObjectPoppler =
2495 167 : std::make_unique<Object>(m_poDocPoppler->getXRef()->getCatalog());
2496 167 : if (!m_poCatalogObjectPoppler->isNull())
2497 167 : m_poCatalogObject =
2498 167 : new GDALPDFObjectPoppler(m_poCatalogObjectPoppler.get(), FALSE);
2499 : }
2500 : #endif
2501 :
2502 : #ifdef HAVE_PODOFO
2503 : if (m_bUseLib.test(PDFLIB_PODOFO) && m_poDocPodofo)
2504 : {
2505 : int nCatalogNum = 0;
2506 : int nCatalogGen = 0;
2507 : VSILFILE *fp = VSIFOpenL(m_osFilename.c_str(), "rb");
2508 : if (fp != nullptr)
2509 : {
2510 : GDALPDFUpdateWriter oWriter(fp);
2511 : if (oWriter.ParseTrailerAndXRef())
2512 : {
2513 : nCatalogNum = oWriter.GetCatalogNum().toInt();
2514 : nCatalogGen = oWriter.GetCatalogGen();
2515 : }
2516 : oWriter.Close();
2517 : }
2518 :
2519 : PoDoFo::PdfObject *poCatalogPodofo =
2520 : m_poDocPodofo->GetObjects().GetObject(
2521 : PoDoFo::PdfReference(nCatalogNum, nCatalogGen));
2522 : if (poCatalogPodofo)
2523 : m_poCatalogObject = new GDALPDFObjectPodofo(
2524 : poCatalogPodofo, m_poDocPodofo->GetObjects());
2525 : }
2526 : #endif
2527 :
2528 : #ifdef HAVE_PDFIUM
2529 378 : if (m_bUseLib.test(PDFLIB_PDFIUM) && m_poDocPdfium)
2530 : {
2531 211 : const CPDF_Dictionary *catalog = m_poDocPdfium->doc->GetRoot();
2532 211 : if (catalog)
2533 211 : m_poCatalogObject =
2534 : // coverity is confused by WrapRetain(), believing that multiple
2535 : // smart pointers manage the same raw pointer. Which is actually
2536 : // true, but a RetainPtr holds a reference counted object. It is
2537 : // thus safe to have several RetainPtr holding it.
2538 : // coverity[multiple_init_smart_ptr]
2539 211 : GDALPDFObjectPdfium::Build(pdfium::WrapRetain(catalog));
2540 : }
2541 : #endif // ~ HAVE_PDFIUM
2542 :
2543 378 : return m_poCatalogObject;
2544 : }
2545 :
2546 : /************************************************************************/
2547 : /* ~PDFDataset() */
2548 : /************************************************************************/
2549 :
2550 760 : PDFDataset::~PDFDataset()
2551 : {
2552 : #ifdef HAVE_PDFIUM
2553 380 : m_apoOvrDS.clear();
2554 380 : m_apoOvrDSBackup.clear();
2555 : #endif
2556 :
2557 380 : CPLFree(m_pabyCachedData);
2558 380 : m_pabyCachedData = nullptr;
2559 :
2560 380 : delete m_poNeatLine;
2561 380 : m_poNeatLine = nullptr;
2562 :
2563 : /* Collect data necessary to update */
2564 380 : int nNum = 0;
2565 380 : int nGen = 0;
2566 380 : GDALPDFDictionaryRW *poPageDictCopy = nullptr;
2567 380 : GDALPDFDictionaryRW *poCatalogDictCopy = nullptr;
2568 380 : if (m_poPageObj)
2569 : {
2570 378 : nNum = m_poPageObj->GetRefNum().toInt();
2571 378 : nGen = m_poPageObj->GetRefGen();
2572 779 : if (eAccess == GA_Update &&
2573 23 : (m_bProjDirty || m_bNeatLineDirty || m_bInfoDirty || m_bXMPDirty) &&
2574 424 : nNum != 0 && m_poPageObj != nullptr &&
2575 23 : m_poPageObj->GetType() == PDFObjectType_Dictionary)
2576 : {
2577 23 : poPageDictCopy = m_poPageObj->GetDictionary()->Clone();
2578 :
2579 23 : if (m_bXMPDirty)
2580 : {
2581 : /* We need the catalog because it points to the XMP Metadata
2582 : * object */
2583 6 : GetCatalog();
2584 12 : if (m_poCatalogObject &&
2585 6 : m_poCatalogObject->GetType() == PDFObjectType_Dictionary)
2586 : poCatalogDictCopy =
2587 6 : m_poCatalogObject->GetDictionary()->Clone();
2588 : }
2589 : }
2590 : }
2591 :
2592 : /* Close document (and file descriptor) to be able to open it */
2593 : /* in read-write mode afterwards */
2594 380 : delete m_poPageObj;
2595 380 : m_poPageObj = nullptr;
2596 380 : delete m_poCatalogObject;
2597 380 : m_poCatalogObject = nullptr;
2598 : #ifdef HAVE_POPPLER
2599 380 : if (m_bUseLib.test(PDFLIB_POPPLER))
2600 : {
2601 167 : m_poCatalogObjectPoppler.reset();
2602 167 : PDFFreeDoc(m_poDocPoppler);
2603 : }
2604 380 : m_poDocPoppler = nullptr;
2605 : #endif
2606 : #ifdef HAVE_PODOFO
2607 : if (m_bUseLib.test(PDFLIB_PODOFO))
2608 : {
2609 : delete m_poDocPodofo;
2610 : }
2611 : m_poDocPodofo = nullptr;
2612 : #endif
2613 : #ifdef HAVE_PDFIUM
2614 380 : if (!m_bIsOvrDS)
2615 : {
2616 376 : if (m_bUseLib.test(PDFLIB_PDFIUM))
2617 : {
2618 211 : UnloadPdfiumDocumentPage(&m_poDocPdfium, &m_poPagePdfium);
2619 : }
2620 : }
2621 380 : m_poDocPdfium = nullptr;
2622 380 : m_poPagePdfium = nullptr;
2623 : #endif // ~ HAVE_PDFIUM
2624 :
2625 : /* Now do the update */
2626 380 : if (poPageDictCopy)
2627 : {
2628 23 : VSILFILE *fp = VSIFOpenL(m_osFilename, "rb+");
2629 23 : if (fp != nullptr)
2630 : {
2631 46 : GDALPDFUpdateWriter oWriter(fp);
2632 23 : if (oWriter.ParseTrailerAndXRef())
2633 : {
2634 23 : if ((m_bProjDirty || m_bNeatLineDirty) &&
2635 : poPageDictCopy != nullptr)
2636 11 : oWriter.UpdateProj(this, m_dfDPI, poPageDictCopy,
2637 22 : GDALPDFObjectNum(nNum), nGen);
2638 :
2639 23 : if (m_bInfoDirty)
2640 6 : oWriter.UpdateInfo(this);
2641 :
2642 23 : if (m_bXMPDirty && poCatalogDictCopy != nullptr)
2643 6 : oWriter.UpdateXMP(this, poCatalogDictCopy);
2644 : }
2645 23 : oWriter.Close();
2646 : }
2647 : else
2648 : {
2649 0 : CPLError(CE_Failure, CPLE_AppDefined,
2650 : "Cannot open %s in update mode", m_osFilename.c_str());
2651 : }
2652 : }
2653 380 : delete poPageDictCopy;
2654 380 : poPageDictCopy = nullptr;
2655 380 : delete poCatalogDictCopy;
2656 380 : poCatalogDictCopy = nullptr;
2657 :
2658 380 : if (m_nGCPCount > 0)
2659 : {
2660 2 : GDALDeinitGCPs(m_nGCPCount, m_pasGCPList);
2661 2 : CPLFree(m_pasGCPList);
2662 2 : m_pasGCPList = nullptr;
2663 2 : m_nGCPCount = 0;
2664 : }
2665 :
2666 380 : CleanupIntermediateResources();
2667 :
2668 380 : m_apoLayers.clear();
2669 :
2670 : // Do that only after having destroyed Poppler objects
2671 380 : m_fp.reset();
2672 760 : }
2673 :
2674 : /************************************************************************/
2675 : /* IRasterIO() */
2676 : /************************************************************************/
2677 :
2678 1674 : CPLErr PDFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2679 : int nXSize, int nYSize, void *pData, int nBufXSize,
2680 : int nBufYSize, GDALDataType eBufType,
2681 : int nBandCount, BANDMAP_TYPE panBandMap,
2682 : GSpacing nPixelSpace, GSpacing nLineSpace,
2683 : GSpacing nBandSpace,
2684 : GDALRasterIOExtraArg *psExtraArg)
2685 : {
2686 : // Try to pass the request to the most appropriate overview dataset.
2687 1674 : if (nBufXSize < nXSize && nBufYSize < nYSize)
2688 : {
2689 0 : int bTried = FALSE;
2690 0 : const CPLErr eErr = TryOverviewRasterIO(
2691 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2692 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2693 : nBandSpace, psExtraArg, &bTried);
2694 0 : if (bTried)
2695 0 : return eErr;
2696 : }
2697 :
2698 : int nBandBlockXSize, nBandBlockYSize;
2699 1674 : int bReadPixels = FALSE;
2700 1674 : GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize);
2701 3348 : if (m_aiTiles.empty() && eRWFlag == GF_Read && nXSize == nBufXSize &&
2702 1674 : nYSize == nBufYSize &&
2703 1674 : (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) &&
2704 3350 : eBufType == GDT_Byte && nBandCount == nBands &&
2705 2 : (nBands >= 3 && panBandMap[0] == 1 && panBandMap[1] == 2 &&
2706 2 : panBandMap[2] == 3 && (nBands == 3 || panBandMap[3] == 4)))
2707 : {
2708 2 : bReadPixels = TRUE;
2709 : #ifdef HAVE_PODOFO
2710 : if (m_bUseLib.test(PDFLIB_PODOFO) && nBands == 4)
2711 : {
2712 : bReadPixels = FALSE;
2713 : }
2714 : #endif
2715 : }
2716 :
2717 1674 : if (bReadPixels)
2718 2 : return ReadPixels(nXOff, nYOff, nXSize, nYSize, nPixelSpace, nLineSpace,
2719 2 : nBandSpace, static_cast<GByte *>(pData));
2720 :
2721 1672 : if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_Byte)
2722 : {
2723 0 : m_bCacheBlocksForOtherBands = true;
2724 : }
2725 1672 : CPLErr eErr = GDALPamDataset::IRasterIO(
2726 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2727 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2728 : psExtraArg);
2729 1672 : m_bCacheBlocksForOtherBands = false;
2730 1672 : return eErr;
2731 : }
2732 :
2733 : /************************************************************************/
2734 : /* IRasterIO() */
2735 : /************************************************************************/
2736 :
2737 43215 : CPLErr PDFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2738 : int nXSize, int nYSize, void *pData,
2739 : int nBufXSize, int nBufYSize,
2740 : GDALDataType eBufType, GSpacing nPixelSpace,
2741 : GSpacing nLineSpace,
2742 : GDALRasterIOExtraArg *psExtraArg)
2743 : {
2744 43215 : PDFDataset *poGDS = cpl::down_cast<PDFDataset *>(poDS);
2745 :
2746 : // Try to pass the request to the most appropriate overview dataset.
2747 43215 : if (nBufXSize < nXSize && nBufYSize < nYSize)
2748 : {
2749 0 : int bTried = FALSE;
2750 0 : const CPLErr eErr = TryOverviewRasterIO(
2751 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2752 : eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
2753 0 : if (bTried)
2754 0 : return eErr;
2755 : }
2756 :
2757 43215 : if (nBufXSize != nXSize || nBufYSize != nYSize || eBufType != GDT_Byte)
2758 : {
2759 38177 : poGDS->m_bCacheBlocksForOtherBands = true;
2760 : }
2761 43215 : CPLErr eErr = GDALPamRasterBand::IRasterIO(
2762 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2763 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
2764 43215 : poGDS->m_bCacheBlocksForOtherBands = false;
2765 43215 : return eErr;
2766 : }
2767 :
2768 : /************************************************************************/
2769 : /* PDFDatasetErrorFunction() */
2770 : /************************************************************************/
2771 :
2772 : #ifdef HAVE_POPPLER
2773 :
2774 2 : static void PDFDatasetErrorFunctionCommon(const CPLString &osError)
2775 : {
2776 2 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
2777 2 : return;
2778 : /* Reported on newer USGS GeoPDF */
2779 0 : if (strcmp(osError.c_str(),
2780 0 : "Couldn't find group for reference to set OFF") == 0)
2781 : {
2782 0 : CPLDebug("PDF", "%s", osError.c_str());
2783 0 : return;
2784 : }
2785 :
2786 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
2787 : }
2788 :
2789 : static int g_nPopplerErrors = 0;
2790 : constexpr int MAX_POPPLER_ERRORS = 1000;
2791 :
2792 2 : static void PDFDatasetErrorFunction(ErrorCategory /* eErrCategory */,
2793 : Goffset nPos, const char *pszMsg)
2794 : {
2795 2 : if (g_nPopplerErrors >= MAX_POPPLER_ERRORS)
2796 : {
2797 : // If there are too many errors, then unregister ourselves and turn
2798 : // quiet error mode, as the error() function in poppler can spend
2799 : // significant time formatting an error message we won't emit...
2800 0 : setErrorCallback(nullptr);
2801 0 : globalParams->setErrQuiet(true);
2802 0 : return;
2803 : }
2804 :
2805 2 : g_nPopplerErrors++;
2806 4 : CPLString osError;
2807 :
2808 2 : if (nPos >= 0)
2809 : osError.Printf("Pos = " CPL_FRMT_GUIB ", ",
2810 0 : static_cast<GUIntBig>(nPos));
2811 2 : osError += pszMsg;
2812 2 : PDFDatasetErrorFunctionCommon(osError);
2813 : }
2814 : #endif
2815 :
2816 : /************************************************************************/
2817 : /* GDALPDFParseStreamContentOnlyDrawForm() */
2818 : /************************************************************************/
2819 :
2820 354 : static CPLString GDALPDFParseStreamContentOnlyDrawForm(const char *pszContent)
2821 : {
2822 708 : CPLString osToken;
2823 : char ch;
2824 354 : int nCurIdx = 0;
2825 708 : CPLString osCurrentForm;
2826 :
2827 : // CPLDebug("PDF", "content = %s", pszContent);
2828 :
2829 1198 : while ((ch = *pszContent) != '\0')
2830 : {
2831 1198 : if (ch == '%')
2832 : {
2833 : /* Skip comments until end-of-line */
2834 0 : while ((ch = *pszContent) != '\0')
2835 : {
2836 0 : if (ch == '\r' || ch == '\n')
2837 : break;
2838 0 : pszContent++;
2839 : }
2840 0 : if (ch == 0)
2841 0 : break;
2842 : }
2843 1198 : else if (ch == ' ' || ch == '\r' || ch == '\n')
2844 : {
2845 408 : if (!osToken.empty())
2846 : {
2847 408 : if (nCurIdx == 0 && osToken[0] == '/')
2848 : {
2849 54 : osCurrentForm = osToken.substr(1);
2850 54 : nCurIdx++;
2851 : }
2852 354 : else if (nCurIdx == 1 && osToken == "Do")
2853 : {
2854 0 : nCurIdx++;
2855 : }
2856 : else
2857 : {
2858 354 : return "";
2859 : }
2860 : }
2861 54 : osToken = "";
2862 : }
2863 : else
2864 790 : osToken += ch;
2865 844 : pszContent++;
2866 : }
2867 :
2868 0 : return osCurrentForm;
2869 : }
2870 :
2871 : /************************************************************************/
2872 : /* GDALPDFParseStreamContent() */
2873 : /************************************************************************/
2874 :
2875 : typedef enum
2876 : {
2877 : STATE_INIT,
2878 : STATE_AFTER_q,
2879 : STATE_AFTER_cm,
2880 : STATE_AFTER_Do
2881 : } PDFStreamState;
2882 :
2883 : /* This parser is reduced to understanding sequences that draw rasters, such as
2884 : :
2885 : q
2886 : scaleX 0 0 scaleY translateX translateY cm
2887 : /ImXXX Do
2888 : Q
2889 :
2890 : All other sequences will abort the parsing.
2891 :
2892 : Returns TRUE if the stream only contains images.
2893 : */
2894 :
2895 354 : static int GDALPDFParseStreamContent(const char *pszContent,
2896 : GDALPDFDictionary *poXObjectDict,
2897 : double *pdfDPI, int *pbDPISet,
2898 : int *pnBands,
2899 : std::vector<GDALPDFTileDesc> &asTiles,
2900 : int bAcceptRotationTerms)
2901 : {
2902 708 : CPLString osToken;
2903 : char ch;
2904 354 : PDFStreamState nState = STATE_INIT;
2905 354 : int nCurIdx = 0;
2906 : double adfVals[6];
2907 708 : CPLString osCurrentImage;
2908 :
2909 354 : double dfDPI = DEFAULT_DPI;
2910 354 : *pbDPISet = FALSE;
2911 :
2912 21695 : while ((ch = *pszContent) != '\0')
2913 : {
2914 21426 : if (ch == '%')
2915 : {
2916 : /* Skip comments until end-of-line */
2917 0 : while ((ch = *pszContent) != '\0')
2918 : {
2919 0 : if (ch == '\r' || ch == '\n')
2920 : break;
2921 0 : pszContent++;
2922 : }
2923 0 : if (ch == 0)
2924 0 : break;
2925 : }
2926 21426 : else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2927 : {
2928 6454 : if (!osToken.empty())
2929 : {
2930 6454 : if (nState == STATE_INIT)
2931 : {
2932 664 : if (osToken == "q")
2933 : {
2934 579 : nState = STATE_AFTER_q;
2935 579 : nCurIdx = 0;
2936 : }
2937 85 : else if (osToken != "Q")
2938 85 : return FALSE;
2939 : }
2940 5790 : else if (nState == STATE_AFTER_q)
2941 : {
2942 4053 : if (osToken == "q")
2943 : {
2944 : // ignore
2945 : }
2946 4053 : else if (nCurIdx < 6)
2947 : {
2948 3474 : adfVals[nCurIdx++] = CPLAtof(osToken);
2949 : }
2950 579 : else if (nCurIdx == 6 && osToken == "cm")
2951 : {
2952 579 : nState = STATE_AFTER_cm;
2953 579 : nCurIdx = 0;
2954 : }
2955 : else
2956 0 : return FALSE;
2957 : }
2958 1737 : else if (nState == STATE_AFTER_cm)
2959 : {
2960 1158 : if (nCurIdx == 0 && osToken[0] == '/')
2961 : {
2962 579 : osCurrentImage = osToken.substr(1);
2963 : }
2964 579 : else if (osToken == "Do")
2965 : {
2966 579 : nState = STATE_AFTER_Do;
2967 : }
2968 : else
2969 0 : return FALSE;
2970 : }
2971 579 : else if (nState == STATE_AFTER_Do)
2972 : {
2973 579 : if (osToken == "Q")
2974 : {
2975 : GDALPDFObject *poImage =
2976 579 : poXObjectDict->Get(osCurrentImage);
2977 1158 : if (poImage != nullptr &&
2978 579 : poImage->GetType() == PDFObjectType_Dictionary)
2979 : {
2980 : GDALPDFTileDesc sTile;
2981 : GDALPDFDictionary *poImageDict =
2982 579 : poImage->GetDictionary();
2983 579 : GDALPDFObject *poWidth = poImageDict->Get("Width");
2984 : GDALPDFObject *poHeight =
2985 579 : poImageDict->Get("Height");
2986 : GDALPDFObject *poColorSpace =
2987 579 : poImageDict->Get("ColorSpace");
2988 579 : GDALPDFObject *poSMask = poImageDict->Get("SMask");
2989 1158 : if (poColorSpace &&
2990 579 : poColorSpace->GetType() == PDFObjectType_Name)
2991 : {
2992 573 : if (poColorSpace->GetName() == "DeviceRGB")
2993 : {
2994 221 : sTile.nBands = 3;
2995 221 : if (*pnBands < 3)
2996 47 : *pnBands = 3;
2997 : }
2998 352 : else if (poColorSpace->GetName() ==
2999 : "DeviceGray")
3000 : {
3001 352 : sTile.nBands = 1;
3002 352 : if (*pnBands < 1)
3003 244 : *pnBands = 1;
3004 : }
3005 : else
3006 0 : sTile.nBands = 0;
3007 : }
3008 579 : if (poSMask != nullptr)
3009 190 : *pnBands = 4;
3010 :
3011 579 : if (poWidth && poHeight &&
3012 0 : ((bAcceptRotationTerms &&
3013 579 : adfVals[1] == -adfVals[2]) ||
3014 579 : (!bAcceptRotationTerms && adfVals[1] == 0.0 &&
3015 579 : adfVals[2] == 0.0)))
3016 : {
3017 579 : double dfWidth = Get(poWidth);
3018 579 : double dfHeight = Get(poHeight);
3019 579 : double dfScaleX = adfVals[0];
3020 579 : double dfScaleY = adfVals[3];
3021 579 : if (dfWidth > 0 && dfHeight > 0 &&
3022 579 : dfScaleX > 0 && dfScaleY > 0 &&
3023 579 : dfWidth / dfScaleX * DEFAULT_DPI <
3024 579 : INT_MAX &&
3025 579 : dfHeight / dfScaleY * DEFAULT_DPI < INT_MAX)
3026 : {
3027 1158 : double dfDPI_X = ROUND_IF_CLOSE(
3028 579 : dfWidth / dfScaleX * DEFAULT_DPI, 1e-3);
3029 1158 : double dfDPI_Y = ROUND_IF_CLOSE(
3030 579 : dfHeight / dfScaleY * DEFAULT_DPI,
3031 : 1e-3);
3032 : // CPLDebug("PDF", "Image %s, width = %.16g,
3033 : // height = %.16g, scaleX = %.16g, scaleY =
3034 : // %.16g --> DPI_X = %.16g, DPI_Y = %.16g",
3035 : // osCurrentImage.c_str(),
3036 : // dfWidth, dfHeight,
3037 : // dfScaleX, dfScaleY,
3038 : // dfDPI_X, dfDPI_Y);
3039 579 : if (dfDPI_X > dfDPI)
3040 40 : dfDPI = dfDPI_X;
3041 579 : if (dfDPI_Y > dfDPI)
3042 0 : dfDPI = dfDPI_Y;
3043 :
3044 579 : memcpy(&(sTile.adfCM), adfVals,
3045 : 6 * sizeof(double));
3046 579 : sTile.poImage = poImage;
3047 579 : sTile.dfWidth = dfWidth;
3048 579 : sTile.dfHeight = dfHeight;
3049 579 : asTiles.push_back(sTile);
3050 :
3051 579 : *pbDPISet = TRUE;
3052 579 : *pdfDPI = dfDPI;
3053 : }
3054 : }
3055 : }
3056 579 : nState = STATE_INIT;
3057 : }
3058 : else
3059 0 : return FALSE;
3060 : }
3061 : }
3062 6369 : osToken = "";
3063 : }
3064 : else
3065 14972 : osToken += ch;
3066 21341 : pszContent++;
3067 : }
3068 :
3069 269 : return TRUE;
3070 : }
3071 :
3072 : /************************************************************************/
3073 : /* CheckTiledRaster() */
3074 : /************************************************************************/
3075 :
3076 297 : int PDFDataset::CheckTiledRaster()
3077 : {
3078 : size_t i;
3079 297 : int l_nBlockXSize = 0;
3080 297 : int l_nBlockYSize = 0;
3081 297 : const double dfUserUnit = m_dfDPI * USER_UNIT_IN_INCH;
3082 :
3083 : /* First pass : check that all tiles have same DPI, */
3084 : /* are contained entirely in the raster size, */
3085 : /* and determine the block size */
3086 870 : for (i = 0; i < m_asTiles.size(); i++)
3087 : {
3088 579 : double dfDrawWidth = m_asTiles[i].adfCM[0] * dfUserUnit;
3089 579 : double dfDrawHeight = m_asTiles[i].adfCM[3] * dfUserUnit;
3090 579 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3091 579 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3092 579 : int nX = static_cast<int>(dfX + 0.1);
3093 579 : int nY = static_cast<int>(dfY + 0.1);
3094 579 : int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8);
3095 579 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3096 :
3097 579 : GDALPDFDictionary *poImageDict = m_asTiles[i].poImage->GetDictionary();
3098 : GDALPDFObject *poBitsPerComponent =
3099 579 : poImageDict->Get("BitsPerComponent");
3100 579 : GDALPDFObject *poColorSpace = poImageDict->Get("ColorSpace");
3101 579 : GDALPDFObject *poFilter = poImageDict->Get("Filter");
3102 :
3103 : /* Podofo cannot uncompress JPEG2000 streams */
3104 579 : if (m_bUseLib.test(PDFLIB_PODOFO) && poFilter != nullptr &&
3105 579 : poFilter->GetType() == PDFObjectType_Name &&
3106 0 : poFilter->GetName() == "JPXDecode")
3107 : {
3108 0 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
3109 : static_cast<int>(i));
3110 0 : return FALSE;
3111 : }
3112 :
3113 579 : if (poBitsPerComponent == nullptr || Get(poBitsPerComponent) != 8 ||
3114 579 : poColorSpace == nullptr ||
3115 2083 : poColorSpace->GetType() != PDFObjectType_Name ||
3116 925 : (poColorSpace->GetName() != "DeviceRGB" &&
3117 352 : poColorSpace->GetName() != "DeviceGray"))
3118 : {
3119 6 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
3120 : static_cast<int>(i));
3121 6 : return FALSE;
3122 : }
3123 :
3124 573 : if (fabs(dfDrawWidth - m_asTiles[i].dfWidth) > 1e-2 ||
3125 573 : fabs(dfDrawHeight - m_asTiles[i].dfHeight) > 1e-2 ||
3126 573 : fabs(nWidth - m_asTiles[i].dfWidth) > 1e-8 ||
3127 573 : fabs(nHeight - m_asTiles[i].dfHeight) > 1e-8 ||
3128 573 : fabs(nX - dfX) > 1e-1 || fabs(nY - dfY) > 1e-1 || nX < 0 ||
3129 1146 : nY < 0 || nX + nWidth > nRasterXSize || nY >= nRasterYSize)
3130 : {
3131 0 : CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", static_cast<int>(i),
3132 0 : dfX, dfY, dfDrawWidth, dfDrawHeight, m_asTiles[i].dfWidth,
3133 0 : m_asTiles[i].dfHeight);
3134 0 : return FALSE;
3135 : }
3136 573 : if (l_nBlockXSize == 0 && l_nBlockYSize == 0 && nX == 0 && nY != 0)
3137 : {
3138 18 : l_nBlockXSize = nWidth;
3139 18 : l_nBlockYSize = nHeight;
3140 : }
3141 : }
3142 291 : if (l_nBlockXSize <= 0 || l_nBlockYSize <= 0 || l_nBlockXSize > 2048 ||
3143 : l_nBlockYSize > 2048)
3144 273 : return FALSE;
3145 :
3146 18 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, l_nBlockXSize);
3147 18 : int nYBlocks = DIV_ROUND_UP(nRasterYSize, l_nBlockYSize);
3148 :
3149 : /* Second pass to determine that all tiles are properly aligned on block
3150 : * size */
3151 318 : for (i = 0; i < m_asTiles.size(); i++)
3152 : {
3153 300 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3154 300 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3155 300 : int nX = static_cast<int>(dfX + 0.1);
3156 300 : int nY = static_cast<int>(dfY + 0.1);
3157 300 : int nWidth = static_cast<int>(m_asTiles[i].dfWidth + 1e-8);
3158 300 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3159 300 : int bOK = TRUE;
3160 300 : int nBlockXOff = nX / l_nBlockXSize;
3161 300 : if ((nX % l_nBlockXSize) != 0)
3162 0 : bOK = FALSE;
3163 300 : if (nBlockXOff < nXBlocks - 1 && nWidth != l_nBlockXSize)
3164 0 : bOK = FALSE;
3165 300 : if (nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize)
3166 0 : bOK = FALSE;
3167 :
3168 300 : if (nY > 0 && nHeight != l_nBlockYSize)
3169 0 : bOK = FALSE;
3170 300 : if (nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * l_nBlockYSize)
3171 0 : bOK = FALSE;
3172 :
3173 300 : if (!bOK)
3174 : {
3175 0 : CPLDebug("PDF", "Tile %d : %d %d %d %d", static_cast<int>(i), nX,
3176 : nY, nWidth, nHeight);
3177 0 : return FALSE;
3178 : }
3179 : }
3180 :
3181 : /* Third pass to set the aiTiles array */
3182 18 : m_aiTiles.resize(static_cast<size_t>(nXBlocks) * nYBlocks, -1);
3183 318 : for (i = 0; i < m_asTiles.size(); i++)
3184 : {
3185 300 : double dfX = m_asTiles[i].adfCM[4] * dfUserUnit;
3186 300 : double dfY = m_asTiles[i].adfCM[5] * dfUserUnit;
3187 300 : int nHeight = static_cast<int>(m_asTiles[i].dfHeight + 1e-8);
3188 300 : int nX = static_cast<int>(dfX + 0.1);
3189 300 : int nY = nRasterYSize - (static_cast<int>(dfY + 0.1) + nHeight);
3190 300 : int nBlockXOff = nX / l_nBlockXSize;
3191 300 : int nBlockYOff = nY / l_nBlockYSize;
3192 300 : m_aiTiles[nBlockYOff * nXBlocks + nBlockXOff] = static_cast<int>(i);
3193 : }
3194 :
3195 18 : this->m_nBlockXSize = l_nBlockXSize;
3196 18 : this->m_nBlockYSize = l_nBlockYSize;
3197 :
3198 18 : return TRUE;
3199 : }
3200 :
3201 : /************************************************************************/
3202 : /* GuessDPI() */
3203 : /************************************************************************/
3204 :
3205 378 : void PDFDataset::GuessDPI(GDALPDFDictionary *poPageDict, int *pnBands)
3206 : {
3207 378 : const char *pszDPI = GetOption(papszOpenOptions, "DPI", nullptr);
3208 378 : if (pszDPI != nullptr)
3209 : {
3210 : // coverity[tainted_data]
3211 4 : m_dfDPI = CPLAtof(pszDPI);
3212 : }
3213 : else
3214 : {
3215 : /* Try to get a better value from the images that are drawn */
3216 : /* Very simplistic logic. Will only work for raster only PDF */
3217 :
3218 374 : GDALPDFObject *poContents = poPageDict->Get("Contents");
3219 746 : if (poContents != nullptr &&
3220 372 : poContents->GetType() == PDFObjectType_Array)
3221 : {
3222 1 : GDALPDFArray *poContentsArray = poContents->GetArray();
3223 1 : if (poContentsArray->GetLength() == 1)
3224 : {
3225 1 : poContents = poContentsArray->Get(0);
3226 : }
3227 : }
3228 :
3229 : GDALPDFObject *poXObject =
3230 374 : poPageDict->LookupObject("Resources.XObject");
3231 746 : if (poContents != nullptr &&
3232 372 : poContents->GetType() == PDFObjectType_Dictionary &&
3233 746 : poXObject != nullptr &&
3234 356 : poXObject->GetType() == PDFObjectType_Dictionary)
3235 : {
3236 356 : GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary();
3237 356 : GDALPDFDictionary *poContentDict = poXObjectDict;
3238 356 : GDALPDFStream *poPageStream = poContents->GetStream();
3239 356 : if (poPageStream != nullptr)
3240 : {
3241 354 : char *pszContent = nullptr;
3242 354 : const int64_t MAX_LENGTH = 10 * 1000 * 1000;
3243 354 : int64_t nLength = poPageStream->GetLength(MAX_LENGTH);
3244 354 : int bResetTiles = FALSE;
3245 354 : double dfScaleDPI = 1.0;
3246 :
3247 354 : if (nLength < MAX_LENGTH)
3248 : {
3249 708 : CPLString osForm;
3250 354 : pszContent = poPageStream->GetBytes();
3251 354 : if (pszContent != nullptr)
3252 : {
3253 : #ifdef DEBUG
3254 : const char *pszDumpStream =
3255 354 : CPLGetConfigOption("PDF_DUMP_STREAM", nullptr);
3256 354 : if (pszDumpStream != nullptr)
3257 : {
3258 0 : VSILFILE *fpDump = VSIFOpenL(pszDumpStream, "wb");
3259 0 : if (fpDump)
3260 : {
3261 0 : VSIFWriteL(pszContent, 1,
3262 : static_cast<int>(nLength), fpDump);
3263 0 : VSIFCloseL(fpDump);
3264 : }
3265 : }
3266 : #endif // DEBUG
3267 : osForm =
3268 354 : GDALPDFParseStreamContentOnlyDrawForm(pszContent);
3269 354 : if (osForm.empty())
3270 : {
3271 : /* Special case for USGS Topo PDF, like
3272 : * CA_Hollywood_20090811_OM_geo.pdf */
3273 : const char *pszOGCDo =
3274 354 : strstr(pszContent, " /XO1 Do");
3275 354 : if (pszOGCDo)
3276 : {
3277 0 : const char *pszcm = strstr(pszContent, " cm ");
3278 0 : if (pszcm != nullptr && pszcm < pszOGCDo)
3279 : {
3280 : const char *pszNextcm =
3281 0 : strstr(pszcm + 2, "cm");
3282 0 : if (pszNextcm == nullptr ||
3283 : pszNextcm > pszOGCDo)
3284 : {
3285 0 : const char *pszIter = pszcm;
3286 0 : while (pszIter > pszContent)
3287 : {
3288 0 : if ((*pszIter >= '0' &&
3289 0 : *pszIter <= '9') ||
3290 0 : *pszIter == '-' ||
3291 0 : *pszIter == '.' ||
3292 0 : *pszIter == ' ')
3293 0 : pszIter--;
3294 : else
3295 : {
3296 0 : pszIter++;
3297 0 : break;
3298 : }
3299 : }
3300 0 : CPLString oscm(pszIter);
3301 0 : oscm.resize(pszcm - pszIter);
3302 : char **papszTokens =
3303 0 : CSLTokenizeString(oscm);
3304 0 : double dfScaleX = -1.0;
3305 0 : double dfScaleY = -2.0;
3306 0 : if (CSLCount(papszTokens) == 6)
3307 : {
3308 0 : dfScaleX = CPLAtof(papszTokens[0]);
3309 0 : dfScaleY = CPLAtof(papszTokens[3]);
3310 : }
3311 0 : CSLDestroy(papszTokens);
3312 0 : if (dfScaleX == dfScaleY &&
3313 : dfScaleX > 0.0)
3314 : {
3315 0 : osForm = "XO1";
3316 0 : bResetTiles = TRUE;
3317 0 : dfScaleDPI = 1.0 / dfScaleX;
3318 : }
3319 0 : }
3320 : }
3321 : else
3322 : {
3323 0 : osForm = "XO1";
3324 0 : bResetTiles = TRUE;
3325 : }
3326 : }
3327 : /* Special case for USGS Topo PDF, like
3328 : * CA_Sacramento_East_20120308_TM_geo.pdf */
3329 : else
3330 : {
3331 : CPLString osOCG =
3332 708 : FindLayerOCG(poPageDict, "Orthoimage");
3333 354 : if (!osOCG.empty())
3334 : {
3335 0 : const char *pszBDCLookup = CPLSPrintf(
3336 : "/OC /%s BDC", osOCG.c_str());
3337 : const char *pszBDC =
3338 0 : strstr(pszContent, pszBDCLookup);
3339 0 : if (pszBDC != nullptr)
3340 : {
3341 0 : const char *pszIter =
3342 0 : pszBDC + strlen(pszBDCLookup);
3343 0 : while (*pszIter != '\0')
3344 : {
3345 0 : if (*pszIter == 13 ||
3346 0 : *pszIter == 10 ||
3347 0 : *pszIter == ' ' ||
3348 0 : *pszIter == 'q')
3349 0 : pszIter++;
3350 : else
3351 : break;
3352 : }
3353 0 : if (STARTS_WITH(pszIter,
3354 : "1 0 0 1 0 0 cm\n"))
3355 0 : pszIter +=
3356 : strlen("1 0 0 1 0 0 cm\n");
3357 0 : if (*pszIter == '/')
3358 : {
3359 0 : pszIter++;
3360 : const char *pszDo =
3361 0 : strstr(pszIter, " Do");
3362 0 : if (pszDo != nullptr)
3363 : {
3364 0 : osForm = pszIter;
3365 0 : osForm.resize(pszDo - pszIter);
3366 0 : bResetTiles = TRUE;
3367 : }
3368 : }
3369 : }
3370 : }
3371 : }
3372 : }
3373 : }
3374 :
3375 354 : if (!osForm.empty())
3376 : {
3377 0 : CPLFree(pszContent);
3378 0 : pszContent = nullptr;
3379 :
3380 0 : GDALPDFObject *poObjForm = poXObjectDict->Get(osForm);
3381 0 : if (poObjForm != nullptr &&
3382 0 : poObjForm->GetType() == PDFObjectType_Dictionary &&
3383 0 : (poPageStream = poObjForm->GetStream()) != nullptr)
3384 : {
3385 : GDALPDFDictionary *poObjFormDict =
3386 0 : poObjForm->GetDictionary();
3387 : GDALPDFObject *poSubtype =
3388 0 : poObjFormDict->Get("Subtype");
3389 0 : if (poSubtype != nullptr &&
3390 0 : poSubtype->GetType() == PDFObjectType_Name &&
3391 0 : poSubtype->GetName() == "Form")
3392 : {
3393 0 : nLength = poPageStream->GetLength(MAX_LENGTH);
3394 0 : if (nLength < MAX_LENGTH)
3395 : {
3396 0 : pszContent = poPageStream->GetBytes();
3397 :
3398 : GDALPDFObject *poXObject2 =
3399 0 : poObjFormDict->LookupObject(
3400 : "Resources.XObject");
3401 0 : if (poXObject2 != nullptr &&
3402 0 : poXObject2->GetType() ==
3403 : PDFObjectType_Dictionary)
3404 : poContentDict =
3405 0 : poXObject2->GetDictionary();
3406 : }
3407 : }
3408 : }
3409 : }
3410 : }
3411 :
3412 354 : if (pszContent != nullptr)
3413 : {
3414 354 : int bDPISet = FALSE;
3415 :
3416 354 : const char *pszContentToParse = pszContent;
3417 354 : if (bResetTiles)
3418 : {
3419 0 : while (*pszContentToParse != '\0')
3420 : {
3421 0 : if (*pszContentToParse == 13 ||
3422 0 : *pszContentToParse == 10 ||
3423 0 : *pszContentToParse == ' ' ||
3424 0 : (*pszContentToParse >= '0' &&
3425 0 : *pszContentToParse <= '9') ||
3426 0 : *pszContentToParse == '.' ||
3427 0 : *pszContentToParse == '-' ||
3428 0 : *pszContentToParse == 'l' ||
3429 0 : *pszContentToParse == 'm' ||
3430 0 : *pszContentToParse == 'n' ||
3431 0 : *pszContentToParse == 'W')
3432 0 : pszContentToParse++;
3433 : else
3434 : break;
3435 : }
3436 : }
3437 :
3438 354 : GDALPDFParseStreamContent(pszContentToParse, poContentDict,
3439 : &(m_dfDPI), &bDPISet, pnBands,
3440 354 : m_asTiles, bResetTiles);
3441 354 : CPLFree(pszContent);
3442 354 : if (bDPISet)
3443 : {
3444 297 : m_dfDPI *= dfScaleDPI;
3445 :
3446 297 : CPLDebug("PDF",
3447 : "DPI guessed from contents stream = %.16g",
3448 : m_dfDPI);
3449 297 : SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI));
3450 297 : if (bResetTiles)
3451 0 : m_asTiles.resize(0);
3452 : }
3453 : else
3454 57 : m_asTiles.resize(0);
3455 : }
3456 : }
3457 : }
3458 :
3459 374 : GDALPDFObject *poUserUnit = nullptr;
3460 686 : if ((poUserUnit = poPageDict->Get("UserUnit")) != nullptr &&
3461 312 : (poUserUnit->GetType() == PDFObjectType_Int ||
3462 23 : poUserUnit->GetType() == PDFObjectType_Real))
3463 : {
3464 312 : m_dfDPI = ROUND_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI, 1e-5);
3465 312 : CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", m_dfDPI);
3466 312 : SetMetadataItem("DPI", CPLSPrintf("%.16g", m_dfDPI));
3467 : }
3468 : }
3469 :
3470 378 : if (m_dfDPI < 1e-2 || m_dfDPI > 7200)
3471 : {
3472 0 : CPLError(CE_Warning, CPLE_AppDefined,
3473 : "Invalid value for GDAL_PDF_DPI. Using default value instead");
3474 0 : m_dfDPI = GDAL_DEFAULT_DPI;
3475 : }
3476 378 : }
3477 :
3478 : /************************************************************************/
3479 : /* FindXMP() */
3480 : /************************************************************************/
3481 :
3482 0 : void PDFDataset::FindXMP(GDALPDFObject *poObj)
3483 : {
3484 0 : if (poObj->GetType() != PDFObjectType_Dictionary)
3485 0 : return;
3486 :
3487 0 : GDALPDFDictionary *poDict = poObj->GetDictionary();
3488 0 : GDALPDFObject *poType = poDict->Get("Type");
3489 0 : GDALPDFObject *poSubtype = poDict->Get("Subtype");
3490 0 : if (poType == nullptr || poType->GetType() != PDFObjectType_Name ||
3491 0 : poType->GetName() != "Metadata" || poSubtype == nullptr ||
3492 0 : poSubtype->GetType() != PDFObjectType_Name ||
3493 0 : poSubtype->GetName() != "XML")
3494 : {
3495 0 : return;
3496 : }
3497 :
3498 0 : GDALPDFStream *poStream = poObj->GetStream();
3499 0 : if (poStream == nullptr)
3500 0 : return;
3501 :
3502 0 : char *pszContent = poStream->GetBytes();
3503 0 : const auto nLength = poStream->GetLength();
3504 0 : if (pszContent != nullptr && nLength > 15 &&
3505 0 : STARTS_WITH(pszContent, "<?xpacket begin="))
3506 : {
3507 : char *apszMDList[2];
3508 0 : apszMDList[0] = pszContent;
3509 0 : apszMDList[1] = nullptr;
3510 0 : SetMetadata(apszMDList, "xml:XMP");
3511 : }
3512 0 : CPLFree(pszContent);
3513 : }
3514 :
3515 : /************************************************************************/
3516 : /* ParseInfo() */
3517 : /************************************************************************/
3518 :
3519 204 : void PDFDataset::ParseInfo(GDALPDFObject *poInfoObj)
3520 : {
3521 204 : if (poInfoObj->GetType() != PDFObjectType_Dictionary)
3522 136 : return;
3523 :
3524 68 : GDALPDFDictionary *poInfoObjDict = poInfoObj->GetDictionary();
3525 68 : GDALPDFObject *poItem = nullptr;
3526 68 : int bOneMDISet = FALSE;
3527 85 : if ((poItem = poInfoObjDict->Get("Author")) != nullptr &&
3528 17 : poItem->GetType() == PDFObjectType_String)
3529 : {
3530 17 : SetMetadataItem("AUTHOR", poItem->GetString().c_str());
3531 17 : bOneMDISet = TRUE;
3532 : }
3533 113 : if ((poItem = poInfoObjDict->Get("Creator")) != nullptr &&
3534 45 : poItem->GetType() == PDFObjectType_String)
3535 : {
3536 45 : SetMetadataItem("CREATOR", poItem->GetString().c_str());
3537 45 : bOneMDISet = TRUE;
3538 : }
3539 76 : if ((poItem = poInfoObjDict->Get("Keywords")) != nullptr &&
3540 8 : poItem->GetType() == PDFObjectType_String)
3541 : {
3542 8 : SetMetadataItem("KEYWORDS", poItem->GetString().c_str());
3543 8 : bOneMDISet = TRUE;
3544 : }
3545 79 : if ((poItem = poInfoObjDict->Get("Subject")) != nullptr &&
3546 11 : poItem->GetType() == PDFObjectType_String)
3547 : {
3548 11 : SetMetadataItem("SUBJECT", poItem->GetString().c_str());
3549 11 : bOneMDISet = TRUE;
3550 : }
3551 80 : if ((poItem = poInfoObjDict->Get("Title")) != nullptr &&
3552 12 : poItem->GetType() == PDFObjectType_String)
3553 : {
3554 12 : SetMetadataItem("TITLE", poItem->GetString().c_str());
3555 12 : bOneMDISet = TRUE;
3556 : }
3557 91 : if ((poItem = poInfoObjDict->Get("Producer")) != nullptr &&
3558 23 : poItem->GetType() == PDFObjectType_String)
3559 : {
3560 34 : if (bOneMDISet ||
3561 11 : poItem->GetString() != "PoDoFo - http://podofo.sf.net")
3562 : {
3563 12 : SetMetadataItem("PRODUCER", poItem->GetString().c_str());
3564 12 : bOneMDISet = TRUE;
3565 : }
3566 : }
3567 116 : if ((poItem = poInfoObjDict->Get("CreationDate")) != nullptr &&
3568 48 : poItem->GetType() == PDFObjectType_String)
3569 : {
3570 48 : if (bOneMDISet)
3571 37 : SetMetadataItem("CREATION_DATE", poItem->GetString().c_str());
3572 : }
3573 : }
3574 :
3575 : #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
3576 :
3577 : /************************************************************************/
3578 : /* AddLayer() */
3579 : /************************************************************************/
3580 :
3581 468 : void PDFDataset::AddLayer(const std::string &osName, int iPage)
3582 : {
3583 936 : LayerStruct layerStruct;
3584 468 : layerStruct.osName = osName;
3585 468 : layerStruct.nInsertIdx = static_cast<int>(m_oLayerNameSet.size());
3586 468 : layerStruct.iPage = iPage;
3587 468 : m_oLayerNameSet.emplace_back(std::move(layerStruct));
3588 468 : }
3589 :
3590 : /************************************************************************/
3591 : /* CreateLayerList() */
3592 : /************************************************************************/
3593 :
3594 249 : void PDFDataset::CreateLayerList()
3595 : {
3596 : // Sort layers by prioritizing page number and then insertion index
3597 249 : std::sort(m_oLayerNameSet.begin(), m_oLayerNameSet.end(),
3598 1938 : [](const LayerStruct &a, const LayerStruct &b)
3599 : {
3600 1938 : if (a.iPage < b.iPage)
3601 78 : return true;
3602 1860 : if (a.iPage > b.iPage)
3603 0 : return false;
3604 1860 : return a.nInsertIdx < b.nInsertIdx;
3605 : });
3606 :
3607 249 : if (m_oLayerNameSet.size() >= 100)
3608 : {
3609 199 : for (const auto &oLayerStruct : m_oLayerNameSet)
3610 : {
3611 : m_aosLayerNames.AddNameValue(
3612 : CPLSPrintf("LAYER_%03d_NAME", m_aosLayerNames.size()),
3613 198 : oLayerStruct.osName.c_str());
3614 : }
3615 : }
3616 : else
3617 : {
3618 518 : for (const auto &oLayerStruct : m_oLayerNameSet)
3619 : {
3620 : m_aosLayerNames.AddNameValue(
3621 : CPLSPrintf("LAYER_%02d_NAME", m_aosLayerNames.size()),
3622 270 : oLayerStruct.osName.c_str());
3623 : }
3624 : }
3625 249 : }
3626 :
3627 : /************************************************************************/
3628 : /* BuildPostfixedLayerNameAndAddLayer() */
3629 : /************************************************************************/
3630 :
3631 : /** Append a suffix with the page number(s) to the provided layer name, if
3632 : * it makes sense (that is if it is a multiple page PDF and we haven't selected
3633 : * a specific name). And also call AddLayer() on it if successful.
3634 : * If may return an empty string if the layer isn't used by the page of interest
3635 : */
3636 612 : std::string PDFDataset::BuildPostfixedLayerNameAndAddLayer(
3637 : const std::string &osName, const std::pair<int, int> &oOCGRef,
3638 : int iPageOfInterest, int nPageCount)
3639 : {
3640 1224 : std::string osPostfixedName = osName;
3641 612 : int iLayerPage = 0;
3642 612 : if (nPageCount > 1 && !m_oMapOCGNumGenToPages.empty())
3643 : {
3644 216 : const auto oIterToPages = m_oMapOCGNumGenToPages.find(oOCGRef);
3645 216 : if (oIterToPages != m_oMapOCGNumGenToPages.end())
3646 : {
3647 216 : const auto &anPages = oIterToPages->second;
3648 216 : if (iPageOfInterest > 0)
3649 : {
3650 192 : if (std::find(anPages.begin(), anPages.end(),
3651 192 : iPageOfInterest) == anPages.end())
3652 : {
3653 144 : return std::string();
3654 : }
3655 : }
3656 24 : else if (anPages.size() == 1)
3657 : {
3658 24 : iLayerPage = anPages.front();
3659 24 : osPostfixedName += CPLSPrintf(" (page %d)", anPages.front());
3660 : }
3661 : else
3662 : {
3663 0 : osPostfixedName += " (pages ";
3664 0 : for (size_t j = 0; j < anPages.size(); ++j)
3665 : {
3666 0 : if (j > 0)
3667 0 : osPostfixedName += ", ";
3668 0 : osPostfixedName += CPLSPrintf("%d", anPages[j]);
3669 : }
3670 0 : osPostfixedName += ')';
3671 : }
3672 : }
3673 : }
3674 :
3675 468 : AddLayer(osPostfixedName, iLayerPage);
3676 :
3677 468 : return osPostfixedName;
3678 : }
3679 :
3680 : #endif // defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
3681 :
3682 : #ifdef HAVE_POPPLER
3683 :
3684 : /************************************************************************/
3685 : /* ExploreLayersPoppler() */
3686 : /************************************************************************/
3687 :
3688 135 : void PDFDataset::ExploreLayersPoppler(GDALPDFArray *poArray,
3689 : int iPageOfInterest, int nPageCount,
3690 : CPLString osTopLayer, int nRecLevel,
3691 : int &nVisited, bool &bStop)
3692 : {
3693 135 : if (nRecLevel == 16 || nVisited == 1000)
3694 : {
3695 0 : CPLError(
3696 : CE_Failure, CPLE_AppDefined,
3697 : "ExploreLayersPoppler(): too deep exploration or too many items");
3698 0 : bStop = true;
3699 0 : return;
3700 : }
3701 135 : if (bStop)
3702 0 : return;
3703 :
3704 135 : int nLength = poArray->GetLength();
3705 135 : CPLString osCurLayer;
3706 414 : for (int i = 0; i < nLength; i++)
3707 : {
3708 279 : nVisited++;
3709 279 : GDALPDFObject *poObj = poArray->Get(i);
3710 279 : if (poObj == nullptr)
3711 0 : continue;
3712 279 : if (i == 0 && poObj->GetType() == PDFObjectType_String)
3713 : {
3714 : std::string osName =
3715 0 : PDFSanitizeLayerName(poObj->GetString().c_str());
3716 0 : if (!osTopLayer.empty())
3717 : {
3718 0 : osTopLayer += '.';
3719 0 : osTopLayer += osName;
3720 : }
3721 : else
3722 0 : osTopLayer = std::move(osName);
3723 0 : AddLayer(osTopLayer, 0);
3724 0 : m_oLayerOCGListPoppler.push_back(std::pair(osTopLayer, nullptr));
3725 : }
3726 279 : else if (poObj->GetType() == PDFObjectType_Array)
3727 : {
3728 97 : ExploreLayersPoppler(poObj->GetArray(), iPageOfInterest, nPageCount,
3729 : osCurLayer, nRecLevel + 1, nVisited, bStop);
3730 97 : if (bStop)
3731 0 : return;
3732 97 : osCurLayer = "";
3733 : }
3734 182 : else if (poObj->GetType() == PDFObjectType_Dictionary)
3735 : {
3736 182 : GDALPDFDictionary *poDict = poObj->GetDictionary();
3737 182 : GDALPDFObject *poName = poDict->Get("Name");
3738 182 : if (poName != nullptr && poName->GetType() == PDFObjectType_String)
3739 : {
3740 : std::string osName =
3741 182 : PDFSanitizeLayerName(poName->GetString().c_str());
3742 : /* coverity[copy_paste_error] */
3743 182 : if (!osTopLayer.empty())
3744 : {
3745 103 : osCurLayer = osTopLayer;
3746 103 : osCurLayer += '.';
3747 103 : osCurLayer += osName;
3748 : }
3749 : else
3750 79 : osCurLayer = std::move(osName);
3751 : // CPLDebug("PDF", "Layer %s", osCurLayer.c_str());
3752 :
3753 : #if POPPLER_MAJOR_VERSION > 25 || \
3754 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3755 : const
3756 : #endif
3757 : OCGs *optContentConfig =
3758 182 : m_poDocPoppler->getOptContentConfig();
3759 : struct Ref r;
3760 182 : r.num = poObj->GetRefNum().toInt();
3761 182 : r.gen = poObj->GetRefGen();
3762 182 : OptionalContentGroup *ocg = optContentConfig->findOcgByRef(r);
3763 182 : if (ocg)
3764 : {
3765 182 : const auto oRefPair = std::pair(poObj->GetRefNum().toInt(),
3766 364 : poObj->GetRefGen());
3767 : const std::string osPostfixedName =
3768 : BuildPostfixedLayerNameAndAddLayer(
3769 182 : osCurLayer, oRefPair, iPageOfInterest, nPageCount);
3770 182 : if (osPostfixedName.empty())
3771 72 : continue;
3772 :
3773 110 : m_oLayerOCGListPoppler.push_back(
3774 220 : std::make_pair(osPostfixedName, ocg));
3775 110 : m_aoLayerWithRef.emplace_back(osPostfixedName.c_str(),
3776 220 : poObj->GetRefNum(), r.gen);
3777 : }
3778 : }
3779 : }
3780 : }
3781 : }
3782 :
3783 : /************************************************************************/
3784 : /* FindLayersPoppler() */
3785 : /************************************************************************/
3786 :
3787 167 : void PDFDataset::FindLayersPoppler(int iPageOfInterest)
3788 : {
3789 167 : int nPageCount = 0;
3790 167 : const auto poPages = GetPagesKids();
3791 167 : if (poPages)
3792 167 : nPageCount = poPages->GetLength();
3793 :
3794 : #if POPPLER_MAJOR_VERSION > 25 || \
3795 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3796 : const
3797 : #endif
3798 167 : OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig();
3799 167 : if (optContentConfig == nullptr || !optContentConfig->isOk())
3800 129 : return;
3801 :
3802 : #if POPPLER_MAJOR_VERSION > 25 || \
3803 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3804 : const
3805 : #endif
3806 38 : Array *array = optContentConfig->getOrderArray();
3807 38 : if (array)
3808 : {
3809 38 : GDALPDFArray *poArray = GDALPDFCreateArray(array);
3810 38 : int nVisited = 0;
3811 38 : bool bStop = false;
3812 38 : ExploreLayersPoppler(poArray, iPageOfInterest, nPageCount, CPLString(),
3813 : 0, nVisited, bStop);
3814 38 : delete poArray;
3815 : }
3816 : else
3817 : {
3818 0 : for (const auto &refOCGPair : optContentConfig->getOCGs())
3819 : {
3820 0 : auto ocg = refOCGPair.second.get();
3821 0 : if (ocg != nullptr && ocg->getName() != nullptr)
3822 : {
3823 : const char *pszLayerName =
3824 0 : reinterpret_cast<const char *>(ocg->getName()->c_str());
3825 0 : AddLayer(pszLayerName, 0);
3826 0 : m_oLayerOCGListPoppler.push_back(
3827 0 : std::make_pair(CPLString(pszLayerName), ocg));
3828 : }
3829 : }
3830 : }
3831 :
3832 38 : CreateLayerList();
3833 38 : m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS");
3834 : }
3835 :
3836 : /************************************************************************/
3837 : /* TurnLayersOnOffPoppler() */
3838 : /************************************************************************/
3839 :
3840 167 : void PDFDataset::TurnLayersOnOffPoppler()
3841 : {
3842 : #if POPPLER_MAJOR_VERSION > 25 || \
3843 : (POPPLER_MAJOR_VERSION == 25 && POPPLER_MINOR_VERSION >= 2)
3844 : const
3845 : #endif
3846 167 : OCGs *optContentConfig = m_poDocPoppler->getOptContentConfig();
3847 167 : if (optContentConfig == nullptr || !optContentConfig->isOk())
3848 129 : return;
3849 :
3850 : // Which layers to turn ON ?
3851 38 : const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr);
3852 38 : if (pszLayers)
3853 : {
3854 : int i;
3855 2 : int bAll = EQUAL(pszLayers, "ALL");
3856 12 : for (const auto &refOCGPair : optContentConfig->getOCGs())
3857 : {
3858 10 : auto ocg = refOCGPair.second.get();
3859 10 : ocg->setState((bAll) ? OptionalContentGroup::On
3860 : : OptionalContentGroup::Off);
3861 : }
3862 :
3863 2 : char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
3864 4 : for (i = 0; !bAll && papszLayers[i] != nullptr; i++)
3865 : {
3866 2 : bool isFound = false;
3867 12 : for (auto oIter2 = m_oLayerOCGListPoppler.begin();
3868 22 : oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2)
3869 : {
3870 10 : if (oIter2->first != papszLayers[i])
3871 8 : continue;
3872 :
3873 2 : isFound = true;
3874 2 : auto oIter = oIter2;
3875 2 : if (oIter->second)
3876 : {
3877 : // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
3878 2 : oIter->second->setState(OptionalContentGroup::On);
3879 : }
3880 :
3881 : // Turn child layers on, unless there's one of them explicitly
3882 : // listed in the list.
3883 2 : size_t nLen = strlen(papszLayers[i]);
3884 2 : int bFoundChildLayer = FALSE;
3885 2 : oIter = m_oLayerOCGListPoppler.begin();
3886 10 : for (;
3887 12 : oIter != m_oLayerOCGListPoppler.end() && !bFoundChildLayer;
3888 10 : ++oIter)
3889 : {
3890 10 : if (oIter->first.size() > nLen &&
3891 5 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) ==
3892 15 : 0 &&
3893 2 : oIter->first[nLen] == '.')
3894 : {
3895 4 : for (int j = 0; papszLayers[j] != nullptr; j++)
3896 : {
3897 2 : if (strcmp(papszLayers[j], oIter->first.c_str()) ==
3898 : 0)
3899 : {
3900 0 : bFoundChildLayer = TRUE;
3901 0 : break;
3902 : }
3903 : }
3904 : }
3905 : }
3906 :
3907 2 : if (!bFoundChildLayer)
3908 : {
3909 2 : oIter = m_oLayerOCGListPoppler.begin();
3910 12 : for (; oIter != m_oLayerOCGListPoppler.end() &&
3911 : !bFoundChildLayer;
3912 10 : ++oIter)
3913 : {
3914 10 : if (oIter->first.size() > nLen &&
3915 5 : strncmp(oIter->first.c_str(), papszLayers[i],
3916 15 : nLen) == 0 &&
3917 2 : oIter->first[nLen] == '.')
3918 : {
3919 2 : if (oIter->second)
3920 : {
3921 : // CPLDebug("PDF", "Turn '%s' on too",
3922 : // oIter->first.c_str());
3923 2 : oIter->second->setState(
3924 : OptionalContentGroup::On);
3925 : }
3926 : }
3927 : }
3928 : }
3929 :
3930 : // Turn parent layers on too
3931 6 : std::string layer(papszLayers[i]);
3932 : std::string::size_type j;
3933 3 : while ((j = layer.find_last_of('.')) != std::string::npos)
3934 : {
3935 1 : layer.resize(j);
3936 1 : oIter = m_oLayerOCGListPoppler.begin();
3937 6 : for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter)
3938 : {
3939 5 : if (oIter->first == layer && oIter->second)
3940 : {
3941 : // CPLDebug("PDF", "Turn '%s' on too",
3942 : // layer.c_str());
3943 1 : oIter->second->setState(OptionalContentGroup::On);
3944 : }
3945 : }
3946 : }
3947 : }
3948 2 : if (!isFound)
3949 : {
3950 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
3951 0 : papszLayers[i]);
3952 : }
3953 : }
3954 2 : CSLDestroy(papszLayers);
3955 :
3956 2 : m_bUseOCG = true;
3957 : }
3958 :
3959 : // Which layers to turn OFF ?
3960 : const char *pszLayersOFF =
3961 38 : GetOption(papszOpenOptions, "LAYERS_OFF", nullptr);
3962 38 : if (pszLayersOFF)
3963 : {
3964 5 : char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
3965 10 : for (int i = 0; papszLayersOFF[i] != nullptr; i++)
3966 : {
3967 5 : bool isFound = false;
3968 22 : for (auto oIter2 = m_oLayerOCGListPoppler.begin();
3969 39 : oIter2 != m_oLayerOCGListPoppler.end(); ++oIter2)
3970 : {
3971 17 : if (oIter2->first != papszLayersOFF[i])
3972 12 : continue;
3973 :
3974 5 : isFound = true;
3975 5 : auto oIter = oIter2;
3976 5 : if (oIter->second)
3977 : {
3978 : // CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]);
3979 5 : oIter->second->setState(OptionalContentGroup::Off);
3980 : }
3981 :
3982 : // Turn child layers off too
3983 5 : size_t nLen = strlen(papszLayersOFF[i]);
3984 5 : oIter = m_oLayerOCGListPoppler.begin();
3985 22 : for (; oIter != m_oLayerOCGListPoppler.end(); ++oIter)
3986 : {
3987 17 : if (oIter->first.size() > nLen &&
3988 3 : strncmp(oIter->first.c_str(), papszLayersOFF[i],
3989 20 : nLen) == 0 &&
3990 1 : oIter->first[nLen] == '.')
3991 : {
3992 1 : if (oIter->second)
3993 : {
3994 : // CPLDebug("PDF", "Turn '%s' off too",
3995 : // oIter->first.c_str());
3996 1 : oIter->second->setState(OptionalContentGroup::Off);
3997 : }
3998 : }
3999 : }
4000 : }
4001 5 : if (!isFound)
4002 : {
4003 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
4004 0 : papszLayersOFF[i]);
4005 : }
4006 : }
4007 5 : CSLDestroy(papszLayersOFF);
4008 :
4009 5 : m_bUseOCG = true;
4010 : }
4011 : }
4012 :
4013 : #endif
4014 :
4015 : #ifdef HAVE_PDFIUM
4016 :
4017 : /************************************************************************/
4018 : /* ExploreLayersPdfium() */
4019 : /************************************************************************/
4020 :
4021 209 : void PDFDataset::ExploreLayersPdfium(GDALPDFArray *poArray, int iPageOfInterest,
4022 : int nPageCount, int nRecLevel,
4023 : CPLString osTopLayer)
4024 : {
4025 209 : if (nRecLevel == 16)
4026 0 : return;
4027 :
4028 209 : const int nLength = poArray->GetLength();
4029 418 : std::string osCurLayer;
4030 787 : for (int i = 0; i < nLength; i++)
4031 : {
4032 578 : GDALPDFObject *poObj = poArray->Get(i);
4033 578 : if (poObj == nullptr)
4034 0 : continue;
4035 578 : if (i == 0 && poObj->GetType() == PDFObjectType_String)
4036 : {
4037 : const std::string osName =
4038 0 : PDFSanitizeLayerName(poObj->GetString().c_str());
4039 0 : if (!osTopLayer.empty())
4040 0 : osTopLayer = std::string(osTopLayer).append(".").append(osName);
4041 : else
4042 0 : osTopLayer = osName;
4043 0 : AddLayer(osTopLayer, 0);
4044 0 : m_oMapLayerNameToOCGNumGenPdfium[osTopLayer] = std::pair(-1, -1);
4045 : }
4046 578 : else if (poObj->GetType() == PDFObjectType_Array)
4047 : {
4048 148 : ExploreLayersPdfium(poObj->GetArray(), iPageOfInterest, nPageCount,
4049 : nRecLevel + 1, osCurLayer);
4050 148 : osCurLayer.clear();
4051 : }
4052 430 : else if (poObj->GetType() == PDFObjectType_Dictionary)
4053 : {
4054 430 : GDALPDFDictionary *poDict = poObj->GetDictionary();
4055 430 : GDALPDFObject *poName = poDict->Get("Name");
4056 430 : if (poName != nullptr && poName->GetType() == PDFObjectType_String)
4057 : {
4058 : const std::string osName =
4059 430 : PDFSanitizeLayerName(poName->GetString().c_str());
4060 : // coverity[copy_paste_error]
4061 430 : if (!osTopLayer.empty())
4062 : {
4063 : osCurLayer =
4064 304 : std::string(osTopLayer).append(".").append(osName);
4065 : }
4066 : else
4067 126 : osCurLayer = osName;
4068 : // CPLDebug("PDF", "Layer %s", osCurLayer.c_str());
4069 :
4070 : const auto oRefPair =
4071 430 : std::pair(poObj->GetRefNum().toInt(), poObj->GetRefGen());
4072 : const std::string osPostfixedName =
4073 : BuildPostfixedLayerNameAndAddLayer(
4074 430 : osCurLayer, oRefPair, iPageOfInterest, nPageCount);
4075 430 : if (osPostfixedName.empty())
4076 72 : continue;
4077 :
4078 : m_aoLayerWithRef.emplace_back(
4079 358 : osPostfixedName, poObj->GetRefNum(), poObj->GetRefGen());
4080 358 : m_oMapLayerNameToOCGNumGenPdfium[osPostfixedName] = oRefPair;
4081 : }
4082 : }
4083 : }
4084 : }
4085 :
4086 : /************************************************************************/
4087 : /* FindLayersPdfium() */
4088 : /************************************************************************/
4089 :
4090 211 : void PDFDataset::FindLayersPdfium(int iPageOfInterest)
4091 : {
4092 211 : int nPageCount = 0;
4093 211 : const auto poPages = GetPagesKids();
4094 211 : if (poPages)
4095 211 : nPageCount = poPages->GetLength();
4096 :
4097 211 : GDALPDFObject *poCatalog = GetCatalog();
4098 422 : if (poCatalog == nullptr ||
4099 211 : poCatalog->GetType() != PDFObjectType_Dictionary)
4100 0 : return;
4101 211 : GDALPDFObject *poOrder = poCatalog->LookupObject("OCProperties.D.Order");
4102 211 : if (poOrder != nullptr && poOrder->GetType() == PDFObjectType_Array)
4103 : {
4104 61 : ExploreLayersPdfium(poOrder->GetArray(), iPageOfInterest, nPageCount,
4105 : 0);
4106 : }
4107 : #if 0
4108 : else
4109 : {
4110 : GDALPDFObject* poOCGs = poD->GetDictionary()->Get("OCGs");
4111 : if( poOCGs != nullptr && poOCGs->GetType() == PDFObjectType_Array )
4112 : {
4113 : GDALPDFArray* poArray = poOCGs->GetArray();
4114 : int nLength = poArray->GetLength();
4115 : for(int i=0;i<nLength;i++)
4116 : {
4117 : GDALPDFObject* poObj = poArray->Get(i);
4118 : if( poObj != nullptr )
4119 : {
4120 : // TODO ?
4121 : }
4122 : }
4123 : }
4124 : }
4125 : #endif
4126 :
4127 211 : CreateLayerList();
4128 211 : m_oMDMD_PDF.SetMetadata(m_aosLayerNames.List(), "LAYERS");
4129 : }
4130 :
4131 : /************************************************************************/
4132 : /* TurnLayersOnOffPdfium() */
4133 : /************************************************************************/
4134 :
4135 211 : void PDFDataset::TurnLayersOnOffPdfium()
4136 : {
4137 211 : GDALPDFObject *poCatalog = GetCatalog();
4138 422 : if (poCatalog == nullptr ||
4139 211 : poCatalog->GetType() != PDFObjectType_Dictionary)
4140 0 : return;
4141 211 : GDALPDFObject *poOCGs = poCatalog->LookupObject("OCProperties.OCGs");
4142 211 : if (poOCGs == nullptr || poOCGs->GetType() != PDFObjectType_Array)
4143 150 : return;
4144 :
4145 : // Which layers to turn ON ?
4146 61 : const char *pszLayers = GetOption(papszOpenOptions, "LAYERS", nullptr);
4147 61 : if (pszLayers)
4148 : {
4149 : int i;
4150 2 : int bAll = EQUAL(pszLayers, "ALL");
4151 :
4152 2 : GDALPDFArray *poOCGsArray = poOCGs->GetArray();
4153 2 : int nLength = poOCGsArray->GetLength();
4154 12 : for (i = 0; i < nLength; i++)
4155 : {
4156 10 : GDALPDFObject *poOCG = poOCGsArray->Get(i);
4157 0 : m_oMapOCGNumGenToVisibilityStatePdfium[std::pair(
4158 10 : poOCG->GetRefNum().toInt(), poOCG->GetRefGen())] =
4159 10 : (bAll) ? VISIBILITY_ON : VISIBILITY_OFF;
4160 : }
4161 :
4162 2 : char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
4163 4 : for (i = 0; !bAll && papszLayers[i] != nullptr; i++)
4164 : {
4165 2 : auto oIter = m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]);
4166 2 : if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end())
4167 : {
4168 2 : if (oIter->second.first >= 0)
4169 : {
4170 : // CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
4171 2 : m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] =
4172 : VISIBILITY_ON;
4173 : }
4174 :
4175 : // Turn child layers on, unless there's one of them explicitly
4176 : // listed in the list.
4177 2 : size_t nLen = strlen(papszLayers[i]);
4178 2 : int bFoundChildLayer = FALSE;
4179 2 : oIter = m_oMapLayerNameToOCGNumGenPdfium.begin();
4180 12 : for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() &&
4181 : !bFoundChildLayer;
4182 10 : oIter++)
4183 : {
4184 10 : if (oIter->first.size() > nLen &&
4185 5 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) ==
4186 15 : 0 &&
4187 2 : oIter->first[nLen] == '.')
4188 : {
4189 4 : for (int j = 0; papszLayers[j] != nullptr; j++)
4190 : {
4191 2 : if (strcmp(papszLayers[j], oIter->first.c_str()) ==
4192 : 0)
4193 0 : bFoundChildLayer = TRUE;
4194 : }
4195 : }
4196 : }
4197 :
4198 2 : if (!bFoundChildLayer)
4199 : {
4200 2 : oIter = m_oMapLayerNameToOCGNumGenPdfium.begin();
4201 12 : for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end() &&
4202 : !bFoundChildLayer;
4203 10 : oIter++)
4204 : {
4205 10 : if (oIter->first.size() > nLen &&
4206 5 : strncmp(oIter->first.c_str(), papszLayers[i],
4207 15 : nLen) == 0 &&
4208 2 : oIter->first[nLen] == '.')
4209 : {
4210 2 : if (oIter->second.first >= 0)
4211 : {
4212 : // CPLDebug("PDF", "Turn '%s' on too",
4213 : // oIter->first.c_str());
4214 : m_oMapOCGNumGenToVisibilityStatePdfium
4215 2 : [oIter->second] = VISIBILITY_ON;
4216 : }
4217 : }
4218 : }
4219 : }
4220 :
4221 : // Turn parent layers on too
4222 2 : char *pszLastDot = nullptr;
4223 3 : while ((pszLastDot = strrchr(papszLayers[i], '.')) != nullptr)
4224 : {
4225 1 : *pszLastDot = '\0';
4226 : oIter =
4227 1 : m_oMapLayerNameToOCGNumGenPdfium.find(papszLayers[i]);
4228 1 : if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end())
4229 : {
4230 1 : if (oIter->second.first >= 0)
4231 : {
4232 : // CPLDebug("PDF", "Turn '%s' on too",
4233 : // papszLayers[i]);
4234 : m_oMapOCGNumGenToVisibilityStatePdfium
4235 1 : [oIter->second] = VISIBILITY_ON;
4236 : }
4237 : }
4238 : }
4239 : }
4240 : else
4241 : {
4242 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
4243 0 : papszLayers[i]);
4244 : }
4245 : }
4246 2 : CSLDestroy(papszLayers);
4247 :
4248 2 : m_bUseOCG = true;
4249 : }
4250 :
4251 : // Which layers to turn OFF ?
4252 : const char *pszLayersOFF =
4253 61 : GetOption(papszOpenOptions, "LAYERS_OFF", nullptr);
4254 61 : if (pszLayersOFF)
4255 : {
4256 5 : char **papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
4257 10 : for (int i = 0; papszLayersOFF[i] != nullptr; i++)
4258 : {
4259 : auto oIter =
4260 5 : m_oMapLayerNameToOCGNumGenPdfium.find(papszLayersOFF[i]);
4261 5 : if (oIter != m_oMapLayerNameToOCGNumGenPdfium.end())
4262 : {
4263 5 : if (oIter->second.first >= 0)
4264 : {
4265 : // CPLDebug("PDF", "Turn '%s' (%d,%d) off",
4266 : // papszLayersOFF[i], oIter->second.first,
4267 : // oIter->second.second);
4268 5 : m_oMapOCGNumGenToVisibilityStatePdfium[oIter->second] =
4269 : VISIBILITY_OFF;
4270 : }
4271 :
4272 : // Turn child layers off too
4273 5 : size_t nLen = strlen(papszLayersOFF[i]);
4274 5 : oIter = m_oMapLayerNameToOCGNumGenPdfium.begin();
4275 22 : for (; oIter != m_oMapLayerNameToOCGNumGenPdfium.end(); oIter++)
4276 : {
4277 17 : if (oIter->first.size() > nLen &&
4278 3 : strncmp(oIter->first.c_str(), papszLayersOFF[i],
4279 20 : nLen) == 0 &&
4280 1 : oIter->first[nLen] == '.')
4281 : {
4282 1 : if (oIter->second.first >= 0)
4283 : {
4284 : // CPLDebug("PDF", "Turn '%s' off too",
4285 : // oIter->first.c_str());
4286 : m_oMapOCGNumGenToVisibilityStatePdfium
4287 1 : [oIter->second] = VISIBILITY_OFF;
4288 : }
4289 : }
4290 : }
4291 : }
4292 : else
4293 : {
4294 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
4295 0 : papszLayersOFF[i]);
4296 : }
4297 : }
4298 5 : CSLDestroy(papszLayersOFF);
4299 :
4300 5 : m_bUseOCG = true;
4301 : }
4302 : }
4303 :
4304 : /************************************************************************/
4305 : /* GetVisibilityStateForOGCPdfium() */
4306 : /************************************************************************/
4307 :
4308 12996 : PDFDataset::VisibilityState PDFDataset::GetVisibilityStateForOGCPdfium(int nNum,
4309 : int nGen)
4310 : {
4311 : auto oIter =
4312 12996 : m_oMapOCGNumGenToVisibilityStatePdfium.find(std::pair(nNum, nGen));
4313 12996 : if (oIter == m_oMapOCGNumGenToVisibilityStatePdfium.end())
4314 8703 : return VISIBILITY_DEFAULT;
4315 4293 : return oIter->second;
4316 : }
4317 :
4318 : #endif /* HAVE_PDFIUM */
4319 :
4320 : /************************************************************************/
4321 : /* GetPagesKids() */
4322 : /************************************************************************/
4323 :
4324 756 : GDALPDFArray *PDFDataset::GetPagesKids()
4325 : {
4326 756 : const auto poCatalog = GetCatalog();
4327 756 : if (!poCatalog || poCatalog->GetType() != PDFObjectType_Dictionary)
4328 : {
4329 0 : return nullptr;
4330 : }
4331 756 : const auto poKids = poCatalog->LookupObject("Pages.Kids");
4332 756 : if (!poKids || poKids->GetType() != PDFObjectType_Array)
4333 : {
4334 0 : return nullptr;
4335 : }
4336 756 : return poKids->GetArray();
4337 : }
4338 :
4339 : /************************************************************************/
4340 : /* MapOCGsToPages() */
4341 : /************************************************************************/
4342 :
4343 378 : void PDFDataset::MapOCGsToPages()
4344 : {
4345 378 : const auto poKidsArray = GetPagesKids();
4346 378 : if (!poKidsArray)
4347 : {
4348 0 : return;
4349 : }
4350 378 : const int nKidsArrayLength = poKidsArray->GetLength();
4351 820 : for (int iPage = 0; iPage < nKidsArrayLength; ++iPage)
4352 : {
4353 442 : const auto poPage = poKidsArray->Get(iPage);
4354 442 : if (poPage && poPage->GetType() == PDFObjectType_Dictionary)
4355 : {
4356 442 : const auto poXObject = poPage->LookupObject("Resources.XObject");
4357 442 : if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary)
4358 : {
4359 959 : for (const auto &oNameObjectPair :
4360 2342 : poXObject->GetDictionary()->GetValues())
4361 : {
4362 : const auto poProperties =
4363 959 : oNameObjectPair.second->LookupObject(
4364 : "Resources.Properties");
4365 1032 : if (poProperties &&
4366 73 : poProperties->GetType() == PDFObjectType_Dictionary)
4367 : {
4368 : const auto &oMap =
4369 73 : poProperties->GetDictionary()->GetValues();
4370 426 : for (const auto &[osKey, poObj] : oMap)
4371 : {
4372 706 : if (poObj->GetRefNum().toBool() &&
4373 353 : poObj->GetType() == PDFObjectType_Dictionary)
4374 : {
4375 : GDALPDFObject *poType =
4376 353 : poObj->GetDictionary()->Get("Type");
4377 : GDALPDFObject *poName =
4378 353 : poObj->GetDictionary()->Get("Name");
4379 706 : if (poType &&
4380 706 : poType->GetType() == PDFObjectType_Name &&
4381 1412 : poType->GetName() == "OCG" && poName &&
4382 353 : poName->GetType() == PDFObjectType_String)
4383 : {
4384 : m_oMapOCGNumGenToPages
4385 353 : [std::pair(poObj->GetRefNum().toInt(),
4386 706 : poObj->GetRefGen())]
4387 353 : .push_back(iPage + 1);
4388 : }
4389 : }
4390 : }
4391 : }
4392 : }
4393 : }
4394 : }
4395 : }
4396 : }
4397 :
4398 : /************************************************************************/
4399 : /* FindLayerOCG() */
4400 : /************************************************************************/
4401 :
4402 354 : CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary *poPageDict,
4403 : const char *pszLayerName)
4404 : {
4405 : GDALPDFObject *poProperties =
4406 354 : poPageDict->LookupObject("Resources.Properties");
4407 420 : if (poProperties != nullptr &&
4408 66 : poProperties->GetType() == PDFObjectType_Dictionary)
4409 : {
4410 66 : const auto &oMap = poProperties->GetDictionary()->GetValues();
4411 187 : for (const auto &[osKey, poObj] : oMap)
4412 : {
4413 241 : if (poObj->GetRefNum().toBool() &&
4414 120 : poObj->GetType() == PDFObjectType_Dictionary)
4415 : {
4416 120 : GDALPDFObject *poType = poObj->GetDictionary()->Get("Type");
4417 120 : GDALPDFObject *poName = poObj->GetDictionary()->Get("Name");
4418 240 : if (poType != nullptr &&
4419 240 : poType->GetType() == PDFObjectType_Name &&
4420 480 : poType->GetName() == "OCG" && poName != nullptr &&
4421 120 : poName->GetType() == PDFObjectType_String)
4422 : {
4423 120 : if (poName->GetString() == pszLayerName)
4424 0 : return osKey;
4425 : }
4426 : }
4427 : }
4428 : }
4429 354 : return "";
4430 : }
4431 :
4432 : /************************************************************************/
4433 : /* FindLayersGeneric() */
4434 : /************************************************************************/
4435 :
4436 0 : void PDFDataset::FindLayersGeneric(GDALPDFDictionary *poPageDict)
4437 : {
4438 : GDALPDFObject *poProperties =
4439 0 : poPageDict->LookupObject("Resources.Properties");
4440 0 : if (poProperties != nullptr &&
4441 0 : poProperties->GetType() == PDFObjectType_Dictionary)
4442 : {
4443 0 : const auto &oMap = poProperties->GetDictionary()->GetValues();
4444 0 : for (const auto &[osKey, poObj] : oMap)
4445 : {
4446 0 : if (poObj->GetRefNum().toBool() &&
4447 0 : poObj->GetType() == PDFObjectType_Dictionary)
4448 : {
4449 0 : GDALPDFObject *poType = poObj->GetDictionary()->Get("Type");
4450 0 : GDALPDFObject *poName = poObj->GetDictionary()->Get("Name");
4451 0 : if (poType != nullptr &&
4452 0 : poType->GetType() == PDFObjectType_Name &&
4453 0 : poType->GetName() == "OCG" && poName != nullptr &&
4454 0 : poName->GetType() == PDFObjectType_String)
4455 : {
4456 : m_aoLayerWithRef.emplace_back(
4457 0 : PDFSanitizeLayerName(poName->GetString().c_str())
4458 0 : .c_str(),
4459 0 : poObj->GetRefNum(), poObj->GetRefGen());
4460 : }
4461 : }
4462 : }
4463 : }
4464 0 : }
4465 :
4466 : /************************************************************************/
4467 : /* Open() */
4468 : /************************************************************************/
4469 :
4470 400 : PDFDataset *PDFDataset::Open(GDALOpenInfo *poOpenInfo)
4471 :
4472 : {
4473 400 : if (!PDFDatasetIdentify(poOpenInfo))
4474 2 : return nullptr;
4475 :
4476 : const char *pszUserPwd =
4477 398 : GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", nullptr);
4478 :
4479 398 : const bool bOpenSubdataset = STARTS_WITH(poOpenInfo->pszFilename, "PDF:");
4480 398 : const bool bOpenSubdatasetImage =
4481 398 : STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:");
4482 398 : int iPage = -1;
4483 398 : int nImageNum = -1;
4484 796 : std::string osSubdatasetName;
4485 398 : const char *pszFilename = poOpenInfo->pszFilename;
4486 :
4487 398 : if (bOpenSubdataset)
4488 : {
4489 30 : iPage = atoi(pszFilename + 4);
4490 30 : if (iPage <= 0)
4491 2 : return nullptr;
4492 28 : pszFilename = strchr(pszFilename + 4, ':');
4493 28 : if (pszFilename == nullptr)
4494 0 : return nullptr;
4495 28 : pszFilename++;
4496 28 : osSubdatasetName = CPLSPrintf("Page %d", iPage);
4497 : }
4498 368 : else if (bOpenSubdatasetImage)
4499 : {
4500 0 : iPage = atoi(pszFilename + 10);
4501 0 : if (iPage <= 0)
4502 0 : return nullptr;
4503 0 : const char *pszNext = strchr(pszFilename + 10, ':');
4504 0 : if (pszNext == nullptr)
4505 0 : return nullptr;
4506 0 : nImageNum = atoi(pszNext + 1);
4507 0 : if (nImageNum <= 0)
4508 0 : return nullptr;
4509 0 : pszFilename = strchr(pszNext + 1, ':');
4510 0 : if (pszFilename == nullptr)
4511 0 : return nullptr;
4512 0 : pszFilename++;
4513 0 : osSubdatasetName = CPLSPrintf("Image %d", nImageNum);
4514 : }
4515 : else
4516 368 : iPage = 1;
4517 :
4518 396 : std::bitset<PDFLIB_COUNT> bHasLib;
4519 396 : bHasLib.reset();
4520 : // Each library set their flag
4521 : #if defined(HAVE_POPPLER)
4522 396 : bHasLib.set(PDFLIB_POPPLER);
4523 : #endif // HAVE_POPPLER
4524 : #if defined(HAVE_PODOFO)
4525 : bHasLib.set(PDFLIB_PODOFO);
4526 : #endif // HAVE_PODOFO
4527 : #if defined(HAVE_PDFIUM)
4528 396 : bHasLib.set(PDFLIB_PDFIUM);
4529 : #endif // HAVE_PDFIUM
4530 :
4531 396 : std::bitset<PDFLIB_COUNT> bUseLib;
4532 :
4533 : // More than one library available
4534 : // Detect which one
4535 396 : if (bHasLib.count() != 1)
4536 : {
4537 396 : const char *pszDefaultLib = bHasLib.test(PDFLIB_PDFIUM) ? "PDFIUM"
4538 0 : : bHasLib.test(PDFLIB_POPPLER) ? "POPPLER"
4539 396 : : "PODOFO";
4540 : const char *pszPDFLib =
4541 396 : GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", pszDefaultLib);
4542 : while (true)
4543 : {
4544 396 : if (EQUAL(pszPDFLib, "POPPLER"))
4545 171 : bUseLib.set(PDFLIB_POPPLER);
4546 225 : else if (EQUAL(pszPDFLib, "PODOFO"))
4547 0 : bUseLib.set(PDFLIB_PODOFO);
4548 225 : else if (EQUAL(pszPDFLib, "PDFIUM"))
4549 225 : bUseLib.set(PDFLIB_PDFIUM);
4550 :
4551 396 : if (bUseLib.count() != 1 || (bHasLib & bUseLib) == 0)
4552 : {
4553 0 : CPLDebug("PDF",
4554 : "Invalid value for GDAL_PDF_LIB config option: %s. "
4555 : "Fallback to %s",
4556 : pszPDFLib, pszDefaultLib);
4557 0 : pszPDFLib = pszDefaultLib;
4558 0 : bUseLib.reset();
4559 : }
4560 : else
4561 396 : break;
4562 : }
4563 : }
4564 : else
4565 0 : bUseLib = bHasLib;
4566 :
4567 396 : GDALPDFObject *poPageObj = nullptr;
4568 : #ifdef HAVE_POPPLER
4569 396 : PDFDoc *poDocPoppler = nullptr;
4570 396 : Page *poPagePoppler = nullptr;
4571 396 : Catalog *poCatalogPoppler = nullptr;
4572 : #endif
4573 : #ifdef HAVE_PODOFO
4574 : std::unique_ptr<PoDoFo::PdfMemDocument> poDocPodofo;
4575 : PoDoFo::PdfPage *poPagePodofo = nullptr;
4576 : #endif
4577 : #ifdef HAVE_PDFIUM
4578 396 : TPdfiumDocumentStruct *poDocPdfium = nullptr;
4579 396 : TPdfiumPageStruct *poPagePdfium = nullptr;
4580 : #endif
4581 396 : int nPages = 0;
4582 396 : VSIVirtualHandleUniquePtr fp;
4583 :
4584 : #ifdef HAVE_POPPLER
4585 396 : if (bUseLib.test(PDFLIB_POPPLER))
4586 : {
4587 : static bool globalParamsCreatedByGDAL = false;
4588 : {
4589 342 : CPLMutexHolderD(&hGlobalParamsMutex);
4590 : /* poppler global variable */
4591 171 : if (globalParams == nullptr)
4592 : {
4593 2 : globalParamsCreatedByGDAL = true;
4594 2 : globalParams.reset(new GlobalParams());
4595 : }
4596 :
4597 171 : globalParams->setPrintCommands(CPLTestBool(
4598 : CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE")));
4599 : }
4600 :
4601 340 : const auto registerErrorCallback = []()
4602 : {
4603 : /* Set custom error handler for poppler errors */
4604 340 : setErrorCallback(PDFDatasetErrorFunction);
4605 340 : assert(globalParams); // avoid CSA false positive
4606 340 : globalParams->setErrQuiet(false);
4607 340 : };
4608 :
4609 171 : fp.reset(VSIFOpenL(pszFilename, "rb"));
4610 171 : if (!fp)
4611 4 : return nullptr;
4612 :
4613 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
4614 : {
4615 : // Workaround for ossfuzz only due to
4616 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37584
4617 : // https://gitlab.freedesktop.org/poppler/poppler/-/issues/1137
4618 : GByte *pabyRet = nullptr;
4619 : vsi_l_offset nSize = 0;
4620 : if (VSIIngestFile(fp.get(), pszFilename, &pabyRet, &nSize,
4621 : 10 * 1024 * 1024))
4622 : {
4623 : // Replace nul byte by something else so that strstr() works
4624 : for (size_t i = 0; i < nSize; i++)
4625 : {
4626 : if (pabyRet[i] == 0)
4627 : pabyRet[i] = ' ';
4628 : }
4629 : if (strstr(reinterpret_cast<const char *>(pabyRet),
4630 : "/JBIG2Decode"))
4631 : {
4632 : CPLError(CE_Failure, CPLE_AppDefined,
4633 : "/JBIG2Decode found. Giving up due to potential "
4634 : "very long processing time.");
4635 : CPLFree(pabyRet);
4636 : return nullptr;
4637 : }
4638 : }
4639 : CPLFree(pabyRet);
4640 : }
4641 : #endif
4642 :
4643 170 : fp.reset(VSICreateBufferedReaderHandle(fp.release()));
4644 : while (true)
4645 : {
4646 170 : fp->Seek(0, SEEK_SET);
4647 170 : g_nPopplerErrors = 0;
4648 170 : if (globalParamsCreatedByGDAL)
4649 170 : registerErrorCallback();
4650 170 : Object oObj;
4651 : auto poStream =
4652 170 : new VSIPDFFileStream(fp.get(), pszFilename, std::move(oObj));
4653 : #if POPPLER_MAJOR_VERSION > 22 || \
4654 : (POPPLER_MAJOR_VERSION == 22 && POPPLER_MINOR_VERSION > 2)
4655 : std::optional<GooString> osUserPwd;
4656 : if (pszUserPwd)
4657 : osUserPwd = std::optional<GooString>(pszUserPwd);
4658 : try
4659 : {
4660 : poDocPoppler =
4661 : new PDFDoc(poStream, std::optional<GooString>(), osUserPwd);
4662 : }
4663 : catch (const std::exception &e)
4664 : {
4665 : CPLError(CE_Failure, CPLE_AppDefined,
4666 : "PDFDoc::PDFDoc() failed with %s", e.what());
4667 : return nullptr;
4668 : }
4669 : #else
4670 170 : GooString *poUserPwd = nullptr;
4671 170 : if (pszUserPwd)
4672 2 : poUserPwd = new GooString(pszUserPwd);
4673 170 : poDocPoppler = new PDFDoc(poStream, nullptr, poUserPwd);
4674 170 : delete poUserPwd;
4675 : #endif
4676 170 : if (globalParamsCreatedByGDAL)
4677 170 : registerErrorCallback();
4678 170 : if (g_nPopplerErrors >= MAX_POPPLER_ERRORS)
4679 : {
4680 0 : PDFFreeDoc(poDocPoppler);
4681 0 : return nullptr;
4682 : }
4683 :
4684 170 : if (!poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0)
4685 : {
4686 2 : if (poDocPoppler->getErrorCode() == errEncrypted)
4687 : {
4688 2 : if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
4689 : {
4690 : pszUserPwd =
4691 0 : PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd);
4692 0 : PDFFreeDoc(poDocPoppler);
4693 :
4694 : /* Reset errors that could have been issued during
4695 : * opening and that */
4696 : /* did not result in an invalid document */
4697 0 : CPLErrorReset();
4698 :
4699 0 : continue;
4700 : }
4701 2 : else if (pszUserPwd == nullptr)
4702 : {
4703 1 : CPLError(CE_Failure, CPLE_AppDefined,
4704 : "A password is needed. You can specify it "
4705 : "through the PDF_USER_PWD "
4706 : "configuration option / USER_PWD open option "
4707 : "(that can be set to ASK_INTERACTIVE)");
4708 : }
4709 : else
4710 : {
4711 1 : CPLError(CE_Failure, CPLE_AppDefined,
4712 : "Invalid password");
4713 : }
4714 : }
4715 : else
4716 : {
4717 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4718 : }
4719 :
4720 2 : PDFFreeDoc(poDocPoppler);
4721 2 : return nullptr;
4722 : }
4723 168 : else if (poDocPoppler->isLinearized() &&
4724 0 : !poStream->FoundLinearizedHint())
4725 : {
4726 : // This is a likely defect of poppler Linearization.cc file that
4727 : // recognizes a file as linearized if the /Linearized hint is
4728 : // missing, but the content of this dictionary are present. But
4729 : // given the hacks of PDFFreeDoc() and
4730 : // VSIPDFFileStream::FillBuffer() opening such a file will
4731 : // result in a null-ptr deref at closing if we try to access a
4732 : // page and build the page cache, so just exit now
4733 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4734 :
4735 0 : PDFFreeDoc(poDocPoppler);
4736 0 : return nullptr;
4737 : }
4738 : else
4739 : {
4740 168 : break;
4741 : }
4742 0 : }
4743 :
4744 168 : poCatalogPoppler = poDocPoppler->getCatalog();
4745 168 : if (poCatalogPoppler == nullptr || !poCatalogPoppler->isOk())
4746 : {
4747 0 : CPLError(CE_Failure, CPLE_AppDefined,
4748 : "Invalid PDF : invalid catalog");
4749 0 : PDFFreeDoc(poDocPoppler);
4750 0 : return nullptr;
4751 : }
4752 :
4753 168 : nPages = poDocPoppler->getNumPages();
4754 :
4755 168 : if (iPage == 1 && nPages > 10000 &&
4756 0 : CPLTestBool(CPLGetConfigOption("GDAL_PDF_LIMIT_PAGE_COUNT", "YES")))
4757 : {
4758 0 : CPLError(CE_Warning, CPLE_AppDefined,
4759 : "This PDF document reports %d pages. "
4760 : "Limiting count to 10000 for performance reasons. "
4761 : "You may remove this limit by setting the "
4762 : "GDAL_PDF_LIMIT_PAGE_COUNT configuration option to NO",
4763 : nPages);
4764 0 : nPages = 10000;
4765 : }
4766 :
4767 168 : if (iPage < 1 || iPage > nPages)
4768 : {
4769 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
4770 : iPage, nPages);
4771 1 : PDFFreeDoc(poDocPoppler);
4772 1 : return nullptr;
4773 : }
4774 :
4775 : /* Sanity check to validate page count */
4776 167 : if (iPage > 1 && nPages <= 10000 && iPage != nPages)
4777 : {
4778 4 : poPagePoppler = poCatalogPoppler->getPage(nPages);
4779 4 : if (poPagePoppler == nullptr || !poPagePoppler->isOk())
4780 : {
4781 0 : CPLError(CE_Failure, CPLE_AppDefined,
4782 : "Invalid PDF : invalid page count");
4783 0 : PDFFreeDoc(poDocPoppler);
4784 0 : return nullptr;
4785 : }
4786 : }
4787 :
4788 167 : poPagePoppler = poCatalogPoppler->getPage(iPage);
4789 167 : if (poPagePoppler == nullptr || !poPagePoppler->isOk())
4790 : {
4791 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
4792 0 : PDFFreeDoc(poDocPoppler);
4793 0 : return nullptr;
4794 : }
4795 :
4796 : /* Here's the dirty part: this is a private member */
4797 : /* so we had to #define private public to get it ! */
4798 167 : Object &oPageObj = poPagePoppler->pageObj;
4799 167 : if (!oPageObj.isDict())
4800 : {
4801 0 : CPLError(CE_Failure, CPLE_AppDefined,
4802 : "Invalid PDF : !oPageObj.isDict()");
4803 0 : PDFFreeDoc(poDocPoppler);
4804 0 : return nullptr;
4805 : }
4806 :
4807 167 : poPageObj = new GDALPDFObjectPoppler(&oPageObj, FALSE);
4808 167 : Ref *poPageRef = poCatalogPoppler->getPageRef(iPage);
4809 167 : if (poPageRef != nullptr)
4810 : {
4811 334 : cpl::down_cast<GDALPDFObjectPoppler *>(poPageObj)->SetRefNumAndGen(
4812 334 : GDALPDFObjectNum(poPageRef->num), poPageRef->gen);
4813 : }
4814 : }
4815 : #endif // ~ HAVE_POPPLER
4816 :
4817 : #ifdef HAVE_PODOFO
4818 : if (bUseLib.test(PDFLIB_PODOFO) && poPageObj == nullptr)
4819 : {
4820 : #if !(PODOFO_VERSION_MAJOR > 0 || \
4821 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10))
4822 : PoDoFo::PdfError::EnableDebug(false);
4823 : PoDoFo::PdfError::EnableLogging(false);
4824 : #endif
4825 :
4826 : poDocPodofo = std::make_unique<PoDoFo::PdfMemDocument>();
4827 : try
4828 : {
4829 : poDocPodofo->Load(pszFilename);
4830 : }
4831 : catch (PoDoFo::PdfError &oError)
4832 : {
4833 : #if PODOFO_VERSION_MAJOR > 0 || \
4834 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4835 : if (oError.GetCode() == PoDoFo::PdfErrorCode::InvalidPassword)
4836 : #else
4837 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
4838 : #endif
4839 : {
4840 : if (pszUserPwd)
4841 : {
4842 : pszUserPwd =
4843 : PDFEnterPasswordFromConsoleIfNeeded(pszUserPwd);
4844 :
4845 : try
4846 : {
4847 : #if PODOFO_VERSION_MAJOR > 0 || \
4848 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4849 : poDocPodofo =
4850 : std::make_unique<PoDoFo::PdfMemDocument>();
4851 : poDocPodofo->Load(pszFilename, pszUserPwd);
4852 : #else
4853 : poDocPodofo->SetPassword(pszUserPwd);
4854 : #endif
4855 : }
4856 : catch (PoDoFo::PdfError &oError2)
4857 : {
4858 : #if PODOFO_VERSION_MAJOR > 0 || \
4859 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4860 : if (oError2.GetCode() ==
4861 : PoDoFo::PdfErrorCode::InvalidPassword)
4862 : #else
4863 : if (oError2.GetError() ==
4864 : PoDoFo::ePdfError_InvalidPassword)
4865 : #endif
4866 : {
4867 : CPLError(CE_Failure, CPLE_AppDefined,
4868 : "Invalid password");
4869 : }
4870 : else
4871 : {
4872 : CPLError(CE_Failure, CPLE_AppDefined,
4873 : "Invalid PDF : %s", oError2.what());
4874 : }
4875 : return nullptr;
4876 : }
4877 : catch (...)
4878 : {
4879 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4880 : return nullptr;
4881 : }
4882 : }
4883 : else
4884 : {
4885 : CPLError(CE_Failure, CPLE_AppDefined,
4886 : "A password is needed. You can specify it through "
4887 : "the PDF_USER_PWD "
4888 : "configuration option / USER_PWD open option "
4889 : "(that can be set to ASK_INTERACTIVE)");
4890 : return nullptr;
4891 : }
4892 : }
4893 : else
4894 : {
4895 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s",
4896 : oError.what());
4897 : return nullptr;
4898 : }
4899 : }
4900 : catch (...)
4901 : {
4902 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4903 : return nullptr;
4904 : }
4905 :
4906 : #if PODOFO_VERSION_MAJOR > 0 || \
4907 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4908 : auto &oPageCollections = poDocPodofo->GetPages();
4909 : nPages = static_cast<int>(oPageCollections.GetCount());
4910 : #else
4911 : nPages = poDocPodofo->GetPageCount();
4912 : #endif
4913 : if (iPage < 1 || iPage > nPages)
4914 : {
4915 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
4916 : iPage, nPages);
4917 : return nullptr;
4918 : }
4919 :
4920 : try
4921 : {
4922 : #if PODOFO_VERSION_MAJOR > 0 || \
4923 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4924 : /* Sanity check to validate page count */
4925 : if (iPage != nPages)
4926 : CPL_IGNORE_RET_VAL(oPageCollections.GetPageAt(nPages - 1));
4927 :
4928 : poPagePodofo = &oPageCollections.GetPageAt(iPage - 1);
4929 : #else
4930 : /* Sanity check to validate page count */
4931 : if (iPage != nPages)
4932 : CPL_IGNORE_RET_VAL(poDocPodofo->GetPage(nPages - 1));
4933 :
4934 : poPagePodofo = poDocPodofo->GetPage(iPage - 1);
4935 : #endif
4936 : }
4937 : catch (PoDoFo::PdfError &oError)
4938 : {
4939 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s",
4940 : oError.what());
4941 : return nullptr;
4942 : }
4943 : catch (...)
4944 : {
4945 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
4946 : return nullptr;
4947 : }
4948 :
4949 : if (poPagePodofo == nullptr)
4950 : {
4951 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
4952 : return nullptr;
4953 : }
4954 :
4955 : #if PODOFO_VERSION_MAJOR > 0 || \
4956 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
4957 : const PoDoFo::PdfObject *pObj = &poPagePodofo->GetObject();
4958 : #else
4959 : const PoDoFo::PdfObject *pObj = poPagePodofo->GetObject();
4960 : #endif
4961 : poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects());
4962 : }
4963 : #endif // ~ HAVE_PODOFO
4964 :
4965 : #ifdef HAVE_PDFIUM
4966 392 : if (bUseLib.test(PDFLIB_PDFIUM) && poPageObj == nullptr)
4967 : {
4968 225 : if (!LoadPdfiumDocumentPage(pszFilename, pszUserPwd, iPage,
4969 : &poDocPdfium, &poPagePdfium, &nPages))
4970 : {
4971 : // CPLError is called inside function
4972 14 : return nullptr;
4973 : }
4974 :
4975 211 : const auto pageObj = poPagePdfium->page->GetDict();
4976 211 : if (pageObj == nullptr)
4977 : {
4978 0 : CPLError(CE_Failure, CPLE_AppDefined,
4979 : "Invalid PDF : invalid page object");
4980 0 : UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium);
4981 0 : return nullptr;
4982 : }
4983 211 : poPageObj = GDALPDFObjectPdfium::Build(pageObj);
4984 : }
4985 : #endif // ~ HAVE_PDFIUM
4986 :
4987 378 : if (poPageObj == nullptr)
4988 0 : return nullptr;
4989 378 : GDALPDFDictionary *poPageDict = poPageObj->GetDictionary();
4990 378 : if (poPageDict == nullptr)
4991 : {
4992 0 : delete poPageObj;
4993 :
4994 0 : CPLError(CE_Failure, CPLE_AppDefined,
4995 : "Invalid PDF : poPageDict == nullptr");
4996 : #ifdef HAVE_POPPLER
4997 0 : if (bUseLib.test(PDFLIB_POPPLER))
4998 0 : PDFFreeDoc(poDocPoppler);
4999 : #endif
5000 : #ifdef HAVE_PDFIUM
5001 0 : if (bUseLib.test(PDFLIB_PDFIUM))
5002 : {
5003 0 : UnloadPdfiumDocumentPage(&poDocPdfium, &poPagePdfium);
5004 : }
5005 : #endif
5006 0 : return nullptr;
5007 : }
5008 :
5009 378 : const char *pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", nullptr);
5010 378 : if (pszDumpObject != nullptr)
5011 : {
5012 4 : GDALPDFDumper oDumper(pszFilename, pszDumpObject);
5013 2 : oDumper.Dump(poPageObj);
5014 : }
5015 :
5016 378 : PDFDataset *poDS = new PDFDataset();
5017 378 : poDS->m_fp = std::move(fp);
5018 378 : poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
5019 378 : poDS->m_bUseLib = bUseLib;
5020 378 : poDS->m_osFilename = pszFilename;
5021 378 : poDS->eAccess = poOpenInfo->eAccess;
5022 :
5023 378 : if (nPages > 1 && !bOpenSubdataset)
5024 : {
5025 : int i;
5026 8 : CPLStringList aosList;
5027 16 : for (i = 0; i < nPages; i++)
5028 : {
5029 : char szKey[32];
5030 12 : snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_NAME", i + 1);
5031 : aosList.AddNameValue(
5032 12 : szKey, CPLSPrintf("PDF:%d:%s", i + 1, poOpenInfo->pszFilename));
5033 12 : snprintf(szKey, sizeof(szKey), "SUBDATASET_%d_DESC", i + 1);
5034 : aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i + 1,
5035 12 : poOpenInfo->pszFilename));
5036 : }
5037 4 : poDS->SetMetadata(aosList.List(), "SUBDATASETS");
5038 : }
5039 :
5040 : #ifdef HAVE_POPPLER
5041 378 : poDS->m_poDocPoppler = poDocPoppler;
5042 : #endif
5043 : #ifdef HAVE_PODOFO
5044 : poDS->m_poDocPodofo = poDocPodofo.release();
5045 : #endif
5046 : #ifdef HAVE_PDFIUM
5047 378 : poDS->m_poDocPdfium = poDocPdfium;
5048 378 : poDS->m_poPagePdfium = poPagePdfium;
5049 : #endif
5050 378 : poDS->m_poPageObj = poPageObj;
5051 378 : poDS->m_osUserPwd = pszUserPwd ? pszUserPwd : "";
5052 378 : poDS->m_iPage = iPage;
5053 :
5054 : const char *pszDumpCatalog =
5055 378 : CPLGetConfigOption("PDF_DUMP_CATALOG", nullptr);
5056 378 : if (pszDumpCatalog != nullptr)
5057 : {
5058 0 : GDALPDFDumper oDumper(pszFilename, pszDumpCatalog);
5059 0 : auto poCatalog = poDS->GetCatalog();
5060 0 : if (poCatalog)
5061 0 : oDumper.Dump(poCatalog);
5062 : }
5063 :
5064 378 : int nBandsGuessed = 0;
5065 378 : if (nImageNum < 0)
5066 : {
5067 378 : poDS->GuessDPI(poPageDict, &nBandsGuessed);
5068 378 : if (nBandsGuessed < 4)
5069 362 : nBandsGuessed = 0;
5070 : }
5071 : else
5072 : {
5073 : const char *pszDPI =
5074 0 : GetOption(poOpenInfo->papszOpenOptions, "DPI", nullptr);
5075 0 : if (pszDPI != nullptr)
5076 : {
5077 : // coverity[tainted_data]
5078 0 : poDS->m_dfDPI = CPLAtof(pszDPI);
5079 : }
5080 : }
5081 :
5082 378 : double dfX1 = 0.0;
5083 378 : double dfY1 = 0.0;
5084 378 : double dfX2 = 0.0;
5085 378 : double dfY2 = 0.0;
5086 :
5087 : #ifdef HAVE_POPPLER
5088 378 : if (bUseLib.test(PDFLIB_POPPLER))
5089 : {
5090 167 : const auto *psMediaBox = poPagePoppler->getMediaBox();
5091 167 : dfX1 = psMediaBox->x1;
5092 167 : dfY1 = psMediaBox->y1;
5093 167 : dfX2 = psMediaBox->x2;
5094 167 : dfY2 = psMediaBox->y2;
5095 : }
5096 : #endif
5097 :
5098 : #ifdef HAVE_PODOFO
5099 : if (bUseLib.test(PDFLIB_PODOFO))
5100 : {
5101 : CPLAssert(poPagePodofo);
5102 : auto oMediaBox = poPagePodofo->GetMediaBox();
5103 : dfX1 = oMediaBox.GetLeft();
5104 : dfY1 = oMediaBox.GetBottom();
5105 : #if PODOFO_VERSION_MAJOR > 0 || \
5106 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
5107 : dfX2 = dfX1 + oMediaBox.Width;
5108 : dfY2 = dfY1 + oMediaBox.Height;
5109 : #else
5110 : dfX2 = dfX1 + oMediaBox.GetWidth();
5111 : dfY2 = dfY1 + oMediaBox.GetHeight();
5112 : #endif
5113 : }
5114 : #endif
5115 :
5116 : #ifdef HAVE_PDFIUM
5117 378 : if (bUseLib.test(PDFLIB_PDFIUM))
5118 : {
5119 211 : CPLAssert(poPagePdfium);
5120 211 : CFX_FloatRect rect = poPagePdfium->page->GetBBox();
5121 211 : dfX1 = rect.left;
5122 211 : dfX2 = rect.right;
5123 211 : dfY1 = rect.bottom;
5124 211 : dfY2 = rect.top;
5125 : }
5126 : #endif // ~ HAVE_PDFIUM
5127 :
5128 378 : double dfUserUnit = poDS->m_dfDPI * USER_UNIT_IN_INCH;
5129 378 : poDS->m_dfPageWidth = dfX2 - dfX1;
5130 378 : poDS->m_dfPageHeight = dfY2 - dfY1;
5131 : // CPLDebug("PDF", "left=%f right=%f bottom=%f top=%f", dfX1, dfX2, dfY1,
5132 : // dfY2);
5133 378 : const double dfXSize = floor((dfX2 - dfX1) * dfUserUnit + 0.5);
5134 378 : const double dfYSize = floor((dfY2 - dfY1) * dfUserUnit + 0.5);
5135 378 : if (!(dfXSize >= 0 && dfXSize <= INT_MAX && dfYSize >= 0 &&
5136 378 : dfYSize <= INT_MAX))
5137 : {
5138 0 : delete poDS;
5139 0 : return nullptr;
5140 : }
5141 378 : poDS->nRasterXSize = static_cast<int>(dfXSize);
5142 378 : poDS->nRasterYSize = static_cast<int>(dfYSize);
5143 :
5144 378 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
5145 : {
5146 0 : delete poDS;
5147 0 : return nullptr;
5148 : }
5149 :
5150 378 : double dfRotation = 0;
5151 : #ifdef HAVE_POPPLER
5152 378 : if (bUseLib.test(PDFLIB_POPPLER))
5153 167 : dfRotation = poDocPoppler->getPageRotate(iPage);
5154 : #endif
5155 :
5156 : #ifdef HAVE_PODOFO
5157 : if (bUseLib.test(PDFLIB_PODOFO))
5158 : {
5159 : CPLAssert(poPagePodofo);
5160 : #if PODOFO_VERSION_MAJOR > 0 || \
5161 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
5162 : dfRotation = poPagePodofo->GetRotationRaw();
5163 : #else
5164 : dfRotation = poPagePodofo->GetRotation();
5165 : #endif
5166 : }
5167 : #endif
5168 :
5169 : #ifdef HAVE_PDFIUM
5170 378 : if (bUseLib.test(PDFLIB_PDFIUM))
5171 : {
5172 211 : CPLAssert(poPagePdfium);
5173 211 : dfRotation = poPagePdfium->page->GetPageRotation() * 90;
5174 : }
5175 : #endif
5176 :
5177 378 : if (dfRotation == 90 || dfRotation == -90 || dfRotation == 270)
5178 : {
5179 : /* FIXME: the podofo case should be implemented. This needs to rotate */
5180 : /* the output of pdftoppm */
5181 : #if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
5182 0 : if (bUseLib.test(PDFLIB_POPPLER) || bUseLib.test(PDFLIB_PDFIUM))
5183 : {
5184 0 : int nTmp = poDS->nRasterXSize;
5185 0 : poDS->nRasterXSize = poDS->nRasterYSize;
5186 0 : poDS->nRasterYSize = nTmp;
5187 : }
5188 : #endif
5189 : }
5190 :
5191 378 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW"))
5192 : {
5193 2 : poDS->m_nBlockXSize = 512;
5194 2 : poDS->m_nBlockYSize = 512;
5195 : }
5196 : /* Check if the PDF is only made of regularly tiled images */
5197 : /* (like some USGS GeoPDF production) */
5198 673 : else if (dfRotation == 0.0 && !poDS->m_asTiles.empty() &&
5199 297 : EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"),
5200 : "ALL"))
5201 : {
5202 297 : poDS->CheckTiledRaster();
5203 297 : if (!poDS->m_aiTiles.empty())
5204 18 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
5205 : }
5206 :
5207 378 : GDALPDFObject *poLGIDict = nullptr;
5208 378 : GDALPDFObject *poVP = nullptr;
5209 378 : int bIsOGCBP = FALSE;
5210 378 : if ((poLGIDict = poPageDict->Get("LGIDict")) != nullptr && nImageNum < 0)
5211 : {
5212 : /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */
5213 0 : CPLDebug("PDF", "OGC Encoding Best Practice style detected");
5214 0 : if (poDS->ParseLGIDictObject(poLGIDict))
5215 : {
5216 0 : if (poDS->m_bHasCTM)
5217 : {
5218 0 : if (dfRotation == 90)
5219 : {
5220 0 : poDS->m_adfGeoTransform[0] = poDS->m_adfCTM[4];
5221 0 : poDS->m_adfGeoTransform[1] = poDS->m_adfCTM[2] / dfUserUnit;
5222 0 : poDS->m_adfGeoTransform[2] = poDS->m_adfCTM[0] / dfUserUnit;
5223 0 : poDS->m_adfGeoTransform[3] = poDS->m_adfCTM[5];
5224 0 : poDS->m_adfGeoTransform[4] = poDS->m_adfCTM[3] / dfUserUnit;
5225 0 : poDS->m_adfGeoTransform[5] = poDS->m_adfCTM[1] / dfUserUnit;
5226 : }
5227 0 : else if (dfRotation == -90 || dfRotation == 270)
5228 : {
5229 0 : poDS->m_adfGeoTransform[0] =
5230 0 : poDS->m_adfCTM[4] +
5231 0 : poDS->m_adfCTM[2] * poDS->m_dfPageHeight +
5232 0 : poDS->m_adfCTM[0] * poDS->m_dfPageWidth;
5233 0 : poDS->m_adfGeoTransform[1] =
5234 0 : -poDS->m_adfCTM[2] / dfUserUnit;
5235 0 : poDS->m_adfGeoTransform[2] =
5236 0 : -poDS->m_adfCTM[0] / dfUserUnit;
5237 0 : poDS->m_adfGeoTransform[3] =
5238 0 : poDS->m_adfCTM[5] +
5239 0 : poDS->m_adfCTM[3] * poDS->m_dfPageHeight +
5240 0 : poDS->m_adfCTM[1] * poDS->m_dfPageWidth;
5241 0 : poDS->m_adfGeoTransform[4] =
5242 0 : -poDS->m_adfCTM[3] / dfUserUnit;
5243 0 : poDS->m_adfGeoTransform[5] =
5244 0 : -poDS->m_adfCTM[1] / dfUserUnit;
5245 : }
5246 : else
5247 : {
5248 0 : poDS->m_adfGeoTransform[0] = poDS->m_adfCTM[4] +
5249 0 : poDS->m_adfCTM[2] * dfY2 +
5250 0 : poDS->m_adfCTM[0] * dfX1;
5251 0 : poDS->m_adfGeoTransform[1] = poDS->m_adfCTM[0] / dfUserUnit;
5252 0 : poDS->m_adfGeoTransform[2] =
5253 0 : -poDS->m_adfCTM[2] / dfUserUnit;
5254 0 : poDS->m_adfGeoTransform[3] = poDS->m_adfCTM[5] +
5255 0 : poDS->m_adfCTM[3] * dfY2 +
5256 0 : poDS->m_adfCTM[1] * dfX1;
5257 0 : poDS->m_adfGeoTransform[4] = poDS->m_adfCTM[1] / dfUserUnit;
5258 0 : poDS->m_adfGeoTransform[5] =
5259 0 : -poDS->m_adfCTM[3] / dfUserUnit;
5260 : }
5261 :
5262 0 : poDS->m_bGeoTransformValid = true;
5263 : }
5264 :
5265 0 : bIsOGCBP = TRUE;
5266 :
5267 : int i;
5268 0 : for (i = 0; i < poDS->m_nGCPCount; i++)
5269 : {
5270 0 : if (dfRotation == 90)
5271 : {
5272 0 : double dfPixel =
5273 0 : poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit;
5274 0 : double dfLine =
5275 0 : poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit;
5276 0 : poDS->m_pasGCPList[i].dfGCPPixel = dfLine;
5277 0 : poDS->m_pasGCPList[i].dfGCPLine = dfPixel;
5278 : }
5279 0 : else if (dfRotation == -90 || dfRotation == 270)
5280 : {
5281 0 : double dfPixel =
5282 0 : poDS->m_pasGCPList[i].dfGCPPixel * dfUserUnit;
5283 0 : double dfLine =
5284 0 : poDS->m_pasGCPList[i].dfGCPLine * dfUserUnit;
5285 0 : poDS->m_pasGCPList[i].dfGCPPixel =
5286 0 : poDS->nRasterXSize - dfLine;
5287 0 : poDS->m_pasGCPList[i].dfGCPLine =
5288 0 : poDS->nRasterYSize - dfPixel;
5289 : }
5290 : else
5291 : {
5292 0 : poDS->m_pasGCPList[i].dfGCPPixel =
5293 0 : (-dfX1 + poDS->m_pasGCPList[i].dfGCPPixel) * dfUserUnit;
5294 0 : poDS->m_pasGCPList[i].dfGCPLine =
5295 0 : (dfY2 - poDS->m_pasGCPList[i].dfGCPLine) * dfUserUnit;
5296 : }
5297 : }
5298 : }
5299 : }
5300 378 : else if ((poVP = poPageDict->Get("VP")) != nullptr && nImageNum < 0)
5301 : {
5302 : /* Cf adobe_supplement_iso32000.pdf */
5303 279 : CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?");
5304 279 : if (dfX1 != 0 || dfY1 != 0)
5305 : {
5306 0 : CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case...");
5307 : }
5308 279 : poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1);
5309 : }
5310 : else
5311 : {
5312 : GDALPDFObject *poXObject =
5313 99 : poPageDict->LookupObject("Resources.XObject");
5314 :
5315 196 : if (poXObject != nullptr &&
5316 97 : poXObject->GetType() == PDFObjectType_Dictionary)
5317 : {
5318 97 : GDALPDFDictionary *poXObjectDict = poXObject->GetDictionary();
5319 97 : const auto &oMap = poXObjectDict->GetValues();
5320 97 : int nSubDataset = 0;
5321 398 : for (const auto &[osKey, poObj] : oMap)
5322 : {
5323 301 : if (poObj->GetType() == PDFObjectType_Dictionary)
5324 : {
5325 301 : GDALPDFDictionary *poDict = poObj->GetDictionary();
5326 301 : GDALPDFObject *poSubtype = nullptr;
5327 301 : GDALPDFObject *poMeasure = nullptr;
5328 301 : GDALPDFObject *poWidth = nullptr;
5329 301 : GDALPDFObject *poHeight = nullptr;
5330 301 : int nW = 0;
5331 301 : int nH = 0;
5332 301 : if ((poSubtype = poDict->Get("Subtype")) != nullptr &&
5333 602 : poSubtype->GetType() == PDFObjectType_Name &&
5334 301 : poSubtype->GetName() == "Image" &&
5335 256 : (poMeasure = poDict->Get("Measure")) != nullptr &&
5336 0 : poMeasure->GetType() == PDFObjectType_Dictionary &&
5337 0 : (poWidth = poDict->Get("Width")) != nullptr &&
5338 0 : poWidth->GetType() == PDFObjectType_Int &&
5339 0 : (nW = poWidth->GetInt()) > 0 &&
5340 0 : (poHeight = poDict->Get("Height")) != nullptr &&
5341 602 : poHeight->GetType() == PDFObjectType_Int &&
5342 0 : (nH = poHeight->GetInt()) > 0)
5343 : {
5344 0 : if (nImageNum < 0)
5345 0 : CPLDebug("PDF",
5346 : "Measure found on Image object (%d)",
5347 0 : poObj->GetRefNum().toInt());
5348 :
5349 0 : GDALPDFObject *poColorSpace = poDict->Get("ColorSpace");
5350 : GDALPDFObject *poBitsPerComponent =
5351 0 : poDict->Get("BitsPerComponent");
5352 0 : if (poObj->GetRefNum().toBool() &&
5353 0 : poObj->GetRefGen() == 0 &&
5354 0 : poColorSpace != nullptr &&
5355 0 : poColorSpace->GetType() == PDFObjectType_Name &&
5356 0 : (poColorSpace->GetName() == "DeviceGray" ||
5357 0 : poColorSpace->GetName() == "DeviceRGB") &&
5358 0 : (poBitsPerComponent == nullptr ||
5359 0 : (poBitsPerComponent->GetType() ==
5360 0 : PDFObjectType_Int &&
5361 0 : poBitsPerComponent->GetInt() == 8)))
5362 : {
5363 0 : if (nImageNum < 0)
5364 : {
5365 0 : nSubDataset++;
5366 0 : poDS->SetMetadataItem(
5367 : CPLSPrintf("SUBDATASET_%d_NAME",
5368 : nSubDataset),
5369 : CPLSPrintf("PDF_IMAGE:%d:%d:%s", iPage,
5370 0 : poObj->GetRefNum().toInt(),
5371 : pszFilename),
5372 : "SUBDATASETS");
5373 0 : poDS->SetMetadataItem(
5374 : CPLSPrintf("SUBDATASET_%d_DESC",
5375 : nSubDataset),
5376 : CPLSPrintf("Georeferenced image of size "
5377 : "%dx%d of page %d of %s",
5378 : nW, nH, iPage, pszFilename),
5379 : "SUBDATASETS");
5380 : }
5381 0 : else if (poObj->GetRefNum().toInt() == nImageNum)
5382 : {
5383 0 : poDS->nRasterXSize = nW;
5384 0 : poDS->nRasterYSize = nH;
5385 0 : poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW,
5386 : 0);
5387 0 : poDS->m_poImageObj = poObj;
5388 0 : if (poColorSpace->GetName() == "DeviceGray")
5389 0 : nBandsGuessed = 1;
5390 0 : break;
5391 : }
5392 : }
5393 : }
5394 : }
5395 : }
5396 : }
5397 :
5398 99 : if (nImageNum >= 0 && poDS->m_poImageObj == nullptr)
5399 : {
5400 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d",
5401 : nImageNum);
5402 0 : delete poDS;
5403 0 : return nullptr;
5404 : }
5405 :
5406 : /* Not a geospatial PDF doc */
5407 : }
5408 :
5409 : /* If pixel size or top left coordinates are very close to an int, round
5410 : * them to the int */
5411 378 : double dfEps = (fabs(poDS->m_adfGeoTransform[0]) > 1e5 &&
5412 205 : fabs(poDS->m_adfGeoTransform[3]) > 1e5)
5413 583 : ? 1e-5
5414 378 : : 1e-8;
5415 756 : poDS->m_adfGeoTransform[0] =
5416 378 : ROUND_IF_CLOSE(poDS->m_adfGeoTransform[0], dfEps);
5417 378 : poDS->m_adfGeoTransform[1] = ROUND_IF_CLOSE(poDS->m_adfGeoTransform[1]);
5418 756 : poDS->m_adfGeoTransform[3] =
5419 378 : ROUND_IF_CLOSE(poDS->m_adfGeoTransform[3], dfEps);
5420 378 : poDS->m_adfGeoTransform[5] = ROUND_IF_CLOSE(poDS->m_adfGeoTransform[5]);
5421 :
5422 378 : if (bUseLib.test(PDFLIB_PDFIUM))
5423 : {
5424 : // Attempt to "fix" the loss of precision due to the use of float32 for
5425 : // numbers by pdfium
5426 211 : if ((fabs(poDS->m_adfGeoTransform[0]) > 1e5 ||
5427 100 : fabs(poDS->m_adfGeoTransform[3]) > 1e5) &&
5428 111 : fabs(poDS->m_adfGeoTransform[0] -
5429 111 : std::round(poDS->m_adfGeoTransform[0])) <
5430 111 : 1e-6 * fabs(poDS->m_adfGeoTransform[0]) &&
5431 107 : fabs(poDS->m_adfGeoTransform[1] -
5432 107 : std::round(poDS->m_adfGeoTransform[1])) <
5433 107 : 1e-3 * fabs(poDS->m_adfGeoTransform[1]) &&
5434 84 : fabs(poDS->m_adfGeoTransform[3] -
5435 84 : std::round(poDS->m_adfGeoTransform[3])) <
5436 506 : 1e-6 * fabs(poDS->m_adfGeoTransform[3]) &&
5437 84 : fabs(poDS->m_adfGeoTransform[5] -
5438 84 : std::round(poDS->m_adfGeoTransform[5])) <
5439 84 : 1e-3 * fabs(poDS->m_adfGeoTransform[5]))
5440 : {
5441 588 : for (int i = 0; i < 6; i++)
5442 : {
5443 504 : poDS->m_adfGeoTransform[i] =
5444 504 : std::round(poDS->m_adfGeoTransform[i]);
5445 : }
5446 : }
5447 : }
5448 :
5449 378 : if (poDS->m_poNeatLine)
5450 : {
5451 278 : char *pszNeatLineWkt = nullptr;
5452 278 : OGRLinearRing *poRing = poDS->m_poNeatLine->getExteriorRing();
5453 : /* Adobe style is already in target SRS units */
5454 278 : if (bIsOGCBP)
5455 : {
5456 0 : int nPoints = poRing->getNumPoints();
5457 : int i;
5458 :
5459 0 : for (i = 0; i < nPoints; i++)
5460 : {
5461 : double x, y;
5462 0 : if (dfRotation == 90.0)
5463 : {
5464 0 : x = poRing->getY(i) * dfUserUnit;
5465 0 : y = poRing->getX(i) * dfUserUnit;
5466 : }
5467 0 : else if (dfRotation == -90.0 || dfRotation == 270.0)
5468 : {
5469 0 : x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit;
5470 0 : y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit;
5471 : }
5472 : else
5473 : {
5474 0 : x = (-dfX1 + poRing->getX(i)) * dfUserUnit;
5475 0 : y = (dfY2 - poRing->getY(i)) * dfUserUnit;
5476 : }
5477 0 : double X = poDS->m_adfGeoTransform[0] +
5478 0 : x * poDS->m_adfGeoTransform[1] +
5479 0 : y * poDS->m_adfGeoTransform[2];
5480 0 : double Y = poDS->m_adfGeoTransform[3] +
5481 0 : x * poDS->m_adfGeoTransform[4] +
5482 0 : y * poDS->m_adfGeoTransform[5];
5483 0 : poRing->setPoint(i, X, Y);
5484 : }
5485 : }
5486 278 : poRing->closeRings();
5487 :
5488 278 : poDS->m_poNeatLine->exportToWkt(&pszNeatLineWkt);
5489 278 : if (nImageNum < 0)
5490 278 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
5491 278 : CPLFree(pszNeatLineWkt);
5492 : }
5493 :
5494 378 : poDS->MapOCGsToPages();
5495 :
5496 : #ifdef HAVE_POPPLER
5497 378 : if (bUseLib.test(PDFLIB_POPPLER))
5498 : {
5499 167 : auto poMetadata = poCatalogPoppler->readMetadata();
5500 167 : if (poMetadata)
5501 : {
5502 17 : const char *pszContent = poMetadata->c_str();
5503 17 : if (pszContent != nullptr &&
5504 17 : STARTS_WITH(pszContent, "<?xpacket begin="))
5505 : {
5506 17 : const char *const apszMDList[2] = {pszContent, nullptr};
5507 17 : poDS->SetMetadata(const_cast<char **>(apszMDList), "xml:XMP");
5508 : }
5509 : #if (POPPLER_MAJOR_VERSION < 21 || \
5510 : (POPPLER_MAJOR_VERSION == 21 && POPPLER_MINOR_VERSION < 10))
5511 17 : delete poMetadata;
5512 : #endif
5513 : }
5514 :
5515 : /* Read Info object */
5516 : /* The test is necessary since with some corrupted PDFs
5517 : * poDocPoppler->getDocInfo() */
5518 : /* might abort() */
5519 167 : if (poDocPoppler->getXRef()->isOk())
5520 : {
5521 334 : Object oInfo = poDocPoppler->getDocInfo();
5522 334 : GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE);
5523 167 : poDS->ParseInfo(&oInfoObjPoppler);
5524 : }
5525 :
5526 : /* Find layers */
5527 322 : poDS->FindLayersPoppler(
5528 155 : (bOpenSubdataset || bOpenSubdatasetImage) ? iPage : 0);
5529 :
5530 : /* Turn user specified layers on or off */
5531 167 : poDS->TurnLayersOnOffPoppler();
5532 : }
5533 : #endif
5534 :
5535 : #ifdef HAVE_PODOFO
5536 : if (bUseLib.test(PDFLIB_PODOFO))
5537 : {
5538 : for (const auto &obj : poDS->m_poDocPodofo->GetObjects())
5539 : {
5540 : GDALPDFObjectPodofo oObjPodofo(obj,
5541 : poDS->m_poDocPodofo->GetObjects());
5542 : poDS->FindXMP(&oObjPodofo);
5543 : }
5544 :
5545 : /* Find layers */
5546 : poDS->FindLayersGeneric(poPageDict);
5547 :
5548 : /* Read Info object */
5549 : const PoDoFo::PdfInfo *poInfo = poDS->m_poDocPodofo->GetInfo();
5550 : if (poInfo != nullptr)
5551 : {
5552 : GDALPDFObjectPodofo oInfoObjPodofo(
5553 : #if PODOFO_VERSION_MAJOR > 0 || \
5554 : (PODOFO_VERSION_MAJOR == 0 && PODOFO_VERSION_MINOR >= 10)
5555 : &(poInfo->GetObject()),
5556 : #else
5557 : poInfo->GetObject(),
5558 : #endif
5559 : poDS->m_poDocPodofo->GetObjects());
5560 : poDS->ParseInfo(&oInfoObjPodofo);
5561 : }
5562 : }
5563 : #endif
5564 : #ifdef HAVE_PDFIUM
5565 378 : if (bUseLib.test(PDFLIB_PDFIUM))
5566 : {
5567 : // coverity is confused by WrapRetain(), believing that multiple
5568 : // smart pointers manage the same raw pointer. Which is actually
5569 : // true, but a RetainPtr holds a reference counted object. It is
5570 : // thus safe to have several RetainPtr holding it.
5571 : // coverity[multiple_init_smart_ptr]
5572 211 : GDALPDFObjectPdfium *poRoot = GDALPDFObjectPdfium::Build(
5573 422 : pdfium::WrapRetain(poDocPdfium->doc->GetRoot()));
5574 211 : if (poRoot->GetType() == PDFObjectType_Dictionary)
5575 : {
5576 211 : GDALPDFDictionary *poDict = poRoot->GetDictionary();
5577 211 : GDALPDFObject *poMetadata(poDict->Get("Metadata"));
5578 211 : if (poMetadata != nullptr)
5579 : {
5580 21 : GDALPDFStream *poStream = poMetadata->GetStream();
5581 21 : if (poStream != nullptr)
5582 : {
5583 19 : char *pszContent = poStream->GetBytes();
5584 19 : const auto nLength = poStream->GetLength();
5585 19 : if (pszContent != nullptr && nLength > 15 &&
5586 19 : STARTS_WITH(pszContent, "<?xpacket begin="))
5587 : {
5588 : char *apszMDList[2];
5589 19 : apszMDList[0] = pszContent;
5590 19 : apszMDList[1] = nullptr;
5591 19 : poDS->SetMetadata(apszMDList, "xml:XMP");
5592 : }
5593 19 : CPLFree(pszContent);
5594 : }
5595 : }
5596 : }
5597 211 : delete poRoot;
5598 :
5599 : /* Find layers */
5600 211 : poDS->FindLayersPdfium((bOpenSubdataset || bOpenSubdatasetImage) ? iPage
5601 : : 0);
5602 :
5603 : /* Turn user specified layers on or off */
5604 211 : poDS->TurnLayersOnOffPdfium();
5605 :
5606 : GDALPDFObjectPdfium *poInfo =
5607 211 : GDALPDFObjectPdfium::Build(poDocPdfium->doc->GetInfo());
5608 211 : if (poInfo)
5609 : {
5610 : /* Read Info object */
5611 37 : poDS->ParseInfo(poInfo);
5612 37 : delete poInfo;
5613 : }
5614 : }
5615 : #endif // ~ HAVE_PDFIUM
5616 :
5617 378 : int nBands = 3;
5618 : #ifdef HAVE_PDFIUM
5619 : // Use Alpha channel for PDFIUM as default format RGBA
5620 378 : if (bUseLib.test(PDFLIB_PDFIUM))
5621 211 : nBands = 4;
5622 : #endif
5623 378 : if (nBandsGuessed)
5624 16 : nBands = nBandsGuessed;
5625 : const char *pszPDFBands =
5626 378 : GetOption(poOpenInfo->papszOpenOptions, "BANDS", nullptr);
5627 378 : if (pszPDFBands)
5628 : {
5629 2 : nBands = atoi(pszPDFBands);
5630 2 : if (nBands != 3 && nBands != 4)
5631 : {
5632 0 : CPLError(CE_Warning, CPLE_NotSupported,
5633 : "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback");
5634 0 : nBands = 3;
5635 : }
5636 : }
5637 : #ifdef HAVE_PODOFO
5638 : if (bUseLib.test(PDFLIB_PODOFO) && nBands == 4 && poDS->m_aiTiles.empty())
5639 : {
5640 : CPLError(CE_Warning, CPLE_NotSupported,
5641 : "GDAL_PDF_BANDS=4 not supported when PDF driver is compiled "
5642 : "against Podofo. "
5643 : "Using 3 as a fallback");
5644 : nBands = 3;
5645 : }
5646 : #endif
5647 :
5648 : int iBand;
5649 1729 : for (iBand = 1; iBand <= nBands; iBand++)
5650 : {
5651 1351 : if (poDS->m_poImageObj != nullptr)
5652 0 : poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand));
5653 : else
5654 1351 : poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand, 0));
5655 : }
5656 :
5657 : /* Check if this is a raster-only PDF file and that we are */
5658 : /* opened in vector-only mode */
5659 866 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
5660 396 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
5661 18 : !poDS->OpenVectorLayers(poPageDict))
5662 : {
5663 0 : CPLDebug("PDF", "This is a raster-only PDF dataset, "
5664 : "but it has been opened in vector-only mode");
5665 : /* Clear dirty flag */
5666 0 : poDS->m_bProjDirty = false;
5667 0 : poDS->m_bNeatLineDirty = false;
5668 0 : poDS->m_bInfoDirty = false;
5669 0 : poDS->m_bXMPDirty = false;
5670 0 : delete poDS;
5671 0 : return nullptr;
5672 : }
5673 :
5674 : /* -------------------------------------------------------------------- */
5675 : /* Initialize any PAM information. */
5676 : /* -------------------------------------------------------------------- */
5677 378 : if (bOpenSubdataset || bOpenSubdatasetImage)
5678 : {
5679 24 : poDS->SetPhysicalFilename(pszFilename);
5680 24 : poDS->SetSubdatasetName(osSubdatasetName.c_str());
5681 : }
5682 : else
5683 : {
5684 354 : poDS->SetDescription(poOpenInfo->pszFilename);
5685 : }
5686 :
5687 378 : poDS->TryLoadXML();
5688 :
5689 : /* -------------------------------------------------------------------- */
5690 : /* Support overviews. */
5691 : /* -------------------------------------------------------------------- */
5692 378 : if (!CSLFetchNameValue(poOpenInfo->papszOpenOptions, "@OPEN_FOR_OVERVIEW"))
5693 : {
5694 376 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
5695 : }
5696 :
5697 : /* Clear dirty flag */
5698 378 : poDS->m_bProjDirty = false;
5699 378 : poDS->m_bNeatLineDirty = false;
5700 378 : poDS->m_bInfoDirty = false;
5701 378 : poDS->m_bXMPDirty = false;
5702 :
5703 378 : return (poDS);
5704 : }
5705 :
5706 : /************************************************************************/
5707 : /* ParseLGIDictObject() */
5708 : /************************************************************************/
5709 :
5710 0 : int PDFDataset::ParseLGIDictObject(GDALPDFObject *poLGIDict)
5711 : {
5712 0 : bool bOK = false;
5713 0 : if (poLGIDict->GetType() == PDFObjectType_Array)
5714 : {
5715 0 : GDALPDFArray *poArray = poLGIDict->GetArray();
5716 0 : int nArrayLength = poArray->GetLength();
5717 0 : int iMax = -1;
5718 0 : GDALPDFObject *poArrayElt = nullptr;
5719 0 : for (int i = 0; i < nArrayLength; i++)
5720 : {
5721 0 : if ((poArrayElt = poArray->Get(i)) == nullptr ||
5722 0 : poArrayElt->GetType() != PDFObjectType_Dictionary)
5723 : {
5724 0 : CPLError(CE_Failure, CPLE_AppDefined,
5725 : "LGIDict[%d] is not a dictionary", i);
5726 0 : return FALSE;
5727 : }
5728 :
5729 0 : int bIsBestCandidate = FALSE;
5730 0 : if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(),
5731 0 : &bIsBestCandidate))
5732 : {
5733 0 : if (bIsBestCandidate || iMax < 0)
5734 0 : iMax = i;
5735 : }
5736 : }
5737 :
5738 0 : if (iMax < 0)
5739 0 : return FALSE;
5740 :
5741 0 : poArrayElt = poArray->Get(iMax);
5742 0 : bOK = CPL_TO_BOOL(
5743 0 : ParseLGIDictDictSecondPass(poArrayElt->GetDictionary()));
5744 : }
5745 0 : else if (poLGIDict->GetType() == PDFObjectType_Dictionary)
5746 : {
5747 0 : bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) &&
5748 0 : ParseLGIDictDictSecondPass(poLGIDict->GetDictionary());
5749 : }
5750 : else
5751 : {
5752 0 : CPLError(CE_Failure, CPLE_AppDefined, "LGIDict is of type %s",
5753 0 : poLGIDict->GetTypeName());
5754 : }
5755 :
5756 0 : return bOK;
5757 : }
5758 :
5759 : /************************************************************************/
5760 : /* Get() */
5761 : /************************************************************************/
5762 :
5763 19869 : static double Get(GDALPDFObject *poObj, int nIndice)
5764 : {
5765 19869 : if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0)
5766 : {
5767 8820 : poObj = poObj->GetArray()->Get(nIndice);
5768 8820 : if (poObj == nullptr)
5769 0 : return 0;
5770 8820 : return Get(poObj);
5771 : }
5772 11049 : else if (poObj->GetType() == PDFObjectType_Int)
5773 8826 : return poObj->GetInt();
5774 2223 : else if (poObj->GetType() == PDFObjectType_Real)
5775 2223 : return poObj->GetReal();
5776 0 : else if (poObj->GetType() == PDFObjectType_String)
5777 : {
5778 0 : const char *pszStr = poObj->GetString().c_str();
5779 0 : size_t nLen = strlen(pszStr);
5780 0 : if (nLen == 0)
5781 0 : return 0;
5782 : /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W"
5783 : */
5784 0 : char chLast = pszStr[nLen - 1];
5785 0 : if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
5786 : {
5787 0 : double dfDeg = CPLAtof(pszStr);
5788 0 : double dfMin = 0.0;
5789 0 : double dfSec = 0.0;
5790 0 : const char *pszNext = strchr(pszStr, ' ');
5791 0 : if (pszNext)
5792 0 : pszNext++;
5793 0 : if (pszNext)
5794 0 : dfMin = CPLAtof(pszNext);
5795 0 : if (pszNext)
5796 0 : pszNext = strchr(pszNext, ' ');
5797 0 : if (pszNext)
5798 0 : pszNext++;
5799 0 : if (pszNext)
5800 0 : dfSec = CPLAtof(pszNext);
5801 0 : double dfVal = dfDeg + dfMin / 60 + dfSec / 3600;
5802 0 : if (chLast == 'W' || chLast == 'S')
5803 0 : return -dfVal;
5804 : else
5805 0 : return dfVal;
5806 : }
5807 0 : return CPLAtof(pszStr);
5808 : }
5809 : else
5810 : {
5811 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s",
5812 0 : poObj->GetTypeName());
5813 0 : return 0;
5814 : }
5815 : }
5816 :
5817 : /************************************************************************/
5818 : /* Get() */
5819 : /************************************************************************/
5820 :
5821 0 : static double Get(GDALPDFDictionary *poDict, const char *pszName)
5822 : {
5823 0 : GDALPDFObject *poObj = poDict->Get(pszName);
5824 0 : if (poObj != nullptr)
5825 0 : return Get(poObj);
5826 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find parameter %s", pszName);
5827 0 : return 0;
5828 : }
5829 :
5830 : /************************************************************************/
5831 : /* ParseLGIDictDictFirstPass() */
5832 : /************************************************************************/
5833 :
5834 0 : int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary *poLGIDict,
5835 : int *pbIsBestCandidate)
5836 : {
5837 0 : if (pbIsBestCandidate)
5838 0 : *pbIsBestCandidate = FALSE;
5839 :
5840 0 : if (poLGIDict == nullptr)
5841 0 : return FALSE;
5842 :
5843 : /* -------------------------------------------------------------------- */
5844 : /* Extract Type attribute */
5845 : /* -------------------------------------------------------------------- */
5846 0 : GDALPDFObject *poType = poLGIDict->Get("Type");
5847 0 : if (poType == nullptr)
5848 : {
5849 0 : CPLError(CE_Failure, CPLE_AppDefined,
5850 : "Cannot find Type of LGIDict object");
5851 0 : return FALSE;
5852 : }
5853 :
5854 0 : if (poType->GetType() != PDFObjectType_Name)
5855 : {
5856 0 : CPLError(CE_Failure, CPLE_AppDefined,
5857 : "Invalid type for Type of LGIDict object");
5858 0 : return FALSE;
5859 : }
5860 :
5861 0 : if (strcmp(poType->GetName().c_str(), "LGIDict") != 0)
5862 : {
5863 0 : CPLError(CE_Failure, CPLE_AppDefined,
5864 : "Invalid value for Type of LGIDict object : %s",
5865 0 : poType->GetName().c_str());
5866 0 : return FALSE;
5867 : }
5868 :
5869 : /* -------------------------------------------------------------------- */
5870 : /* Extract Version attribute */
5871 : /* -------------------------------------------------------------------- */
5872 0 : GDALPDFObject *poVersion = poLGIDict->Get("Version");
5873 0 : if (poVersion == nullptr)
5874 : {
5875 0 : CPLError(CE_Failure, CPLE_AppDefined,
5876 : "Cannot find Version of LGIDict object");
5877 0 : return FALSE;
5878 : }
5879 :
5880 0 : if (poVersion->GetType() == PDFObjectType_String)
5881 : {
5882 : /* OGC best practice is 2.1 */
5883 0 : CPLDebug("PDF", "LGIDict Version : %s", poVersion->GetString().c_str());
5884 : }
5885 0 : else if (poVersion->GetType() == PDFObjectType_Int)
5886 : {
5887 : /* Old TerraGo is 2 */
5888 0 : CPLDebug("PDF", "LGIDict Version : %d", poVersion->GetInt());
5889 : }
5890 :
5891 : /* USGS PDF maps have several LGIDict. Keep the one whose description */
5892 : /* is "Map Layers" by default */
5893 : const char *pszNeatlineToSelect =
5894 0 : GetOption(papszOpenOptions, "NEATLINE", "Map Layers");
5895 :
5896 : /* -------------------------------------------------------------------- */
5897 : /* Extract Neatline attribute */
5898 : /* -------------------------------------------------------------------- */
5899 0 : GDALPDFObject *poNeatline = poLGIDict->Get("Neatline");
5900 0 : if (poNeatline != nullptr && poNeatline->GetType() == PDFObjectType_Array)
5901 : {
5902 0 : int nLength = poNeatline->GetArray()->GetLength();
5903 0 : if ((nLength % 2) != 0 || nLength < 4)
5904 : {
5905 0 : CPLError(CE_Failure, CPLE_AppDefined,
5906 : "Invalid length for Neatline");
5907 0 : return FALSE;
5908 : }
5909 :
5910 0 : GDALPDFObject *poDescription = poLGIDict->Get("Description");
5911 0 : bool bIsAskedNeatline = false;
5912 0 : if (poDescription != nullptr &&
5913 0 : poDescription->GetType() == PDFObjectType_String)
5914 : {
5915 0 : CPLDebug("PDF", "Description = %s",
5916 0 : poDescription->GetString().c_str());
5917 :
5918 0 : if (EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect))
5919 : {
5920 0 : m_dfMaxArea = 1e300;
5921 0 : bIsAskedNeatline = true;
5922 : }
5923 : }
5924 :
5925 0 : if (!bIsAskedNeatline)
5926 : {
5927 0 : double dfMinX = 0.0;
5928 0 : double dfMinY = 0.0;
5929 0 : double dfMaxX = 0.0;
5930 0 : double dfMaxY = 0.0;
5931 0 : for (int i = 0; i < nLength; i += 2)
5932 : {
5933 0 : double dfX = Get(poNeatline, i);
5934 0 : double dfY = Get(poNeatline, i + 1);
5935 0 : if (i == 0 || dfX < dfMinX)
5936 0 : dfMinX = dfX;
5937 0 : if (i == 0 || dfY < dfMinY)
5938 0 : dfMinY = dfY;
5939 0 : if (i == 0 || dfX > dfMaxX)
5940 0 : dfMaxX = dfX;
5941 0 : if (i == 0 || dfY > dfMaxY)
5942 0 : dfMaxY = dfY;
5943 : }
5944 0 : double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY);
5945 0 : if (dfArea < m_dfMaxArea)
5946 : {
5947 0 : CPLDebug("PDF", "Not the largest neatline. Skipping it");
5948 0 : return TRUE;
5949 : }
5950 :
5951 0 : CPLDebug("PDF", "This is the largest neatline for now");
5952 0 : m_dfMaxArea = dfArea;
5953 : }
5954 : else
5955 0 : CPLDebug("PDF", "The \"%s\" registration will be selected",
5956 : pszNeatlineToSelect);
5957 :
5958 0 : if (pbIsBestCandidate)
5959 0 : *pbIsBestCandidate = TRUE;
5960 :
5961 0 : delete m_poNeatLine;
5962 0 : m_poNeatLine = new OGRPolygon();
5963 0 : OGRLinearRing *poRing = new OGRLinearRing();
5964 0 : if (nLength == 4)
5965 : {
5966 : /* 2 points only ? They are the bounding box */
5967 0 : double dfX1 = Get(poNeatline, 0);
5968 0 : double dfY1 = Get(poNeatline, 1);
5969 0 : double dfX2 = Get(poNeatline, 2);
5970 0 : double dfY2 = Get(poNeatline, 3);
5971 0 : poRing->addPoint(dfX1, dfY1);
5972 0 : poRing->addPoint(dfX2, dfY1);
5973 0 : poRing->addPoint(dfX2, dfY2);
5974 0 : poRing->addPoint(dfX1, dfY2);
5975 : }
5976 : else
5977 : {
5978 0 : for (int i = 0; i < nLength; i += 2)
5979 : {
5980 0 : double dfX = Get(poNeatline, i);
5981 0 : double dfY = Get(poNeatline, i + 1);
5982 0 : poRing->addPoint(dfX, dfY);
5983 : }
5984 : }
5985 0 : poRing->closeRings();
5986 0 : m_poNeatLine->addRingDirectly(poRing);
5987 : }
5988 :
5989 0 : return TRUE;
5990 : }
5991 :
5992 : /************************************************************************/
5993 : /* ParseLGIDictDictSecondPass() */
5994 : /************************************************************************/
5995 :
5996 0 : int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary *poLGIDict)
5997 : {
5998 : int i;
5999 :
6000 : /* -------------------------------------------------------------------- */
6001 : /* Extract Description attribute */
6002 : /* -------------------------------------------------------------------- */
6003 0 : GDALPDFObject *poDescription = poLGIDict->Get("Description");
6004 0 : if (poDescription != nullptr &&
6005 0 : poDescription->GetType() == PDFObjectType_String)
6006 : {
6007 0 : CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str());
6008 : }
6009 :
6010 : /* -------------------------------------------------------------------- */
6011 : /* Extract CTM attribute */
6012 : /* -------------------------------------------------------------------- */
6013 0 : GDALPDFObject *poCTM = poLGIDict->Get("CTM");
6014 0 : m_bHasCTM = false;
6015 0 : if (poCTM != nullptr && poCTM->GetType() == PDFObjectType_Array &&
6016 0 : CPLTestBool(CPLGetConfigOption("PDF_USE_CTM", "YES")))
6017 : {
6018 0 : int nLength = poCTM->GetArray()->GetLength();
6019 0 : if (nLength != 6)
6020 : {
6021 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for CTM");
6022 0 : return FALSE;
6023 : }
6024 :
6025 0 : m_bHasCTM = true;
6026 0 : for (i = 0; i < nLength; i++)
6027 : {
6028 0 : m_adfCTM[i] = Get(poCTM, i);
6029 : /* Nullify rotation terms that are significantly smaller than */
6030 : /* scaling terms. */
6031 0 : if ((i == 1 || i == 2) &&
6032 0 : fabs(m_adfCTM[i]) < fabs(m_adfCTM[0]) * 1e-10)
6033 0 : m_adfCTM[i] = 0;
6034 0 : CPLDebug("PDF", "CTM[%d] = %.16g", i, m_adfCTM[i]);
6035 : }
6036 : }
6037 :
6038 : /* -------------------------------------------------------------------- */
6039 : /* Extract Registration attribute */
6040 : /* -------------------------------------------------------------------- */
6041 0 : GDALPDFObject *poRegistration = poLGIDict->Get("Registration");
6042 0 : if (poRegistration != nullptr &&
6043 0 : poRegistration->GetType() == PDFObjectType_Array)
6044 : {
6045 0 : GDALPDFArray *poRegistrationArray = poRegistration->GetArray();
6046 0 : int nLength = poRegistrationArray->GetLength();
6047 0 : if (nLength > 4 || (!m_bHasCTM && nLength >= 2) ||
6048 0 : CPLTestBool(CPLGetConfigOption("PDF_REPORT_GCPS", "NO")))
6049 : {
6050 0 : m_nGCPCount = 0;
6051 0 : m_pasGCPList =
6052 0 : static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nLength));
6053 :
6054 0 : for (i = 0; i < nLength; i++)
6055 : {
6056 0 : GDALPDFObject *poGCP = poRegistrationArray->Get(i);
6057 0 : if (poGCP != nullptr &&
6058 0 : poGCP->GetType() == PDFObjectType_Array &&
6059 0 : poGCP->GetArray()->GetLength() == 4)
6060 : {
6061 0 : double dfUserX = Get(poGCP, 0);
6062 0 : double dfUserY = Get(poGCP, 1);
6063 0 : double dfX = Get(poGCP, 2);
6064 0 : double dfY = Get(poGCP, 3);
6065 0 : CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX);
6066 0 : CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY);
6067 0 : CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX);
6068 0 : CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY);
6069 :
6070 : char szID[32];
6071 0 : snprintf(szID, sizeof(szID), "%d", m_nGCPCount + 1);
6072 0 : m_pasGCPList[m_nGCPCount].pszId = CPLStrdup(szID);
6073 0 : m_pasGCPList[m_nGCPCount].pszInfo = CPLStrdup("");
6074 0 : m_pasGCPList[m_nGCPCount].dfGCPPixel = dfUserX;
6075 0 : m_pasGCPList[m_nGCPCount].dfGCPLine = dfUserY;
6076 0 : m_pasGCPList[m_nGCPCount].dfGCPX = dfX;
6077 0 : m_pasGCPList[m_nGCPCount].dfGCPY = dfY;
6078 0 : m_nGCPCount++;
6079 : }
6080 : }
6081 :
6082 0 : if (m_nGCPCount == 0)
6083 : {
6084 0 : CPLFree(m_pasGCPList);
6085 0 : m_pasGCPList = nullptr;
6086 : }
6087 : }
6088 : }
6089 :
6090 0 : if (!m_bHasCTM && m_nGCPCount == 0)
6091 : {
6092 0 : CPLDebug("PDF", "Neither CTM nor Registration found");
6093 0 : return FALSE;
6094 : }
6095 :
6096 : /* -------------------------------------------------------------------- */
6097 : /* Extract Projection attribute */
6098 : /* -------------------------------------------------------------------- */
6099 0 : GDALPDFObject *poProjection = poLGIDict->Get("Projection");
6100 0 : if (poProjection == nullptr ||
6101 0 : poProjection->GetType() != PDFObjectType_Dictionary)
6102 : {
6103 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection");
6104 0 : return FALSE;
6105 : }
6106 :
6107 0 : return ParseProjDict(poProjection->GetDictionary());
6108 : }
6109 :
6110 : /************************************************************************/
6111 : /* ParseProjDict() */
6112 : /************************************************************************/
6113 :
6114 0 : int PDFDataset::ParseProjDict(GDALPDFDictionary *poProjDict)
6115 : {
6116 0 : if (poProjDict == nullptr)
6117 0 : return FALSE;
6118 0 : OGRSpatialReference oSRS;
6119 0 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
6120 :
6121 : /* -------------------------------------------------------------------- */
6122 : /* Extract WKT attribute (GDAL extension) */
6123 : /* -------------------------------------------------------------------- */
6124 0 : GDALPDFObject *poWKT = poProjDict->Get("WKT");
6125 0 : if (poWKT != nullptr && poWKT->GetType() == PDFObjectType_String &&
6126 0 : CPLTestBool(CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE")))
6127 : {
6128 0 : CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it");
6129 0 : const char *pszWKTRead = poWKT->GetString().c_str();
6130 0 : if (pszWKTRead[0] != 0)
6131 0 : m_oSRS.importFromWkt(pszWKTRead);
6132 0 : return TRUE;
6133 : }
6134 :
6135 : /* -------------------------------------------------------------------- */
6136 : /* Extract Type attribute */
6137 : /* -------------------------------------------------------------------- */
6138 0 : GDALPDFObject *poType = poProjDict->Get("Type");
6139 0 : if (poType == nullptr)
6140 : {
6141 0 : CPLError(CE_Failure, CPLE_AppDefined,
6142 : "Cannot find Type of Projection object");
6143 0 : return FALSE;
6144 : }
6145 :
6146 0 : if (poType->GetType() != PDFObjectType_Name)
6147 : {
6148 0 : CPLError(CE_Failure, CPLE_AppDefined,
6149 : "Invalid type for Type of Projection object");
6150 0 : return FALSE;
6151 : }
6152 :
6153 0 : if (strcmp(poType->GetName().c_str(), "Projection") != 0)
6154 : {
6155 0 : CPLError(CE_Failure, CPLE_AppDefined,
6156 : "Invalid value for Type of Projection object : %s",
6157 0 : poType->GetName().c_str());
6158 0 : return FALSE;
6159 : }
6160 :
6161 : /* -------------------------------------------------------------------- */
6162 : /* Extract Datum attribute */
6163 : /* -------------------------------------------------------------------- */
6164 0 : int bIsWGS84 = FALSE;
6165 0 : int bIsNAD83 = FALSE;
6166 : /* int bIsNAD27 = FALSE; */
6167 :
6168 0 : GDALPDFObject *poDatum = poProjDict->Get("Datum");
6169 0 : if (poDatum != nullptr)
6170 : {
6171 0 : if (poDatum->GetType() == PDFObjectType_String)
6172 : {
6173 : /* Using Annex A of
6174 : * http://portal.opengeospatial.org/files/?artifact_id=40537 */
6175 0 : const char *pszDatum = poDatum->GetString().c_str();
6176 0 : CPLDebug("PDF", "Datum = %s", pszDatum);
6177 0 : if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE"))
6178 : {
6179 0 : bIsWGS84 = TRUE;
6180 0 : oSRS.SetWellKnownGeogCS("WGS84");
6181 : }
6182 0 : else if (EQUAL(pszDatum, "NAR") || STARTS_WITH_CI(pszDatum, "NAR-"))
6183 : {
6184 0 : bIsNAD83 = TRUE;
6185 0 : oSRS.SetWellKnownGeogCS("NAD83");
6186 : }
6187 0 : else if (EQUAL(pszDatum, "NAS") || STARTS_WITH_CI(pszDatum, "NAS-"))
6188 : {
6189 : /* bIsNAD27 = TRUE; */
6190 0 : oSRS.SetWellKnownGeogCS("NAD27");
6191 : }
6192 0 : else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */
6193 : {
6194 0 : oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/,
6195 : "unknown" /*const char * pszDatumName */,
6196 : "International 1924", 6378388, 297);
6197 0 : oSRS.SetTOWGS84(-333, -222, 114);
6198 : }
6199 0 : else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */
6200 : {
6201 0 : oSRS.importFromEPSG(4131);
6202 : }
6203 0 : else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */
6204 : {
6205 0 : oSRS.importFromEPSG(4283);
6206 : }
6207 0 : else if (STARTS_WITH_CI(pszDatum, "OHA-")) /* Old Hawaiian */
6208 : {
6209 0 : oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */
6210 0 : if (!EQUAL(pszDatum, "OHA-M"))
6211 : {
6212 0 : CPLError(CE_Warning, CPLE_AppDefined,
6213 : "Using OHA-M (Old Hawaiian Mean) definition for "
6214 : "%s. Potential issue with datum shift parameters",
6215 : pszDatum);
6216 0 : OGR_SRSNode *poNode = oSRS.GetRoot();
6217 0 : int iChild = poNode->FindChild("AUTHORITY");
6218 0 : if (iChild != -1)
6219 0 : poNode->DestroyChild(iChild);
6220 0 : iChild = poNode->FindChild("DATUM");
6221 0 : if (iChild != -1)
6222 : {
6223 0 : poNode = poNode->GetChild(iChild);
6224 0 : iChild = poNode->FindChild("AUTHORITY");
6225 0 : if (iChild != -1)
6226 0 : poNode->DestroyChild(iChild);
6227 : }
6228 : }
6229 : }
6230 : else
6231 : {
6232 0 : CPLError(CE_Warning, CPLE_AppDefined,
6233 : "Unhandled (yet) value for Datum : %s. Defaulting to "
6234 : "WGS84...",
6235 : pszDatum);
6236 0 : oSRS.SetGeogCS("unknown" /*const char * pszGeogName*/,
6237 : "unknown" /*const char * pszDatumName */,
6238 : "unknown", 6378137, 298.257223563);
6239 : }
6240 : }
6241 0 : else if (poDatum->GetType() == PDFObjectType_Dictionary)
6242 : {
6243 0 : GDALPDFDictionary *poDatumDict = poDatum->GetDictionary();
6244 :
6245 0 : GDALPDFObject *poDatumDescription = poDatumDict->Get("Description");
6246 0 : const char *pszDatumDescription = "unknown";
6247 0 : if (poDatumDescription != nullptr &&
6248 0 : poDatumDescription->GetType() == PDFObjectType_String)
6249 0 : pszDatumDescription = poDatumDescription->GetString().c_str();
6250 0 : CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription);
6251 :
6252 0 : GDALPDFObject *poEllipsoid = poDatumDict->Get("Ellipsoid");
6253 0 : if (poEllipsoid == nullptr ||
6254 0 : !(poEllipsoid->GetType() == PDFObjectType_String ||
6255 0 : poEllipsoid->GetType() == PDFObjectType_Dictionary))
6256 : {
6257 0 : CPLError(
6258 : CE_Warning, CPLE_AppDefined,
6259 : "Cannot find Ellipsoid in Datum. Defaulting to WGS84...");
6260 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, "unknown",
6261 : 6378137, 298.257223563);
6262 : }
6263 0 : else if (poEllipsoid->GetType() == PDFObjectType_String)
6264 : {
6265 0 : const char *pszEllipsoid = poEllipsoid->GetString().c_str();
6266 0 : CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid);
6267 0 : if (EQUAL(pszEllipsoid, "WE"))
6268 : {
6269 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, "WGS 84",
6270 : 6378137, 298.257223563);
6271 : }
6272 : else
6273 : {
6274 0 : CPLError(CE_Warning, CPLE_AppDefined,
6275 : "Unhandled (yet) value for Ellipsoid : %s. "
6276 : "Defaulting to WGS84...",
6277 : pszEllipsoid);
6278 0 : oSRS.SetGeogCS("unknown", pszDatumDescription, pszEllipsoid,
6279 : 6378137, 298.257223563);
6280 : }
6281 : }
6282 : else // if (poEllipsoid->GetType() == PDFObjectType_Dictionary)
6283 : {
6284 : GDALPDFDictionary *poEllipsoidDict =
6285 0 : poEllipsoid->GetDictionary();
6286 :
6287 : GDALPDFObject *poEllipsoidDescription =
6288 0 : poEllipsoidDict->Get("Description");
6289 0 : const char *pszEllipsoidDescription = "unknown";
6290 0 : if (poEllipsoidDescription != nullptr &&
6291 0 : poEllipsoidDescription->GetType() == PDFObjectType_String)
6292 : pszEllipsoidDescription =
6293 0 : poEllipsoidDescription->GetString().c_str();
6294 0 : CPLDebug("PDF", "Datum.Ellipsoid.Description = %s",
6295 : pszEllipsoidDescription);
6296 :
6297 0 : double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis");
6298 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g",
6299 : dfSemiMajor);
6300 0 : double dfInvFlattening = -1.0;
6301 :
6302 0 : if (poEllipsoidDict->Get("InvFlattening"))
6303 : {
6304 0 : dfInvFlattening = Get(poEllipsoidDict, "InvFlattening");
6305 0 : CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g",
6306 : dfInvFlattening);
6307 : }
6308 0 : else if (poEllipsoidDict->Get("SemiMinorAxis"))
6309 : {
6310 0 : double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis");
6311 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g",
6312 : dfSemiMinor);
6313 : dfInvFlattening =
6314 0 : OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
6315 : }
6316 :
6317 0 : if (dfSemiMajor != 0.0 && dfInvFlattening != -1.0)
6318 : {
6319 0 : oSRS.SetGeogCS("unknown", pszDatumDescription,
6320 : pszEllipsoidDescription, dfSemiMajor,
6321 : dfInvFlattening);
6322 : }
6323 : else
6324 : {
6325 0 : CPLError(
6326 : CE_Warning, CPLE_AppDefined,
6327 : "Invalid Ellipsoid object. Defaulting to WGS84...");
6328 0 : oSRS.SetGeogCS("unknown", pszDatumDescription,
6329 : pszEllipsoidDescription, 6378137,
6330 : 298.257223563);
6331 : }
6332 : }
6333 :
6334 0 : GDALPDFObject *poTOWGS84 = poDatumDict->Get("ToWGS84");
6335 0 : if (poTOWGS84 != nullptr &&
6336 0 : poTOWGS84->GetType() == PDFObjectType_Dictionary)
6337 : {
6338 0 : GDALPDFDictionary *poTOWGS84Dict = poTOWGS84->GetDictionary();
6339 0 : double dx = Get(poTOWGS84Dict, "dx");
6340 0 : double dy = Get(poTOWGS84Dict, "dy");
6341 0 : double dz = Get(poTOWGS84Dict, "dz");
6342 0 : if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") &&
6343 0 : poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf"))
6344 : {
6345 0 : double rx = Get(poTOWGS84Dict, "rx");
6346 0 : double ry = Get(poTOWGS84Dict, "ry");
6347 0 : double rz = Get(poTOWGS84Dict, "rz");
6348 0 : double sf = Get(poTOWGS84Dict, "sf");
6349 0 : oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf);
6350 : }
6351 : else
6352 : {
6353 0 : oSRS.SetTOWGS84(dx, dy, dz);
6354 : }
6355 : }
6356 : }
6357 : }
6358 :
6359 : /* -------------------------------------------------------------------- */
6360 : /* Extract Hemisphere attribute */
6361 : /* -------------------------------------------------------------------- */
6362 0 : CPLString osHemisphere;
6363 0 : GDALPDFObject *poHemisphere = poProjDict->Get("Hemisphere");
6364 0 : if (poHemisphere != nullptr &&
6365 0 : poHemisphere->GetType() == PDFObjectType_String)
6366 : {
6367 0 : osHemisphere = poHemisphere->GetString();
6368 : }
6369 :
6370 : /* -------------------------------------------------------------------- */
6371 : /* Extract ProjectionType attribute */
6372 : /* -------------------------------------------------------------------- */
6373 0 : GDALPDFObject *poProjectionType = poProjDict->Get("ProjectionType");
6374 0 : if (poProjectionType == nullptr ||
6375 0 : poProjectionType->GetType() != PDFObjectType_String)
6376 : {
6377 0 : CPLError(CE_Failure, CPLE_AppDefined,
6378 : "Cannot find ProjectionType of Projection object");
6379 0 : return FALSE;
6380 : }
6381 0 : CPLString osProjectionType(poProjectionType->GetString());
6382 0 : CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str());
6383 :
6384 : /* Unhandled: NONE, GEODETIC */
6385 :
6386 0 : if (EQUAL(osProjectionType, "GEOGRAPHIC"))
6387 : {
6388 : /* Nothing to do */
6389 : }
6390 :
6391 : /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */
6392 :
6393 0 : else if (EQUAL(osProjectionType, "UT")) /* UTM */
6394 : {
6395 0 : const double dfZone = Get(poProjDict, "Zone");
6396 0 : if (dfZone >= 1 && dfZone <= 60)
6397 : {
6398 0 : int nZone = static_cast<int>(dfZone);
6399 0 : int bNorth = EQUAL(osHemisphere, "N");
6400 0 : if (bIsWGS84)
6401 0 : oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone);
6402 : else
6403 0 : oSRS.SetUTM(nZone, bNorth);
6404 : }
6405 : }
6406 :
6407 0 : else if (EQUAL(osProjectionType,
6408 : "UP")) /* Universal Polar Stereographic (UPS) */
6409 : {
6410 0 : int bNorth = EQUAL(osHemisphere, "N");
6411 0 : if (bIsWGS84)
6412 0 : oSRS.importFromEPSG((bNorth) ? 32661 : 32761);
6413 : else
6414 0 : oSRS.SetPS((bNorth) ? 90 : -90, 0, 0.994, 200000, 200000);
6415 : }
6416 :
6417 0 : else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */
6418 : {
6419 0 : const double dfZone = Get(poProjDict, "Zone");
6420 0 : if (dfZone >= 0 && dfZone <= INT_MAX)
6421 : {
6422 0 : int nZone = static_cast<int>(dfZone);
6423 0 : oSRS.SetStatePlane(nZone, bIsNAD83);
6424 : }
6425 : }
6426 :
6427 0 : else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */
6428 : {
6429 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
6430 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
6431 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6432 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6433 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6434 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6435 0 : oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6436 : dfFalseEasting, dfFalseNorthing);
6437 : }
6438 :
6439 0 : else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */
6440 : {
6441 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6442 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6443 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6444 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6445 0 : oSRS.SetAE(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing);
6446 : }
6447 :
6448 0 : else if (EQUAL(osProjectionType, "BF")) /* Bonne */
6449 : {
6450 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
6451 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
6452 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6453 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6454 0 : oSRS.SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6455 : dfFalseNorthing);
6456 : }
6457 :
6458 0 : else if (EQUAL(osProjectionType, "CS")) /* Cassini */
6459 : {
6460 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6461 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6462 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6463 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6464 0 : oSRS.SetCS(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing);
6465 : }
6466 :
6467 0 : else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */
6468 : {
6469 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
6470 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
6471 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6472 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6473 0 : oSRS.SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6474 : dfFalseNorthing);
6475 : }
6476 :
6477 0 : else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */
6478 : {
6479 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
6480 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6481 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6482 0 : oSRS.SetEckertIV(dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6483 : }
6484 :
6485 0 : else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */
6486 : {
6487 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
6488 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6489 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6490 0 : oSRS.SetEckertVI(dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6491 : }
6492 :
6493 0 : else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */
6494 : {
6495 0 : double dfCenterLat = Get(poProjDict, "StandardParallel");
6496 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6497 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6498 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6499 0 : oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting,
6500 : dfFalseNorthing);
6501 : }
6502 :
6503 0 : else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */
6504 : {
6505 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6506 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6507 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6508 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6509 0 : oSRS.SetGnomonic(dfCenterLat, dfCenterLong, dfFalseEasting,
6510 : dfFalseNorthing);
6511 : }
6512 :
6513 0 : else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */
6514 : {
6515 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
6516 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
6517 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6518 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6519 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6520 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6521 0 : oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting,
6522 : dfFalseNorthing);
6523 : }
6524 :
6525 0 : else if (EQUAL(osProjectionType, "MC")) /* Mercator */
6526 : {
6527 : #ifdef not_supported
6528 : if (poProjDict->Get("StandardParallelOne") == nullptr)
6529 : #endif
6530 : {
6531 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6532 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6533 0 : double dfScale = Get(poProjDict, "ScaleFactor");
6534 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6535 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6536 0 : oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6537 : dfFalseNorthing);
6538 : }
6539 : #ifdef not_supported
6540 : else
6541 : {
6542 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
6543 : double dfCenterLat = poProjDict->Get("OriginLatitude")
6544 : ? Get(poProjDict, "OriginLatitude")
6545 : : 0;
6546 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6547 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6548 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6549 : oSRS.SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
6550 : dfFalseEasting, dfFalseNorthing);
6551 : }
6552 : #endif
6553 : }
6554 :
6555 0 : else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */
6556 : {
6557 0 : double dfCenterLat = 0 /* ? */;
6558 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6559 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6560 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6561 0 : oSRS.SetMC(dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing);
6562 : }
6563 :
6564 0 : else if (EQUAL(osProjectionType, "MP")) /* Mollweide */
6565 : {
6566 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
6567 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6568 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6569 0 : oSRS.SetMollweide(dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6570 : }
6571 :
6572 : /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */
6573 :
6574 0 : else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */
6575 : {
6576 : /* No parameter specified in the PDF, so let's take the ones of
6577 : * EPSG:27200 */
6578 0 : double dfCenterLat = -41;
6579 0 : double dfCenterLong = 173;
6580 0 : double dfFalseEasting = 2510000;
6581 0 : double dfFalseNorthing = 6023150;
6582 0 : oSRS.SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6583 : dfFalseNorthing);
6584 : }
6585 :
6586 0 : else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */
6587 : {
6588 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6589 0 : double dfLat1 = Get(poProjDict, "LatitudeOne");
6590 0 : double dfLong1 = Get(poProjDict, "LongitudeOne");
6591 0 : double dfLat2 = Get(poProjDict, "LatitudeTwo");
6592 0 : double dfLong2 = Get(poProjDict, "LongitudeTwo");
6593 0 : double dfScale = Get(poProjDict, "ScaleFactor");
6594 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6595 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6596 0 : oSRS.SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2, dfScale,
6597 : dfFalseEasting, dfFalseNorthing);
6598 : }
6599 :
6600 0 : else if (EQUAL(osProjectionType, "OD")) /* Orthographic */
6601 : {
6602 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6603 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6604 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6605 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6606 0 : oSRS.SetOrthographic(dfCenterLat, dfCenterLong, dfFalseEasting,
6607 : dfFalseNorthing);
6608 : }
6609 :
6610 0 : else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */
6611 : {
6612 0 : double dfCenterLat = Get(poProjDict, "LatitudeTrueScale");
6613 0 : double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole");
6614 0 : double dfScale = 1.0;
6615 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6616 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6617 0 : oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6618 : dfFalseNorthing);
6619 : }
6620 :
6621 0 : else if (EQUAL(osProjectionType, "PH")) /* Polyconic */
6622 : {
6623 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6624 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6625 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6626 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6627 0 : oSRS.SetPolyconic(dfCenterLat, dfCenterLong, dfFalseEasting,
6628 : dfFalseNorthing);
6629 : }
6630 :
6631 0 : else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */
6632 : {
6633 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6634 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6635 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6636 0 : oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing);
6637 : }
6638 :
6639 0 : else if (EQUAL(osProjectionType, "SD")) /* Stereographic */
6640 : {
6641 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6642 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6643 0 : double dfScale = 1.0;
6644 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6645 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6646 0 : oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale,
6647 : dfFalseEasting, dfFalseNorthing);
6648 : }
6649 :
6650 0 : else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */
6651 : {
6652 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
6653 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6654 0 : double dfScale = Get(poProjDict, "ScaleFactor");
6655 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6656 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6657 0 : if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfCenterLong >= -180 &&
6658 0 : dfCenterLong <= 180 && dfFalseEasting == 500000 &&
6659 0 : (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0))
6660 : {
6661 0 : const int nZone =
6662 0 : static_cast<int>(floor((dfCenterLong + 180.0) / 6.0) + 1);
6663 0 : int bNorth = dfFalseNorthing == 0;
6664 0 : if (bIsWGS84)
6665 0 : oSRS.importFromEPSG(((bNorth) ? 32600 : 32700) + nZone);
6666 0 : else if (bIsNAD83 && bNorth)
6667 0 : oSRS.importFromEPSG(26900 + nZone);
6668 : else
6669 0 : oSRS.SetUTM(nZone, bNorth);
6670 : }
6671 : else
6672 : {
6673 0 : oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6674 : dfFalseNorthing);
6675 : }
6676 : }
6677 :
6678 : /* Unhandled TX : Transverse Cylindrical Equal Area */
6679 :
6680 0 : else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */
6681 : {
6682 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
6683 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
6684 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
6685 0 : oSRS.SetVDG(dfCenterLong, dfFalseEasting, dfFalseNorthing);
6686 : }
6687 :
6688 : else
6689 : {
6690 0 : CPLError(CE_Failure, CPLE_AppDefined,
6691 : "Unhandled (yet) value for ProjectionType : %s",
6692 : osProjectionType.c_str());
6693 0 : return FALSE;
6694 : }
6695 :
6696 : /* -------------------------------------------------------------------- */
6697 : /* Extract Units attribute */
6698 : /* -------------------------------------------------------------------- */
6699 0 : CPLString osUnits;
6700 0 : GDALPDFObject *poUnits = poProjDict->Get("Units");
6701 0 : if (poUnits != nullptr && poUnits->GetType() == PDFObjectType_String &&
6702 0 : !EQUAL(osProjectionType, "GEOGRAPHIC"))
6703 : {
6704 0 : osUnits = poUnits->GetString();
6705 0 : CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str());
6706 :
6707 : // This is super weird. The false easting/northing of the SRS
6708 : // are expressed in the unit, but the geotransform is expressed in
6709 : // meters. Hence this hack to have an equivalent SRS definition, but
6710 : // with linear units converted in meters.
6711 0 : if (EQUAL(osUnits, "M"))
6712 0 : oSRS.SetLinearUnits("Meter", 1.0);
6713 0 : else if (EQUAL(osUnits, "FT"))
6714 : {
6715 0 : oSRS.SetLinearUnits("foot", 0.3048);
6716 0 : oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0);
6717 : }
6718 0 : else if (EQUAL(osUnits, "USSF"))
6719 : {
6720 0 : oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV));
6721 0 : oSRS.SetLinearUnitsAndUpdateParameters("Meter", 1.0);
6722 : }
6723 : else
6724 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unhandled unit: %s",
6725 : osUnits.c_str());
6726 : }
6727 :
6728 : /* -------------------------------------------------------------------- */
6729 : /* Export SpatialRef */
6730 : /* -------------------------------------------------------------------- */
6731 0 : m_oSRS = std::move(oSRS);
6732 :
6733 0 : return TRUE;
6734 : }
6735 :
6736 : /************************************************************************/
6737 : /* ParseVP() */
6738 : /************************************************************************/
6739 :
6740 279 : int PDFDataset::ParseVP(GDALPDFObject *poVP, double dfMediaBoxWidth,
6741 : double dfMediaBoxHeight)
6742 : {
6743 : int i;
6744 :
6745 279 : if (poVP->GetType() != PDFObjectType_Array)
6746 0 : return FALSE;
6747 :
6748 279 : GDALPDFArray *poVPArray = poVP->GetArray();
6749 :
6750 279 : int nLength = poVPArray->GetLength();
6751 279 : CPLDebug("PDF", "VP length = %d", nLength);
6752 279 : if (nLength < 1)
6753 0 : return FALSE;
6754 :
6755 : /* -------------------------------------------------------------------- */
6756 : /* Find the largest BBox */
6757 : /* -------------------------------------------------------------------- */
6758 : const char *pszNeatlineToSelect =
6759 279 : GetOption(papszOpenOptions, "NEATLINE", "Map Layers");
6760 :
6761 279 : int iLargest = 0;
6762 279 : int iRequestedVP = -1;
6763 279 : double dfLargestArea = 0;
6764 :
6765 573 : for (i = 0; i < nLength; i++)
6766 : {
6767 294 : GDALPDFObject *poVPElt = poVPArray->Get(i);
6768 588 : if (poVPElt == nullptr ||
6769 294 : poVPElt->GetType() != PDFObjectType_Dictionary)
6770 : {
6771 0 : return FALSE;
6772 : }
6773 :
6774 294 : GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary();
6775 :
6776 294 : GDALPDFObject *poMeasure = poVPEltDict->Get("Measure");
6777 588 : if (poMeasure == nullptr ||
6778 294 : poMeasure->GetType() != PDFObjectType_Dictionary)
6779 : {
6780 0 : continue;
6781 : }
6782 : /* --------------------------------------------------------------------
6783 : */
6784 : /* Extract Subtype attribute */
6785 : /* --------------------------------------------------------------------
6786 : */
6787 294 : GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary();
6788 294 : GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype");
6789 294 : if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name)
6790 : {
6791 0 : continue;
6792 : }
6793 :
6794 294 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
6795 294 : if (!EQUAL(poSubtype->GetName().c_str(), "GEO"))
6796 : {
6797 0 : continue;
6798 : }
6799 :
6800 294 : GDALPDFObject *poName = poVPEltDict->Get("Name");
6801 294 : if (poName != nullptr && poName->GetType() == PDFObjectType_String)
6802 : {
6803 291 : CPLDebug("PDF", "Name = %s", poName->GetString().c_str());
6804 291 : if (EQUAL(poName->GetString().c_str(), pszNeatlineToSelect))
6805 : {
6806 0 : iRequestedVP = i;
6807 : }
6808 : }
6809 :
6810 294 : GDALPDFObject *poBBox = poVPEltDict->Get("BBox");
6811 294 : if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array)
6812 : {
6813 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object");
6814 0 : return FALSE;
6815 : }
6816 :
6817 294 : int nBboxLength = poBBox->GetArray()->GetLength();
6818 294 : if (nBboxLength != 4)
6819 : {
6820 0 : CPLError(CE_Failure, CPLE_AppDefined,
6821 : "Invalid length for Bbox object");
6822 0 : return FALSE;
6823 : }
6824 :
6825 : double adfBBox[4];
6826 294 : adfBBox[0] = Get(poBBox, 0);
6827 294 : adfBBox[1] = Get(poBBox, 1);
6828 294 : adfBBox[2] = Get(poBBox, 2);
6829 294 : adfBBox[3] = Get(poBBox, 3);
6830 294 : double dfArea =
6831 294 : fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]);
6832 294 : if (dfArea > dfLargestArea)
6833 : {
6834 279 : iLargest = i;
6835 279 : dfLargestArea = dfArea;
6836 : }
6837 : }
6838 :
6839 279 : if (nLength > 1)
6840 : {
6841 15 : CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest);
6842 : }
6843 :
6844 279 : GDALPDFObject *poVPElt = nullptr;
6845 :
6846 279 : if (iRequestedVP > -1)
6847 : {
6848 0 : CPLDebug("PDF", "Requested NEATLINE BBox in VP array is element %d",
6849 : iRequestedVP);
6850 0 : poVPElt = poVPArray->Get(iRequestedVP);
6851 : }
6852 : else
6853 : {
6854 279 : poVPElt = poVPArray->Get(iLargest);
6855 : }
6856 :
6857 279 : if (poVPElt == nullptr || poVPElt->GetType() != PDFObjectType_Dictionary)
6858 : {
6859 0 : return FALSE;
6860 : }
6861 :
6862 279 : GDALPDFDictionary *poVPEltDict = poVPElt->GetDictionary();
6863 :
6864 279 : GDALPDFObject *poBBox = poVPEltDict->Get("BBox");
6865 279 : if (poBBox == nullptr || poBBox->GetType() != PDFObjectType_Array)
6866 : {
6867 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Bbox object");
6868 0 : return FALSE;
6869 : }
6870 :
6871 279 : int nBboxLength = poBBox->GetArray()->GetLength();
6872 279 : if (nBboxLength != 4)
6873 : {
6874 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for Bbox object");
6875 0 : return FALSE;
6876 : }
6877 :
6878 279 : double dfULX = Get(poBBox, 0);
6879 279 : double dfULY = dfMediaBoxHeight - Get(poBBox, 1);
6880 279 : double dfLRX = Get(poBBox, 2);
6881 279 : double dfLRY = dfMediaBoxHeight - Get(poBBox, 3);
6882 :
6883 : /* -------------------------------------------------------------------- */
6884 : /* Extract Measure attribute */
6885 : /* -------------------------------------------------------------------- */
6886 279 : GDALPDFObject *poMeasure = poVPEltDict->Get("Measure");
6887 558 : if (poMeasure == nullptr ||
6888 279 : poMeasure->GetType() != PDFObjectType_Dictionary)
6889 : {
6890 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Measure object");
6891 0 : return FALSE;
6892 : }
6893 :
6894 279 : int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, dfULX,
6895 : dfULY, dfLRX, dfLRY);
6896 :
6897 : /* -------------------------------------------------------------------- */
6898 : /* Extract PointData attribute */
6899 : /* -------------------------------------------------------------------- */
6900 279 : GDALPDFObject *poPointData = poVPEltDict->Get("PtData");
6901 279 : if (poPointData != nullptr &&
6902 0 : poPointData->GetType() == PDFObjectType_Dictionary)
6903 : {
6904 0 : CPLDebug("PDF", "Found PointData");
6905 : }
6906 :
6907 279 : return bRet;
6908 : }
6909 :
6910 : /************************************************************************/
6911 : /* ParseMeasure() */
6912 : /************************************************************************/
6913 :
6914 279 : int PDFDataset::ParseMeasure(GDALPDFObject *poMeasure, double dfMediaBoxWidth,
6915 : double dfMediaBoxHeight, double dfULX,
6916 : double dfULY, double dfLRX, double dfLRY)
6917 : {
6918 279 : GDALPDFDictionary *poMeasureDict = poMeasure->GetDictionary();
6919 :
6920 : /* -------------------------------------------------------------------- */
6921 : /* Extract Subtype attribute */
6922 : /* -------------------------------------------------------------------- */
6923 279 : GDALPDFObject *poSubtype = poMeasureDict->Get("Subtype");
6924 279 : if (poSubtype == nullptr || poSubtype->GetType() != PDFObjectType_Name)
6925 : {
6926 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Subtype object");
6927 0 : return FALSE;
6928 : }
6929 :
6930 279 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
6931 279 : if (!EQUAL(poSubtype->GetName().c_str(), "GEO"))
6932 0 : return FALSE;
6933 :
6934 : /* -------------------------------------------------------------------- */
6935 : /* Extract Bounds attribute (optional) */
6936 : /* -------------------------------------------------------------------- */
6937 :
6938 : /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf
6939 : */
6940 : /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */
6941 : /* LPTS, GPTS and Bounds. Use those ones */
6942 :
6943 279 : GDALPDFObject *poBounds = poMeasureDict->Get("lgit:Bounds");
6944 279 : if (poBounds != nullptr && poBounds->GetType() == PDFObjectType_Array)
6945 : {
6946 0 : CPLDebug("PDF", "Using lgit:Bounds");
6947 : }
6948 555 : else if ((poBounds = poMeasureDict->Get("Bounds")) == nullptr ||
6949 276 : poBounds->GetType() != PDFObjectType_Array)
6950 : {
6951 3 : poBounds = nullptr;
6952 : }
6953 :
6954 279 : if (poBounds != nullptr)
6955 : {
6956 276 : int nBoundsLength = poBounds->GetArray()->GetLength();
6957 276 : if (nBoundsLength == 8)
6958 : {
6959 : double adfBounds[8];
6960 2322 : for (int i = 0; i < 8; i++)
6961 : {
6962 2064 : adfBounds[i] = Get(poBounds, i);
6963 2064 : CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
6964 : }
6965 :
6966 : // TODO we should use it to restrict the neatline but
6967 : // I have yet to set a sample where bounds are not the four
6968 : // corners of the unit square.
6969 : }
6970 : }
6971 :
6972 : /* -------------------------------------------------------------------- */
6973 : /* Extract GPTS attribute */
6974 : /* -------------------------------------------------------------------- */
6975 279 : GDALPDFObject *poGPTS = poMeasureDict->Get("lgit:GPTS");
6976 279 : if (poGPTS != nullptr && poGPTS->GetType() == PDFObjectType_Array)
6977 : {
6978 0 : CPLDebug("PDF", "Using lgit:GPTS");
6979 : }
6980 558 : else if ((poGPTS = poMeasureDict->Get("GPTS")) == nullptr ||
6981 279 : poGPTS->GetType() != PDFObjectType_Array)
6982 : {
6983 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GPTS object");
6984 0 : return FALSE;
6985 : }
6986 :
6987 279 : int nGPTSLength = poGPTS->GetArray()->GetLength();
6988 279 : if ((nGPTSLength % 2) != 0 || nGPTSLength < 6)
6989 : {
6990 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for GPTS object");
6991 0 : return FALSE;
6992 : }
6993 :
6994 558 : std::vector<double> adfGPTS(nGPTSLength);
6995 2511 : for (int i = 0; i < nGPTSLength; i++)
6996 : {
6997 2232 : adfGPTS[i] = Get(poGPTS, i);
6998 2232 : CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
6999 : }
7000 :
7001 : /* -------------------------------------------------------------------- */
7002 : /* Extract LPTS attribute */
7003 : /* -------------------------------------------------------------------- */
7004 279 : GDALPDFObject *poLPTS = poMeasureDict->Get("lgit:LPTS");
7005 279 : if (poLPTS != nullptr && poLPTS->GetType() == PDFObjectType_Array)
7006 : {
7007 0 : CPLDebug("PDF", "Using lgit:LPTS");
7008 : }
7009 558 : else if ((poLPTS = poMeasureDict->Get("LPTS")) == nullptr ||
7010 279 : poLPTS->GetType() != PDFObjectType_Array)
7011 : {
7012 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find LPTS object");
7013 0 : return FALSE;
7014 : }
7015 :
7016 279 : int nLPTSLength = poLPTS->GetArray()->GetLength();
7017 279 : if (nLPTSLength != nGPTSLength)
7018 : {
7019 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length for LPTS object");
7020 0 : return FALSE;
7021 : }
7022 :
7023 558 : std::vector<double> adfLPTS(nLPTSLength);
7024 2511 : for (int i = 0; i < nLPTSLength; i++)
7025 : {
7026 2232 : adfLPTS[i] = Get(poLPTS, i);
7027 2232 : CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
7028 : }
7029 :
7030 : /* -------------------------------------------------------------------- */
7031 : /* Extract GCS attribute */
7032 : /* -------------------------------------------------------------------- */
7033 279 : GDALPDFObject *poGCS = poMeasureDict->Get("GCS");
7034 279 : if (poGCS == nullptr || poGCS->GetType() != PDFObjectType_Dictionary)
7035 : {
7036 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS object");
7037 0 : return FALSE;
7038 : }
7039 :
7040 279 : GDALPDFDictionary *poGCSDict = poGCS->GetDictionary();
7041 :
7042 : /* -------------------------------------------------------------------- */
7043 : /* Extract GCS.Type attribute */
7044 : /* -------------------------------------------------------------------- */
7045 279 : GDALPDFObject *poGCSType = poGCSDict->Get("Type");
7046 279 : if (poGCSType == nullptr || poGCSType->GetType() != PDFObjectType_Name)
7047 : {
7048 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GCS.Type object");
7049 0 : return FALSE;
7050 : }
7051 :
7052 279 : CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str());
7053 :
7054 : /* -------------------------------------------------------------------- */
7055 : /* Extract EPSG attribute */
7056 : /* -------------------------------------------------------------------- */
7057 279 : GDALPDFObject *poEPSG = poGCSDict->Get("EPSG");
7058 279 : int nEPSGCode = 0;
7059 279 : if (poEPSG != nullptr && poEPSG->GetType() == PDFObjectType_Int)
7060 : {
7061 237 : nEPSGCode = poEPSG->GetInt();
7062 237 : CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode);
7063 : }
7064 :
7065 : /* -------------------------------------------------------------------- */
7066 : /* Extract GCS.WKT attribute */
7067 : /* -------------------------------------------------------------------- */
7068 279 : GDALPDFObject *poGCSWKT = poGCSDict->Get("WKT");
7069 279 : if (poGCSWKT != nullptr && poGCSWKT->GetType() != PDFObjectType_String)
7070 : {
7071 0 : poGCSWKT = nullptr;
7072 : }
7073 :
7074 279 : if (poGCSWKT != nullptr)
7075 276 : CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str());
7076 :
7077 279 : if (nEPSGCode <= 0 && poGCSWKT == nullptr)
7078 : {
7079 0 : CPLError(CE_Failure, CPLE_AppDefined,
7080 : "Cannot find GCS.WKT or GCS.EPSG objects");
7081 0 : return FALSE;
7082 : }
7083 :
7084 279 : if (poGCSWKT != nullptr)
7085 : {
7086 276 : m_oSRS.importFromWkt(poGCSWKT->GetString().c_str());
7087 : }
7088 :
7089 279 : bool bSRSOK = false;
7090 279 : if (nEPSGCode != 0)
7091 : {
7092 : // At time of writing EPSG CRS codes are <= 32767.
7093 : // The usual practice is that codes >= 100000 are in the ESRI namespace
7094 : // instead
7095 237 : if (nEPSGCode >= 100000)
7096 : {
7097 4 : CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler);
7098 4 : OGRSpatialReference oSRS_ESRI;
7099 2 : oSRS_ESRI.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
7100 2 : if (oSRS_ESRI.SetFromUserInput(CPLSPrintf("ESRI:%d", nEPSGCode)) ==
7101 : OGRERR_NONE)
7102 : {
7103 2 : bSRSOK = true;
7104 :
7105 : // Check consistency of ESRI:xxxx and WKT definitions
7106 2 : if (poGCSWKT != nullptr)
7107 : {
7108 3 : if (!m_oSRS.GetName() ||
7109 1 : (!EQUAL(oSRS_ESRI.GetName(), m_oSRS.GetName()) &&
7110 0 : !oSRS_ESRI.IsSame(&m_oSRS)))
7111 : {
7112 1 : CPLDebug("PDF",
7113 : "Definition from ESRI:%d and WKT=%s do not "
7114 : "match. Using WKT string",
7115 1 : nEPSGCode, poGCSWKT->GetString().c_str());
7116 1 : bSRSOK = false;
7117 : }
7118 : }
7119 2 : if (bSRSOK)
7120 : {
7121 1 : m_oSRS = std::move(oSRS_ESRI);
7122 : }
7123 : }
7124 : }
7125 235 : else if (m_oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
7126 : {
7127 235 : bSRSOK = true;
7128 : }
7129 : }
7130 :
7131 279 : if (!bSRSOK)
7132 : {
7133 43 : if (poGCSWKT == nullptr)
7134 : {
7135 0 : CPLError(CE_Failure, CPLE_AppDefined,
7136 : "Cannot resolve EPSG object, and GCS.WKT not found");
7137 0 : return FALSE;
7138 : }
7139 :
7140 43 : if (m_oSRS.importFromWkt(poGCSWKT->GetString().c_str()) != OGRERR_NONE)
7141 : {
7142 1 : m_oSRS.Clear();
7143 1 : return FALSE;
7144 : }
7145 : }
7146 :
7147 : /* -------------------------------------------------------------------- */
7148 : /* Compute geotransform */
7149 : /* -------------------------------------------------------------------- */
7150 278 : OGRSpatialReference *poSRSGeog = m_oSRS.CloneGeogCS();
7151 :
7152 : /* Files found at
7153 : * http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/
7154 : */
7155 : /* are in a PROJCS. However the coordinates in GPTS array are not in (lat,
7156 : * long) as required by the */
7157 : /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is
7158 : * able to understand that, */
7159 : /* so let's also try to do it with a heuristics. */
7160 :
7161 278 : bool bReproject = true;
7162 278 : if (m_oSRS.IsProjected())
7163 : {
7164 1025 : for (int i = 0; i < nGPTSLength / 2; i++)
7165 : {
7166 820 : if (fabs(adfGPTS[2 * i]) > 91 || fabs(adfGPTS[2 * i + 1]) > 361)
7167 : {
7168 0 : CPLDebug("PDF", "GPTS coordinates seems to be in (northing, "
7169 : "easting), which is non-standard");
7170 0 : bReproject = false;
7171 0 : break;
7172 : }
7173 : }
7174 : }
7175 :
7176 278 : OGRCoordinateTransformation *poCT = nullptr;
7177 278 : if (bReproject)
7178 : {
7179 278 : poCT = OGRCreateCoordinateTransformation(poSRSGeog, &m_oSRS);
7180 278 : if (poCT == nullptr)
7181 : {
7182 0 : delete poSRSGeog;
7183 0 : m_oSRS.Clear();
7184 0 : return FALSE;
7185 : }
7186 : }
7187 :
7188 556 : std::vector<GDAL_GCP> asGCPS(nGPTSLength / 2);
7189 :
7190 : /* Create NEATLINE */
7191 278 : OGRLinearRing *poRing = nullptr;
7192 278 : if (nGPTSLength == 8)
7193 : {
7194 278 : m_poNeatLine = new OGRPolygon();
7195 278 : poRing = new OGRLinearRing();
7196 278 : m_poNeatLine->addRingDirectly(poRing);
7197 : }
7198 :
7199 1390 : for (int i = 0; i < nGPTSLength / 2; i++)
7200 : {
7201 : /* We probably assume LPTS is 0 or 1 */
7202 2224 : asGCPS[i].dfGCPPixel =
7203 1112 : (dfULX * (1 - adfLPTS[2 * i + 0]) + dfLRX * adfLPTS[2 * i + 0]) /
7204 1112 : dfMediaBoxWidth * nRasterXSize;
7205 2224 : asGCPS[i].dfGCPLine =
7206 1112 : (dfULY * (1 - adfLPTS[2 * i + 1]) + dfLRY * adfLPTS[2 * i + 1]) /
7207 1112 : dfMediaBoxHeight * nRasterYSize;
7208 :
7209 1112 : double lat = adfGPTS[2 * i];
7210 1112 : double lon = adfGPTS[2 * i + 1];
7211 1112 : double x = lon;
7212 1112 : double y = lat;
7213 1112 : if (bReproject)
7214 : {
7215 1112 : if (!poCT->Transform(1, &x, &y, nullptr))
7216 : {
7217 0 : CPLError(CE_Failure, CPLE_AppDefined,
7218 : "Cannot reproject (%f, %f)", lon, lat);
7219 0 : delete poSRSGeog;
7220 0 : delete poCT;
7221 0 : m_oSRS.Clear();
7222 0 : return FALSE;
7223 : }
7224 : }
7225 :
7226 1112 : x = ROUND_IF_CLOSE(x);
7227 1112 : y = ROUND_IF_CLOSE(y);
7228 :
7229 1112 : asGCPS[i].dfGCPX = x;
7230 1112 : asGCPS[i].dfGCPY = y;
7231 :
7232 1112 : if (poRing)
7233 1112 : poRing->addPoint(x, y);
7234 : }
7235 :
7236 278 : delete poSRSGeog;
7237 278 : delete poCT;
7238 :
7239 278 : if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(),
7240 : m_adfGeoTransform.data(), FALSE))
7241 : {
7242 2 : CPLDebug("PDF",
7243 : "Could not compute GT with exact match. Try with approximate");
7244 2 : if (!GDALGCPsToGeoTransform(nGPTSLength / 2, asGCPS.data(),
7245 : m_adfGeoTransform.data(), TRUE))
7246 : {
7247 0 : CPLError(CE_Failure, CPLE_AppDefined,
7248 : "Could not compute GT with approximate match.");
7249 0 : return FALSE;
7250 : }
7251 : }
7252 278 : m_bGeoTransformValid = true;
7253 :
7254 : // If the non scaling terms of the geotransform are significantly smaller
7255 : // than the pixel size, then nullify them as being just artifacts of
7256 : // reprojection and GDALGCPsToGeoTransform() numerical imprecisions.
7257 : const double dfPixelSize =
7258 278 : std::min(fabs(m_adfGeoTransform[1]), fabs(m_adfGeoTransform[5]));
7259 : const double dfRotationShearTerm =
7260 278 : std::max(fabs(m_adfGeoTransform[2]), fabs(m_adfGeoTransform[4]));
7261 364 : if (dfRotationShearTerm < 1e-5 * dfPixelSize ||
7262 86 : (m_bUseLib.test(PDFLIB_PDFIUM) &&
7263 359 : std::min(fabs(m_adfGeoTransform[2]), fabs(m_adfGeoTransform[4])) <
7264 81 : 1e-5 * dfPixelSize))
7265 : {
7266 259 : dfLRX = m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1] +
7267 259 : nRasterYSize * m_adfGeoTransform[2];
7268 259 : dfLRY = m_adfGeoTransform[3] + nRasterXSize * m_adfGeoTransform[4] +
7269 259 : nRasterYSize * m_adfGeoTransform[5];
7270 259 : m_adfGeoTransform[1] = (dfLRX - m_adfGeoTransform[0]) / nRasterXSize;
7271 259 : m_adfGeoTransform[5] = (dfLRY - m_adfGeoTransform[3]) / nRasterYSize;
7272 259 : m_adfGeoTransform[2] = m_adfGeoTransform[4] = 0;
7273 : }
7274 :
7275 278 : return TRUE;
7276 : }
7277 :
7278 : /************************************************************************/
7279 : /* GetSpatialRef() */
7280 : /************************************************************************/
7281 :
7282 255 : const OGRSpatialReference *PDFDataset::GetSpatialRef() const
7283 : {
7284 255 : const auto poSRS = GDALPamDataset::GetSpatialRef();
7285 255 : if (poSRS)
7286 6 : return poSRS;
7287 :
7288 249 : if (!m_oSRS.IsEmpty() && m_bGeoTransformValid)
7289 247 : return &m_oSRS;
7290 2 : return nullptr;
7291 : }
7292 :
7293 : /************************************************************************/
7294 : /* GetGeoTransform() */
7295 : /************************************************************************/
7296 :
7297 47 : CPLErr PDFDataset::GetGeoTransform(double *padfTransform)
7298 :
7299 : {
7300 47 : if (GDALPamDataset::GetGeoTransform(padfTransform) == CE_None)
7301 : {
7302 6 : return CE_None;
7303 : }
7304 :
7305 41 : std::copy(std::begin(m_adfGeoTransform), std::end(m_adfGeoTransform),
7306 : padfTransform);
7307 :
7308 41 : return ((m_bGeoTransformValid) ? CE_None : CE_Failure);
7309 : }
7310 :
7311 : /************************************************************************/
7312 : /* SetSpatialRef() */
7313 : /************************************************************************/
7314 :
7315 11 : CPLErr PDFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
7316 : {
7317 11 : if (eAccess == GA_ReadOnly)
7318 4 : GDALPamDataset::SetSpatialRef(poSRS);
7319 :
7320 11 : m_oSRS.Clear();
7321 11 : if (poSRS)
7322 9 : m_oSRS = *poSRS;
7323 11 : m_bProjDirty = true;
7324 11 : return CE_None;
7325 : }
7326 :
7327 : /************************************************************************/
7328 : /* SetGeoTransform() */
7329 : /************************************************************************/
7330 :
7331 9 : CPLErr PDFDataset::SetGeoTransform(double *padfGeoTransform)
7332 : {
7333 9 : if (eAccess == GA_ReadOnly)
7334 4 : GDALPamDataset::SetGeoTransform(padfGeoTransform);
7335 :
7336 9 : std::copy(padfGeoTransform, padfGeoTransform + 6,
7337 9 : std::begin(m_adfGeoTransform));
7338 9 : m_bGeoTransformValid = true;
7339 9 : m_bProjDirty = true;
7340 :
7341 : /* Reset NEATLINE if not explicitly set by the user */
7342 9 : if (!m_bNeatLineDirty)
7343 9 : SetMetadataItem("NEATLINE", nullptr);
7344 9 : return CE_None;
7345 : }
7346 :
7347 : /************************************************************************/
7348 : /* GetMetadataDomainList() */
7349 : /************************************************************************/
7350 :
7351 1 : char **PDFDataset::GetMetadataDomainList()
7352 : {
7353 1 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
7354 : TRUE, "xml:XMP", "LAYERS",
7355 1 : "EMBEDDED_METADATA", nullptr);
7356 : }
7357 :
7358 : /************************************************************************/
7359 : /* GetMetadata() */
7360 : /************************************************************************/
7361 :
7362 2087 : char **PDFDataset::GetMetadata(const char *pszDomain)
7363 : {
7364 2087 : if (pszDomain != nullptr && EQUAL(pszDomain, "EMBEDDED_METADATA"))
7365 : {
7366 1 : char **papszRet = m_oMDMD_PDF.GetMetadata(pszDomain);
7367 1 : if (papszRet)
7368 0 : return papszRet;
7369 :
7370 1 : GDALPDFObject *poCatalog = GetCatalog();
7371 1 : if (poCatalog == nullptr)
7372 0 : return nullptr;
7373 : GDALPDFObject *poFirstElt =
7374 1 : poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]");
7375 : GDALPDFObject *poF =
7376 1 : poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F");
7377 :
7378 1 : if (poFirstElt == nullptr ||
7379 1 : poFirstElt->GetType() != PDFObjectType_String ||
7380 0 : poFirstElt->GetString() != "Metadata")
7381 1 : return nullptr;
7382 0 : if (poF == nullptr || poF->GetType() != PDFObjectType_Dictionary)
7383 0 : return nullptr;
7384 0 : GDALPDFStream *poStream = poF->GetStream();
7385 0 : if (poStream == nullptr)
7386 0 : return nullptr;
7387 :
7388 0 : char *apszMetadata[2] = {nullptr, nullptr};
7389 0 : apszMetadata[0] = poStream->GetBytes();
7390 0 : m_oMDMD_PDF.SetMetadata(apszMetadata, pszDomain);
7391 0 : VSIFree(apszMetadata[0]);
7392 0 : return m_oMDMD_PDF.GetMetadata(pszDomain);
7393 : }
7394 2086 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7395 : {
7396 217 : char **papszPAMMD = GDALPamDataset::GetMetadata(pszDomain);
7397 222 : for (char **papszIter = papszPAMMD; papszIter && *papszIter;
7398 : ++papszIter)
7399 : {
7400 5 : char *pszKey = nullptr;
7401 5 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
7402 5 : if (pszKey && pszValue)
7403 : {
7404 5 : if (m_oMDMD_PDF.GetMetadataItem(pszKey, pszDomain) == nullptr)
7405 4 : m_oMDMD_PDF.SetMetadataItem(pszKey, pszValue, pszDomain);
7406 : }
7407 5 : CPLFree(pszKey);
7408 : }
7409 217 : return m_oMDMD_PDF.GetMetadata(pszDomain);
7410 : }
7411 1869 : if (EQUAL(pszDomain, "LAYERS") || EQUAL(pszDomain, "xml:XMP") ||
7412 1821 : EQUAL(pszDomain, "SUBDATASETS"))
7413 : {
7414 50 : return m_oMDMD_PDF.GetMetadata(pszDomain);
7415 : }
7416 1819 : return GDALPamDataset::GetMetadata(pszDomain);
7417 : }
7418 :
7419 : /************************************************************************/
7420 : /* SetMetadata() */
7421 : /************************************************************************/
7422 :
7423 129 : CPLErr PDFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
7424 : {
7425 129 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7426 : {
7427 83 : char **papszMetadataDup = CSLDuplicate(papszMetadata);
7428 83 : m_oMDMD_PDF.SetMetadata(nullptr, pszDomain);
7429 :
7430 259 : for (char **papszIter = papszMetadataDup; papszIter && *papszIter;
7431 : ++papszIter)
7432 : {
7433 176 : char *pszKey = nullptr;
7434 176 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
7435 176 : if (pszKey && pszValue)
7436 : {
7437 173 : SetMetadataItem(pszKey, pszValue, pszDomain);
7438 : }
7439 176 : CPLFree(pszKey);
7440 : }
7441 83 : CSLDestroy(papszMetadataDup);
7442 83 : return CE_None;
7443 : }
7444 46 : else if (EQUAL(pszDomain, "xml:XMP"))
7445 : {
7446 42 : m_bXMPDirty = true;
7447 42 : return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain);
7448 : }
7449 4 : else if (EQUAL(pszDomain, "SUBDATASETS"))
7450 : {
7451 4 : return m_oMDMD_PDF.SetMetadata(papszMetadata, pszDomain);
7452 : }
7453 : else
7454 : {
7455 0 : return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
7456 : }
7457 : }
7458 :
7459 : /************************************************************************/
7460 : /* GetMetadataItem() */
7461 : /************************************************************************/
7462 :
7463 1921 : const char *PDFDataset::GetMetadataItem(const char *pszName,
7464 : const char *pszDomain)
7465 : {
7466 1921 : if (pszDomain != nullptr && EQUAL(pszDomain, "_INTERNAL_") &&
7467 0 : pszName != nullptr && EQUAL(pszName, "PDF_LIB"))
7468 : {
7469 0 : if (m_bUseLib.test(PDFLIB_POPPLER))
7470 0 : return "POPPLER";
7471 0 : if (m_bUseLib.test(PDFLIB_PODOFO))
7472 0 : return "PODOFO";
7473 0 : if (m_bUseLib.test(PDFLIB_PDFIUM))
7474 0 : return "PDFIUM";
7475 : }
7476 1921 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
7477 : }
7478 :
7479 : /************************************************************************/
7480 : /* SetMetadataItem() */
7481 : /************************************************************************/
7482 :
7483 1323 : CPLErr PDFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
7484 : const char *pszDomain)
7485 : {
7486 1323 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
7487 : {
7488 1221 : if (EQUAL(pszName, "NEATLINE"))
7489 : {
7490 : const char *pszOldValue =
7491 349 : m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain);
7492 349 : if ((pszValue == nullptr && pszOldValue != nullptr) ||
7493 346 : (pszValue != nullptr && pszOldValue == nullptr) ||
7494 2 : (pszValue != nullptr && pszOldValue != nullptr &&
7495 2 : strcmp(pszValue, pszOldValue) != 0))
7496 : {
7497 341 : m_bProjDirty = true;
7498 341 : m_bNeatLineDirty = true;
7499 : }
7500 349 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7501 : }
7502 : else
7503 : {
7504 872 : if (EQUAL(pszName, "AUTHOR") || EQUAL(pszName, "PRODUCER") ||
7505 829 : EQUAL(pszName, "CREATOR") || EQUAL(pszName, "CREATION_DATE") ||
7506 739 : EQUAL(pszName, "SUBJECT") || EQUAL(pszName, "TITLE") ||
7507 704 : EQUAL(pszName, "KEYWORDS"))
7508 : {
7509 180 : if (pszValue == nullptr)
7510 2 : pszValue = "";
7511 : const char *pszOldValue =
7512 180 : m_oMDMD_PDF.GetMetadataItem(pszName, pszDomain);
7513 180 : if (pszOldValue == nullptr ||
7514 4 : strcmp(pszValue, pszOldValue) != 0)
7515 : {
7516 180 : m_bInfoDirty = true;
7517 : }
7518 180 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue,
7519 180 : pszDomain);
7520 : }
7521 692 : else if (EQUAL(pszName, "DPI"))
7522 : {
7523 689 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue,
7524 689 : pszDomain);
7525 : }
7526 : else
7527 : {
7528 3 : m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7529 3 : return GDALPamDataset::SetMetadataItem(pszName, pszValue,
7530 3 : pszDomain);
7531 : }
7532 : }
7533 : }
7534 102 : else if (EQUAL(pszDomain, "xml:XMP"))
7535 : {
7536 0 : m_bXMPDirty = true;
7537 0 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7538 : }
7539 102 : else if (EQUAL(pszDomain, "SUBDATASETS"))
7540 : {
7541 0 : return m_oMDMD_PDF.SetMetadataItem(pszName, pszValue, pszDomain);
7542 : }
7543 : else
7544 : {
7545 102 : return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
7546 : }
7547 : }
7548 :
7549 : /************************************************************************/
7550 : /* GetGCPCount() */
7551 : /************************************************************************/
7552 :
7553 21 : int PDFDataset::GetGCPCount()
7554 : {
7555 21 : return m_nGCPCount;
7556 : }
7557 :
7558 : /************************************************************************/
7559 : /* GetGCPSpatialRef() */
7560 : /************************************************************************/
7561 :
7562 4 : const OGRSpatialReference *PDFDataset::GetGCPSpatialRef() const
7563 : {
7564 4 : if (!m_oSRS.IsEmpty() && m_nGCPCount != 0)
7565 2 : return &m_oSRS;
7566 2 : return nullptr;
7567 : }
7568 :
7569 : /************************************************************************/
7570 : /* GetGCPs() */
7571 : /************************************************************************/
7572 :
7573 4 : const GDAL_GCP *PDFDataset::GetGCPs()
7574 : {
7575 4 : return m_pasGCPList;
7576 : }
7577 :
7578 : /************************************************************************/
7579 : /* SetGCPs() */
7580 : /************************************************************************/
7581 :
7582 2 : CPLErr PDFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
7583 : const OGRSpatialReference *poSRS)
7584 : {
7585 : const char *pszGEO_ENCODING =
7586 2 : CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
7587 2 : if (nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000"))
7588 : {
7589 0 : CPLError(CE_Failure, CPLE_NotSupported,
7590 : "PDF driver only supports writing 4 GCPs when "
7591 : "GDAL_PDF_GEO_ENCODING=ISO32000.");
7592 0 : return CE_Failure;
7593 : }
7594 :
7595 : /* Free previous GCPs */
7596 2 : GDALDeinitGCPs(m_nGCPCount, m_pasGCPList);
7597 2 : CPLFree(m_pasGCPList);
7598 :
7599 : /* Duplicate in GCPs */
7600 2 : m_nGCPCount = nGCPCountIn;
7601 2 : m_pasGCPList = GDALDuplicateGCPs(m_nGCPCount, pasGCPListIn);
7602 :
7603 2 : m_oSRS.Clear();
7604 2 : if (poSRS)
7605 2 : m_oSRS = *poSRS;
7606 :
7607 2 : m_bProjDirty = true;
7608 :
7609 : /* Reset NEATLINE if not explicitly set by the user */
7610 2 : if (!m_bNeatLineDirty)
7611 2 : SetMetadataItem("NEATLINE", nullptr);
7612 :
7613 2 : return CE_None;
7614 : }
7615 :
7616 : #endif // #ifdef HAVE_PDF_READ_SUPPORT
7617 :
7618 : /************************************************************************/
7619 : /* GDALPDFOpen() */
7620 : /************************************************************************/
7621 :
7622 90 : GDALDataset *GDALPDFOpen(
7623 : #ifdef HAVE_PDF_READ_SUPPORT
7624 : const char *pszFilename, GDALAccess eAccess
7625 : #else
7626 : CPL_UNUSED const char *pszFilename, CPL_UNUSED GDALAccess eAccess
7627 : #endif
7628 : )
7629 : {
7630 : #ifdef HAVE_PDF_READ_SUPPORT
7631 180 : GDALOpenInfo oOpenInfo(pszFilename, eAccess);
7632 180 : return PDFDataset::Open(&oOpenInfo);
7633 : #else
7634 : return nullptr;
7635 : #endif
7636 : }
7637 :
7638 : /************************************************************************/
7639 : /* GDALPDFUnloadDriver() */
7640 : /************************************************************************/
7641 :
7642 10 : static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver *poDriver)
7643 : {
7644 : #ifdef HAVE_POPPLER
7645 10 : if (hGlobalParamsMutex != nullptr)
7646 0 : CPLDestroyMutex(hGlobalParamsMutex);
7647 : #endif
7648 : #ifdef HAVE_PDFIUM
7649 10 : if (PDFDataset::g_bPdfiumInit)
7650 : {
7651 2 : CPLCreateOrAcquireMutex(&g_oPdfiumLoadDocMutex, PDFIUM_MUTEX_TIMEOUT);
7652 : // Destroy every loaded document or page
7653 2 : TMapPdfiumDatasets::iterator itDoc;
7654 2 : TMapPdfiumPages::iterator itPage;
7655 2 : for (itDoc = g_mPdfiumDatasets.begin();
7656 2 : itDoc != g_mPdfiumDatasets.end(); ++itDoc)
7657 : {
7658 0 : TPdfiumDocumentStruct *pDoc = itDoc->second;
7659 0 : for (itPage = pDoc->pages.begin(); itPage != pDoc->pages.end();
7660 0 : ++itPage)
7661 : {
7662 0 : TPdfiumPageStruct *pPage = itPage->second;
7663 :
7664 0 : CPLCreateOrAcquireMutex(&g_oPdfiumReadMutex,
7665 : PDFIUM_MUTEX_TIMEOUT);
7666 0 : CPLCreateOrAcquireMutex(&(pPage->readMutex),
7667 : PDFIUM_MUTEX_TIMEOUT);
7668 0 : CPLReleaseMutex(pPage->readMutex);
7669 0 : CPLDestroyMutex(pPage->readMutex);
7670 0 : FPDF_ClosePage(FPDFPageFromIPDFPage(pPage->page));
7671 0 : delete pPage;
7672 0 : CPLReleaseMutex(g_oPdfiumReadMutex);
7673 : } // ~ foreach page
7674 :
7675 0 : FPDF_CloseDocument(FPDFDocumentFromCPDFDocument(pDoc->doc));
7676 0 : CPLFree(pDoc->filename);
7677 0 : VSIFCloseL(static_cast<VSILFILE *>(pDoc->psFileAccess->m_Param));
7678 0 : delete pDoc->psFileAccess;
7679 0 : pDoc->pages.clear();
7680 :
7681 0 : delete pDoc;
7682 : } // ~ foreach document
7683 2 : g_mPdfiumDatasets.clear();
7684 2 : FPDF_DestroyLibrary();
7685 2 : PDFDataset::g_bPdfiumInit = FALSE;
7686 :
7687 2 : CPLReleaseMutex(g_oPdfiumLoadDocMutex);
7688 :
7689 2 : if (g_oPdfiumReadMutex)
7690 0 : CPLDestroyMutex(g_oPdfiumReadMutex);
7691 2 : CPLDestroyMutex(g_oPdfiumLoadDocMutex);
7692 : }
7693 : #endif
7694 10 : }
7695 :
7696 : /************************************************************************/
7697 : /* PDFSanitizeLayerName() */
7698 : /************************************************************************/
7699 :
7700 827 : CPLString PDFSanitizeLayerName(const char *pszName)
7701 : {
7702 827 : if (!CPLTestBool(CPLGetConfigOption("GDAL_PDF_LAUNDER_LAYER_NAMES", "YES")))
7703 0 : return pszName;
7704 :
7705 1654 : CPLString osName;
7706 18439 : for (int i = 0; pszName[i] != '\0'; i++)
7707 : {
7708 17612 : if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',')
7709 1021 : osName += "_";
7710 16591 : else if (pszName[i] != '"')
7711 16591 : osName += pszName[i];
7712 : }
7713 827 : if (osName.empty())
7714 3 : osName = "unnamed";
7715 827 : return osName;
7716 : }
7717 :
7718 : /************************************************************************/
7719 : /* GDALRegister_PDF() */
7720 : /************************************************************************/
7721 :
7722 16 : void GDALRegister_PDF()
7723 :
7724 : {
7725 16 : if (!GDAL_CHECK_VERSION("PDF driver"))
7726 0 : return;
7727 :
7728 16 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
7729 0 : return;
7730 :
7731 16 : GDALDriver *poDriver = new GDALDriver();
7732 16 : PDFDriverSetCommonMetadata(poDriver);
7733 :
7734 : #ifdef HAVE_PDF_READ_SUPPORT
7735 16 : poDriver->pfnOpen = PDFDataset::OpenWrapper;
7736 : #endif // HAVE_PDF_READ_SUPPORT
7737 :
7738 16 : poDriver->pfnCreateCopy = GDALPDFCreateCopy;
7739 16 : poDriver->pfnCreate = PDFWritableVectorDataset::Create;
7740 16 : poDriver->pfnUnloadDriver = GDALPDFUnloadDriver;
7741 :
7742 16 : GetGDALDriverManager()->RegisterDriver(poDriver);
7743 : }
|