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