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