1 : /******************************************************************************
2 : * $Id: pdfdataset.cpp 24144 2012-03-19 21:51:32Z rouault $
3 : *
4 : * Project: PDF driver
5 : * Purpose: GDALDataset driver for PDF dataset.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : /* hack for PDF driver and poppler >= 0.15.0 that defines incompatible "typedef bool GBool" */
31 : /* in include/poppler/goo/gtypes.h with the one defined in cpl_port.h */
32 : #define CPL_GBOOL_DEFINED
33 :
34 : #include "pdfdataset.h"
35 : #include "cpl_vsi_virtual.h"
36 : #include "cpl_string.h"
37 : #include "gdal_pam.h"
38 : #include "ogr_spatialref.h"
39 : #include "ogr_geometry.h"
40 :
41 : #ifdef HAVE_POPPLER
42 : #include "pdfio.h"
43 : #include <goo/GooList.h>
44 : #endif
45 :
46 : #include "pdfobject.h"
47 : #include "pdfcreatecopy.h"
48 :
49 : #include <set>
50 : #include <map>
51 :
52 : /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */
53 :
54 : CPL_CVSID("$Id: pdfdataset.cpp 24144 2012-03-19 21:51:32Z rouault $");
55 :
56 : CPL_C_START
57 : void GDALRegister_PDF(void);
58 : CPL_C_END
59 :
60 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
61 :
62 : static double Get(GDALPDFObject* poObj, int nIndice = -1);
63 :
64 : #ifdef HAVE_POPPLER
65 :
66 : /************************************************************************/
67 : /* ObjectAutoFree */
68 : /************************************************************************/
69 :
70 : class ObjectAutoFree : public Object
71 : {
72 : public:
73 332 : ObjectAutoFree() {}
74 332 : ~ObjectAutoFree() { free(); }
75 : };
76 :
77 :
78 : /************************************************************************/
79 : /* GDALPDFOutputDev */
80 : /************************************************************************/
81 :
82 : class GDALPDFOutputDev : public SplashOutputDev
83 98 : {
84 : private:
85 : int bEnableVector;
86 : int bEnableText;
87 : int bEnableBitmap;
88 :
89 0 : void skipBytes(Stream *str,
90 : int width, int height,
91 : int nComps, int nBits)
92 : {
93 0 : int nVals = width * nComps;
94 0 : int nLineSize = (nVals * nBits + 7) >> 3;
95 0 : int nBytes = nLineSize * height;
96 0 : for (int i = 0; i < nBytes; i++)
97 : {
98 0 : if( str->getChar() == EOF)
99 0 : break;
100 : }
101 0 : }
102 :
103 : public:
104 98 : GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
105 : GBool reverseVideoA, SplashColorPtr paperColorA,
106 : GBool bitmapTopDownA = gTrue,
107 : GBool allowAntialiasA = gTrue) :
108 : SplashOutputDev(colorModeA, bitmapRowPadA,
109 : reverseVideoA, paperColorA,
110 : bitmapTopDownA, allowAntialiasA),
111 : bEnableVector(TRUE),
112 : bEnableText(TRUE),
113 98 : bEnableBitmap(TRUE) {}
114 :
115 0 : void SetEnableVector(int bFlag) { bEnableVector = bFlag; }
116 0 : void SetEnableText(int bFlag) { bEnableText = bFlag; }
117 0 : void SetEnableBitmap(int bFlag) { bEnableBitmap = bFlag; }
118 :
119 98 : virtual void startPage(int pageNum, GfxState *state)
120 : {
121 98 : SplashOutputDev::startPage(pageNum, state);
122 98 : SplashBitmap* poBitmap = getBitmap();
123 98 : memset(poBitmap->getDataPtr(), 255, poBitmap->getRowSize() * poBitmap->getHeight());
124 98 : }
125 :
126 3244 : virtual void stroke(GfxState * state)
127 : {
128 3244 : if (bEnableVector)
129 3244 : SplashOutputDev::stroke(state);
130 3244 : }
131 :
132 0 : virtual void fill(GfxState * state)
133 : {
134 0 : if (bEnableVector)
135 0 : SplashOutputDev::fill(state);
136 0 : }
137 :
138 4 : virtual void eoFill(GfxState * state)
139 : {
140 4 : if (bEnableVector)
141 4 : SplashOutputDev::eoFill(state);
142 4 : }
143 :
144 8422 : virtual void drawChar(GfxState *state, double x, double y,
145 : double dx, double dy,
146 : double originX, double originY,
147 : CharCode code, int nBytes, Unicode *u, int uLen)
148 : {
149 8422 : if (bEnableText)
150 : SplashOutputDev::drawChar(state, x, y, dx, dy,
151 : originX, originY,
152 8422 : code, nBytes, u, uLen);
153 8422 : }
154 :
155 1348 : virtual void beginTextObject(GfxState *state)
156 : {
157 1348 : if (bEnableText)
158 1348 : SplashOutputDev::beginTextObject(state);
159 1348 : }
160 :
161 0 : virtual GBool deviceHasTextClip(GfxState *state)
162 : {
163 0 : if (bEnableText)
164 0 : return SplashOutputDev::deviceHasTextClip(state);
165 0 : return gFalse;
166 : }
167 :
168 1348 : virtual void endTextObject(GfxState *state)
169 : {
170 1348 : if (bEnableText)
171 1348 : SplashOutputDev::endTextObject(state);
172 1348 : }
173 :
174 0 : virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
175 : int width, int height, GBool invert,
176 : GBool interpolate, GBool inlineImg)
177 : {
178 0 : if (bEnableBitmap)
179 : SplashOutputDev::drawImageMask(state, ref, str,
180 : width, height, invert,
181 0 : interpolate, inlineImg);
182 : else
183 : {
184 0 : str->reset();
185 0 : if (inlineImg)
186 : {
187 0 : skipBytes(str, width, height, 1, 1);
188 : }
189 0 : str->close();
190 : }
191 0 : }
192 :
193 : #ifdef POPPLER_0_20_OR_LATER
194 0 : virtual void setSoftMaskFromImageMask(GfxState *state,
195 : Object *ref, Stream *str,
196 : int width, int height, GBool invert,
197 : GBool inlineImg)
198 : {
199 0 : if (bEnableBitmap)
200 : SplashOutputDev::setSoftMaskFromImageMask(state, ref, str,
201 : width, height, invert,
202 0 : inlineImg);
203 : else
204 0 : str->close();
205 0 : }
206 :
207 0 : virtual void unsetSoftMaskFromImageMask(GfxState *state)
208 : {
209 0 : if (bEnableBitmap)
210 0 : SplashOutputDev::unsetSoftMaskFromImageMask(state);
211 0 : }
212 : #endif
213 :
214 160 : virtual void drawImage(GfxState *state, Object *ref, Stream *str,
215 : int width, int height, GfxImageColorMap *colorMap,
216 : GBool interpolate, int *maskColors, GBool inlineImg)
217 : {
218 160 : if (bEnableBitmap)
219 : SplashOutputDev::drawImage(state, ref, str,
220 : width, height, colorMap,
221 160 : interpolate, maskColors, inlineImg);
222 : else
223 : {
224 0 : str->reset();
225 0 : if (inlineImg)
226 : {
227 : skipBytes(str, width, height,
228 : colorMap->getNumPixelComps(),
229 0 : colorMap->getBits());
230 : }
231 0 : str->close();
232 : }
233 160 : }
234 :
235 0 : virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
236 : int width, int height,
237 : GfxImageColorMap *colorMap,
238 : GBool interpolate,
239 : Stream *maskStr, int maskWidth, int maskHeight,
240 : GBool maskInvert, GBool maskInterpolate)
241 : {
242 0 : if (bEnableBitmap)
243 : SplashOutputDev::drawMaskedImage(state, ref, str,
244 : width, height, colorMap,
245 : interpolate,
246 : maskStr, maskWidth, maskHeight,
247 0 : maskInvert, maskInterpolate);
248 : else
249 0 : str->close();
250 0 : }
251 :
252 6 : virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
253 : int width, int height,
254 : GfxImageColorMap *colorMap,
255 : GBool interpolate,
256 : Stream *maskStr,
257 : int maskWidth, int maskHeight,
258 : GfxImageColorMap *maskColorMap,
259 : GBool maskInterpolate)
260 : {
261 6 : if (bEnableBitmap)
262 : SplashOutputDev::drawSoftMaskedImage(state, ref, str,
263 : width, height, colorMap,
264 : interpolate,
265 : maskStr, maskWidth, maskHeight,
266 6 : maskColorMap, maskInterpolate);
267 : else
268 0 : str->close();
269 6 : }
270 : };
271 :
272 : #endif
273 :
274 : /************************************************************************/
275 : /* Dump routines */
276 : /************************************************************************/
277 :
278 : class GDALPDFDumper
279 0 : {
280 : private:
281 : FILE* f;
282 : int nDepthLimit;
283 : std::set< int > aoSetObjectExplored;
284 : int bDumpParent;
285 :
286 : void DumpSimplified(GDALPDFObject* poObj);
287 :
288 : public:
289 0 : GDALPDFDumper(FILE* fIn, int nDepthLimitIn = -1) : f(fIn), nDepthLimit(nDepthLimitIn)
290 : {
291 0 : bDumpParent = CSLTestBoolean(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE"));
292 0 : }
293 :
294 : void Dump(GDALPDFObject* poObj, int nDepth = 0);
295 : void Dump(GDALPDFDictionary* poDict, int nDepth = 0);
296 : void Dump(GDALPDFArray* poArray, int nDepth = 0);
297 : };
298 :
299 0 : void GDALPDFDumper::Dump(GDALPDFArray* poArray, int nDepth)
300 : {
301 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
302 0 : return;
303 :
304 0 : int nLength = poArray->GetLength();
305 : int i;
306 0 : CPLString osIndent;
307 0 : for(i=0;i<nDepth;i++)
308 0 : osIndent += " ";
309 0 : for(i=0;i<nLength;i++)
310 : {
311 0 : fprintf(f, "%sItem[%d]:", osIndent.c_str(), i);
312 0 : GDALPDFObject* poObj = NULL;
313 0 : if ((poObj = poArray->Get(i)) != NULL)
314 : {
315 0 : if (poObj->GetType() == PDFObjectType_String ||
316 0 : poObj->GetType() == PDFObjectType_Null ||
317 0 : poObj->GetType() == PDFObjectType_Bool ||
318 0 : poObj->GetType() == PDFObjectType_Int ||
319 0 : poObj->GetType() == PDFObjectType_Real ||
320 0 : poObj->GetType() == PDFObjectType_Name)
321 : {
322 0 : fprintf(f, " ");
323 0 : DumpSimplified(poObj);
324 0 : fprintf(f, "\n");
325 : }
326 : else
327 : {
328 0 : fprintf(f, "\n");
329 0 : Dump( poObj, nDepth+1);
330 : }
331 : }
332 0 : }
333 : }
334 :
335 0 : void GDALPDFDumper::DumpSimplified(GDALPDFObject* poObj)
336 : {
337 0 : switch(poObj->GetType())
338 : {
339 : case PDFObjectType_String:
340 0 : fprintf(f, "%s (string)", poObj->GetString().c_str());
341 0 : break;
342 :
343 : case PDFObjectType_Null:
344 0 : fprintf(f, "null");
345 0 : break;
346 :
347 : case PDFObjectType_Bool:
348 0 : fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false");
349 0 : break;
350 :
351 : case PDFObjectType_Int:
352 0 : fprintf(f, "%d (int)", poObj->GetInt());
353 0 : break;
354 :
355 : case PDFObjectType_Real:
356 0 : fprintf(f, "%f (real)", poObj->GetReal());
357 0 : break;
358 :
359 : case PDFObjectType_Name:
360 0 : fprintf(f, "%s (name)", poObj->GetName().c_str());
361 0 : break;
362 :
363 : default:
364 0 : fprintf(f, "unknown !");
365 : break;
366 : }
367 0 : }
368 :
369 0 : void GDALPDFDumper::Dump(GDALPDFObject* poObj, int nDepth)
370 : {
371 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
372 0 : return;
373 :
374 : int i;
375 0 : CPLString osIndent;
376 0 : for(i=0;i<nDepth;i++)
377 0 : osIndent += " ";
378 : fprintf(f, "%sType = %s",
379 0 : osIndent.c_str(), poObj->GetTypeName());
380 0 : int nRefNum = poObj->GetRefNum();
381 0 : if (nRefNum != 0)
382 : fprintf(f, ", Num = %d, Gen = %d",
383 0 : nRefNum, poObj->GetRefGen());
384 0 : fprintf(f, "\n");
385 :
386 0 : if (nRefNum != 0)
387 : {
388 0 : if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end())
389 : return;
390 0 : aoSetObjectExplored.insert(nRefNum);
391 : }
392 :
393 0 : switch(poObj->GetType())
394 : {
395 : case PDFObjectType_Array:
396 0 : Dump(poObj->GetArray(), nDepth+1);
397 0 : break;
398 :
399 : case PDFObjectType_Dictionary:
400 0 : Dump(poObj->GetDictionary(), nDepth+1);
401 0 : break;
402 :
403 : case PDFObjectType_String:
404 : case PDFObjectType_Null:
405 : case PDFObjectType_Bool:
406 : case PDFObjectType_Int:
407 : case PDFObjectType_Real:
408 : case PDFObjectType_Name:
409 0 : fprintf(f, "%s", osIndent.c_str());
410 0 : DumpSimplified(poObj);
411 0 : fprintf(f, "\n");
412 0 : break;
413 :
414 : default:
415 0 : fprintf(f, "%s", osIndent.c_str());
416 0 : fprintf(f, "unknown !\n");
417 : break;
418 : }
419 :
420 0 : GDALPDFStream* poStream = poObj->GetStream();
421 0 : if (poStream != NULL)
422 : {
423 0 : fprintf(f, "%sHas stream (%d bytes)\n", osIndent.c_str(), poStream->GetLength());
424 0 : }
425 : }
426 :
427 0 : void GDALPDFDumper::Dump(GDALPDFDictionary* poDict, int nDepth)
428 : {
429 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
430 0 : return;
431 :
432 0 : std::map<CPLString, GDALPDFObject*>& oMap = poDict->GetValues();
433 0 : std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
434 0 : std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
435 : int i;
436 0 : CPLString osIndent;
437 0 : for(i=0;i<nDepth;i++)
438 0 : osIndent += " ";
439 0 : for(i=0;oIter != oEnd;++oIter, i++)
440 : {
441 0 : const char* pszKey = oIter->first.c_str();
442 0 : fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, pszKey);
443 0 : GDALPDFObject* poObj = oIter->second;
444 0 : if (strcmp(pszKey, "Parent") == 0 && !bDumpParent)
445 : {
446 0 : if (poObj->GetRefNum())
447 : fprintf(f, ", Num = %d, Gen = %d",
448 0 : poObj->GetRefNum(), poObj->GetRefGen());
449 0 : fprintf(f, "\n");
450 0 : continue;
451 : }
452 0 : if (poObj != NULL)
453 : {
454 0 : if (poObj->GetType() == PDFObjectType_String ||
455 0 : poObj->GetType() == PDFObjectType_Null ||
456 0 : poObj->GetType() == PDFObjectType_Bool ||
457 0 : poObj->GetType() == PDFObjectType_Int ||
458 0 : poObj->GetType() == PDFObjectType_Real ||
459 0 : poObj->GetType() == PDFObjectType_Name)
460 : {
461 0 : fprintf(f, " = ");
462 0 : DumpSimplified(poObj);
463 0 : fprintf(f, "\n");
464 : }
465 : else
466 : {
467 0 : fprintf(f, "\n");
468 0 : Dump(poObj, nDepth+1);
469 : }
470 : }
471 0 : }
472 : }
473 :
474 :
475 : /************************************************************************/
476 : /* ==================================================================== */
477 : /* PDFDataset */
478 : /* ==================================================================== */
479 : /************************************************************************/
480 :
481 : class PDFRasterBand;
482 :
483 : class PDFDataset : public GDALPamDataset
484 : {
485 : friend class PDFRasterBand;
486 : CPLString osFilename;
487 : CPLString osUserPwd;
488 : char *pszWKT;
489 : double dfDPI;
490 : int bHasCTM;
491 : double adfCTM[6];
492 : double adfGeoTransform[6];
493 : int bGeoTransformValid;
494 : int nGCPCount;
495 : GDAL_GCP *pasGCPList;
496 : int bProjDirty;
497 : int bNeatLineDirty;
498 :
499 : GDALMultiDomainMetadata oMDMD;
500 : int bInfoDirty;
501 : int bXMPDirty;
502 :
503 : int bUsePoppler;
504 : #ifdef HAVE_POPPLER
505 : PDFDoc* poDocPoppler;
506 : #endif
507 : #ifdef HAVE_PODOFO
508 : PoDoFo::PdfMemDocument* poDocPodofo;
509 : #endif
510 : GDALPDFObject* poPageObj;
511 :
512 : int iPage;
513 :
514 : double dfMaxArea;
515 : int ParseLGIDictObject(GDALPDFObject* poLGIDict);
516 : int ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict, int* pbIsLargestArea = NULL);
517 : int ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict);
518 : int ParseProjDict(GDALPDFDictionary* poProjDict);
519 : int ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight);
520 :
521 : int bTried;
522 : GByte *pabyData;
523 :
524 : OGRPolygon* poNeatLine;
525 :
526 : void GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands);
527 : void FindXMP(GDALPDFObject* poObj);
528 : void ParseInfo(GDALPDFObject* poObj);
529 :
530 : #ifdef HAVE_POPPLER
531 : ObjectAutoFree* poCatalogObjectPoppler;
532 : #endif
533 : GDALPDFObject* poCatalogObject;
534 : GDALPDFObject* GetCatalog();
535 :
536 : #ifdef HAVE_POPPLER
537 : void AddLayer(const char* pszLayerName, OptionalContentGroup* ocg);
538 : void ExploreLayers(GDALPDFArray* poArray, CPLString osTopLayer = "");
539 : void FindLayers();
540 : void TurnLayersOnOff();
541 : CPLStringList osLayerList;
542 : std::map<CPLString, OptionalContentGroup*> oLayerOCGMap;
543 : #endif
544 : int bUseOCG;
545 :
546 : public:
547 : PDFDataset();
548 : virtual ~PDFDataset();
549 :
550 : virtual const char* GetProjectionRef();
551 : virtual CPLErr GetGeoTransform( double * );
552 :
553 : virtual CPLErr SetProjection(const char* pszWKTIn);
554 : virtual CPLErr SetGeoTransform(double* padfGeoTransform);
555 :
556 : virtual char **GetMetadata( const char * pszDomain = "" );
557 : virtual CPLErr SetMetadata( char ** papszMetadata,
558 : const char * pszDomain = "" );
559 : virtual const char *GetMetadataItem( const char * pszName,
560 : const char * pszDomain = "" );
561 : virtual CPLErr SetMetadataItem( const char * pszName,
562 : const char * pszValue,
563 : const char * pszDomain = "" );
564 :
565 : virtual int GetGCPCount();
566 : virtual const char *GetGCPProjection();
567 : virtual const GDAL_GCP *GetGCPs();
568 : virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
569 : const char *pszGCPProjection );
570 :
571 : static GDALDataset *Open( GDALOpenInfo * );
572 : static int Identify( GDALOpenInfo * );
573 : };
574 :
575 : /************************************************************************/
576 : /* ==================================================================== */
577 : /* PDFRasterBand */
578 : /* ==================================================================== */
579 : /************************************************************************/
580 :
581 : class PDFRasterBand : public GDALPamRasterBand
582 968 : {
583 : friend class PDFDataset;
584 :
585 : public:
586 :
587 : PDFRasterBand( PDFDataset *, int );
588 :
589 : virtual CPLErr IReadBlock( int, int, void * );
590 : virtual GDALColorInterp GetColorInterpretation();
591 : };
592 :
593 :
594 : /************************************************************************/
595 : /* PDFRasterBand() */
596 : /************************************************************************/
597 :
598 968 : PDFRasterBand::PDFRasterBand( PDFDataset *poDS, int nBand )
599 :
600 : {
601 968 : this->poDS = poDS;
602 968 : this->nBand = nBand;
603 :
604 968 : eDataType = GDT_Byte;
605 :
606 968 : nBlockXSize = poDS->GetRasterXSize();
607 968 : nBlockYSize = 1;
608 968 : }
609 :
610 : /************************************************************************/
611 : /* GetColorInterpretation() */
612 : /************************************************************************/
613 :
614 0 : GDALColorInterp PDFRasterBand::GetColorInterpretation()
615 : {
616 0 : PDFDataset *poGDS = (PDFDataset *) poDS;
617 0 : if (poGDS->nBands == 1)
618 0 : return GCI_GrayIndex;
619 : else
620 0 : return (GDALColorInterp)(GCI_RedBand + (nBand - 1));
621 : }
622 :
623 : /************************************************************************/
624 : /* IReadBlock() */
625 : /************************************************************************/
626 :
627 48980 : CPLErr PDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
628 : void * pImage )
629 :
630 : {
631 48980 : PDFDataset *poGDS = (PDFDataset *) poDS;
632 48980 : if (poGDS->bTried == FALSE)
633 : {
634 106 : poGDS->bTried = TRUE;
635 106 : poGDS->pabyData = (GByte*)VSIMalloc3(MAX(3, poGDS->nBands), nRasterXSize, nRasterYSize);
636 106 : if (poGDS->pabyData == NULL)
637 0 : return CE_Failure;
638 :
639 106 : const char* pszRenderingOptions = CPLGetConfigOption("GDAL_PDF_RENDERING_OPTIONS", NULL);
640 :
641 : #ifdef HAVE_POPPLER
642 106 : if(poGDS->bUsePoppler)
643 : {
644 : SplashColor sColor;
645 98 : sColor[0] = 255;
646 98 : sColor[1] = 255;
647 98 : sColor[2] = 255;
648 : GDALPDFOutputDev *poSplashOut;
649 : poSplashOut = new GDALPDFOutputDev((poGDS->nBands < 4) ? splashModeRGB8 : splashModeXBGR8,
650 : 4, gFalse,
651 98 : (poGDS->nBands < 4) ? sColor : NULL);
652 :
653 98 : if (pszRenderingOptions != NULL)
654 : {
655 0 : poSplashOut->SetEnableVector(FALSE);
656 0 : poSplashOut->SetEnableText(FALSE);
657 0 : poSplashOut->SetEnableBitmap(FALSE);
658 :
659 0 : char** papszTokens = CSLTokenizeString2( pszRenderingOptions, " ,", 0 );
660 0 : for(int i=0;papszTokens[i] != NULL;i++)
661 : {
662 0 : if (EQUAL(papszTokens[i], "VECTOR"))
663 0 : poSplashOut->SetEnableVector(TRUE);
664 0 : else if (EQUAL(papszTokens[i], "TEXT"))
665 0 : poSplashOut->SetEnableText(TRUE);
666 0 : else if (EQUAL(papszTokens[i], "BITMAP"))
667 0 : poSplashOut->SetEnableBitmap(TRUE);
668 : else
669 : {
670 : CPLError(CE_Warning, CPLE_NotSupported,
671 : "Value %s is not a valid value for GDAL_PDF_RENDERING_OPTIONS",
672 0 : papszTokens[i]);
673 : }
674 : }
675 0 : CSLDestroy(papszTokens);
676 : }
677 :
678 98 : PDFDoc* poDoc = poGDS->poDocPoppler;
679 : #ifdef POPPLER_0_20_OR_LATER
680 98 : poSplashOut->startDoc(poDoc);
681 : #else
682 : poSplashOut->startDoc(poDoc->getXRef());
683 : #endif
684 98 : double dfDPI = poGDS->dfDPI;
685 :
686 : /* EVIL: we modify a private member... */
687 : /* poppler (at least 0.12 and 0.14 versions) don't render correctly */
688 : /* some PDFs and display an error message 'Could not find a OCG with Ref' */
689 : /* in those cases. This processing of optional content is an addition of */
690 : /* poppler in comparison to original xpdf, which hasn't the issue. All in */
691 : /* all, nullifying optContent removes the error message and improves the rendering */
692 : #ifdef POPPLER_HAS_OPTCONTENT
693 98 : Catalog* poCatalog = poDoc->getCatalog();
694 98 : OCGs* poOldOCGs = poCatalog->optContent;
695 98 : if (!poGDS->bUseOCG)
696 92 : poCatalog->optContent = NULL;
697 : #endif
698 : poDoc->displayPageSlice(poSplashOut,
699 : poGDS->iPage,
700 : dfDPI, dfDPI,
701 : 0,
702 : TRUE, gFalse, gFalse,
703 : 0, 0,
704 : nRasterXSize,
705 98 : nRasterYSize);
706 :
707 : /* Restore back */
708 : #ifdef POPPLER_HAS_OPTCONTENT
709 98 : poCatalog->optContent = poOldOCGs;
710 : #endif
711 :
712 98 : SplashBitmap* poBitmap = poSplashOut->getBitmap();
713 98 : if (poBitmap->getWidth() != nRasterXSize || poBitmap->getHeight() != nRasterYSize)
714 : {
715 : CPLError(CE_Failure, CPLE_AppDefined,
716 : "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)" ,
717 : poBitmap->getWidth(), poBitmap->getHeight(),
718 0 : nRasterXSize, nRasterYSize);
719 0 : VSIFree(poGDS->pabyData);
720 0 : poGDS->pabyData = NULL;
721 0 : delete poSplashOut;
722 0 : return CE_Failure;
723 : }
724 :
725 98 : GByte* pabyDataR = poGDS->pabyData;
726 98 : GByte* pabyDataG = poGDS->pabyData + nRasterXSize * nRasterYSize;
727 98 : GByte* pabyDataB = poGDS->pabyData + 2 * nRasterXSize * nRasterYSize;
728 98 : GByte* pabyDataA = poGDS->pabyData + 3 * nRasterXSize * nRasterYSize;
729 98 : GByte* pabySrc = poBitmap->getDataPtr();
730 98 : GByte* pabyAlphaSrc = (GByte*)poBitmap->getAlphaPtr();
731 : int i, j;
732 41338 : for(j=0;j<nRasterYSize;j++)
733 : {
734 39428546 : for(i=0;i<nRasterXSize;i++)
735 : {
736 39387306 : if (poGDS->nBands < 4)
737 : {
738 39338706 : pabyDataR[i] = pabySrc[i * 3 + 0];
739 39338706 : pabyDataG[i] = pabySrc[i * 3 + 1];
740 39338706 : pabyDataB[i] = pabySrc[i * 3 + 2];
741 : }
742 : else
743 : {
744 48600 : pabyDataR[i] = pabySrc[i * 4 + 2];
745 48600 : pabyDataG[i] = pabySrc[i * 4 + 1];
746 48600 : pabyDataB[i] = pabySrc[i * 4 + 0];
747 48600 : pabyDataA[i] = pabyAlphaSrc[i];
748 : }
749 : }
750 41240 : pabyDataR += nRasterXSize;
751 41240 : pabyDataG += nRasterXSize;
752 41240 : pabyDataB += nRasterXSize;
753 41240 : pabyDataA += nRasterXSize;
754 41240 : pabyAlphaSrc += poBitmap->getAlphaRowSize();
755 41240 : pabySrc += poBitmap->getRowSize();
756 : }
757 98 : delete poSplashOut;
758 : }
759 : #endif // HAVE_POPPLER
760 :
761 : #ifdef HAVE_PODOFO
762 106 : if (!poGDS->bUsePoppler)
763 : {
764 8 : CPLAssert(poGDS->nBands != 4);
765 :
766 8 : if (pszRenderingOptions != NULL)
767 : {
768 : CPLError(CE_Warning, CPLE_NotSupported,
769 : "GDAL_PDF_RENDERING_OPTIONS only supported "
770 0 : "when PDF driver is compiled against Poppler.");
771 : }
772 :
773 8 : memset(poGDS->pabyData, 0, ((size_t)3) * nRasterXSize * nRasterYSize);
774 :
775 8 : CPLString osTmpFilenamePrefix = CPLGenerateTempFilename("pdf");
776 : CPLString osTmpFilename(CPLSPrintf("%s-%d.ppm",
777 : osTmpFilenamePrefix.c_str(),
778 8 : poGDS->iPage));
779 :
780 : CPLString osCmd = CPLSPrintf("pdftoppm -r %f -f %d -l %d \"%s\" \"%s\"",
781 : poGDS->dfDPI, poGDS->iPage, poGDS->iPage,
782 8 : poGDS->osFilename.c_str(), osTmpFilenamePrefix.c_str());
783 8 : if (poGDS->osUserPwd.size() != 0)
784 : {
785 0 : osCmd += " -upw \"";
786 0 : osCmd += poGDS->osUserPwd;
787 0 : osCmd += "\"";
788 : }
789 :
790 8 : CPLDebug("PDF", "Running '%s'", osCmd.c_str());
791 8 : int nRet = system(osCmd.c_str());
792 8 : if (nRet == 0)
793 : {
794 8 : GDALDataset* poDS = (GDALDataset*) GDALOpen(osTmpFilename, GA_ReadOnly);
795 8 : if (poDS)
796 : {
797 8 : if (poDS->GetRasterCount() == 3)
798 : {
799 : poDS->RasterIO(GF_Read, 0, 0,
800 : poDS->GetRasterXSize(),
801 : poDS->GetRasterYSize(),
802 : poGDS->pabyData, nRasterXSize, nRasterYSize,
803 8 : GDT_Byte, 3, NULL, 0, 0, 0);
804 : }
805 8 : delete poDS;
806 : }
807 : }
808 : else
809 : {
810 0 : CPLDebug("PDF", "Ret code = %d", nRet);
811 : }
812 8 : VSIUnlink(osTmpFilename);
813 : }
814 : #endif
815 : }
816 48980 : if (poGDS->pabyData == NULL)
817 0 : return CE_Failure;
818 :
819 : memcpy(pImage,
820 : poGDS->pabyData + (nBand - 1) * nRasterXSize * nRasterYSize + nBlockYOff * nRasterXSize,
821 48980 : nRasterXSize);
822 :
823 48980 : return CE_None;
824 : }
825 :
826 : /************************************************************************/
827 : /* ~PDFDataset() */
828 : /************************************************************************/
829 :
830 322 : PDFDataset::PDFDataset()
831 : {
832 322 : bUsePoppler = FALSE;
833 : #ifdef HAVE_POPPLER
834 322 : poDocPoppler = NULL;
835 : #endif
836 : #ifdef HAVE_PODOFO
837 322 : poDocPodofo = NULL;
838 : #endif
839 322 : pszWKT = NULL;
840 322 : dfMaxArea = 0;
841 322 : adfGeoTransform[0] = 0;
842 322 : adfGeoTransform[1] = 1;
843 322 : adfGeoTransform[2] = 0;
844 322 : adfGeoTransform[3] = 0;
845 322 : adfGeoTransform[4] = 0;
846 322 : adfGeoTransform[5] = 1;
847 322 : bHasCTM = FALSE;
848 322 : bGeoTransformValid = FALSE;
849 322 : nGCPCount = 0;
850 322 : pasGCPList = NULL;
851 322 : bProjDirty = FALSE;
852 322 : bNeatLineDirty = FALSE;
853 322 : bInfoDirty = FALSE;
854 322 : bXMPDirty = FALSE;
855 322 : bTried = FALSE;
856 322 : pabyData = NULL;
857 322 : iPage = -1;
858 322 : poNeatLine = NULL;
859 322 : bUseOCG = FALSE;
860 322 : poCatalogObject = NULL;
861 : #ifdef HAVE_POPPLER
862 322 : poCatalogObjectPoppler = NULL;
863 : #endif
864 322 : }
865 :
866 : /************************************************************************/
867 : /* PDFFreeDoc() */
868 : /************************************************************************/
869 :
870 : #ifdef HAVE_POPPLER
871 322 : static void PDFFreeDoc(PDFDoc* poDoc)
872 : {
873 322 : if (poDoc)
874 : {
875 : /* hack to avoid potential cross heap issues on Win32 */
876 : /* str is the VSIPDFFileStream object passed in the constructor of PDFDoc */
877 262 : delete poDoc->str;
878 262 : poDoc->str = NULL;
879 :
880 262 : delete poDoc;
881 : }
882 322 : }
883 : #endif
884 :
885 : /************************************************************************/
886 : /* GetCatalog() */
887 : /************************************************************************/
888 :
889 16 : GDALPDFObject* PDFDataset::GetCatalog()
890 : {
891 16 : if (poCatalogObject)
892 0 : return poCatalogObject;
893 :
894 : #ifdef HAVE_POPPLER
895 16 : if (bUsePoppler)
896 : {
897 10 : poCatalogObjectPoppler = new ObjectAutoFree;
898 10 : poDocPoppler->getXRef()->getCatalog(poCatalogObjectPoppler);
899 10 : if (!poCatalogObjectPoppler->isNull())
900 10 : poCatalogObject = new GDALPDFObjectPoppler(poCatalogObjectPoppler, FALSE);
901 : }
902 : #endif
903 :
904 : #ifdef HAVE_PODOFO
905 16 : if (!bUsePoppler)
906 : {
907 6 : int nCatalogNum = 0, nCatalogGen = 0;
908 6 : VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb");
909 6 : if (fp != NULL)
910 : {
911 6 : GDALPDFWriter oWriter(fp, TRUE);
912 6 : if (oWriter.ParseTrailerAndXRef())
913 : {
914 6 : nCatalogNum = oWriter.GetCatalogNum();
915 6 : nCatalogGen = oWriter.GetCatalogGen();
916 : }
917 6 : oWriter.Close();
918 : }
919 :
920 : PoDoFo::PdfObject* poCatalogPodofo =
921 6 : poDocPodofo->GetObjects().GetObject(PoDoFo::PdfReference(nCatalogNum, nCatalogGen));
922 6 : if (poCatalogPodofo)
923 6 : poCatalogObject = new GDALPDFObjectPodofo(poCatalogPodofo, poDocPodofo->GetObjects());
924 : }
925 : #endif
926 :
927 16 : return poCatalogObject;
928 : }
929 :
930 : /************************************************************************/
931 : /* ~PDFDataset() */
932 : /************************************************************************/
933 :
934 322 : PDFDataset::~PDFDataset()
935 : {
936 322 : CPLFree(pabyData);
937 322 : pabyData = NULL;
938 :
939 322 : delete poNeatLine;
940 322 : poNeatLine = NULL;
941 :
942 : /* Collect data necessary to update */
943 322 : int nNum = poPageObj->GetRefNum();
944 322 : int nGen = poPageObj->GetRefGen();
945 322 : GDALPDFDictionaryRW* poPageDictCopy = NULL;
946 322 : GDALPDFDictionaryRW* poCatalogDictCopy = NULL;
947 366 : if (eAccess == GA_Update &&
948 : (bProjDirty || bNeatLineDirty || bInfoDirty || bXMPDirty) &&
949 : nNum != 0 &&
950 : poPageObj != NULL &&
951 44 : poPageObj->GetType() == PDFObjectType_Dictionary)
952 : {
953 44 : poPageDictCopy = poPageObj->GetDictionary()->Clone();
954 :
955 44 : if (bXMPDirty)
956 : {
957 : /* We need the catalog because it points to the XMP Metadata object */
958 12 : GetCatalog();
959 12 : if (poCatalogObject && poCatalogObject->GetType() == PDFObjectType_Dictionary)
960 12 : poCatalogDictCopy = poCatalogObject->GetDictionary()->Clone();
961 : }
962 : }
963 :
964 : /* Close document (and file descriptor) to be able to open it */
965 : /* in read-write mode afterwards */
966 322 : delete poPageObj;
967 322 : poPageObj = NULL;
968 322 : delete poCatalogObject;
969 322 : poCatalogObject = NULL;
970 : #ifdef HAVE_POPPLER
971 322 : delete poCatalogObjectPoppler;
972 322 : PDFFreeDoc(poDocPoppler);
973 322 : poDocPoppler = NULL;
974 : #endif
975 : #ifdef HAVE_PODOFO
976 322 : delete poDocPodofo;
977 322 : poDocPodofo = NULL;
978 : #endif
979 :
980 : /* Now do the update */
981 322 : if (poPageDictCopy)
982 : {
983 44 : VSILFILE* fp = VSIFOpenL(osFilename, "rb+");
984 44 : if (fp != NULL)
985 : {
986 44 : GDALPDFWriter oWriter(fp, TRUE);
987 44 : if (oWriter.ParseTrailerAndXRef())
988 : {
989 44 : if ((bProjDirty || bNeatLineDirty) && poPageDictCopy != NULL)
990 : oWriter.UpdateProj(this, dfDPI,
991 20 : poPageDictCopy, nNum, nGen);
992 :
993 44 : if (bInfoDirty)
994 12 : oWriter.UpdateInfo(this);
995 :
996 44 : if (bXMPDirty && poCatalogDictCopy != NULL)
997 12 : oWriter.UpdateXMP(this, poCatalogDictCopy);
998 : }
999 44 : oWriter.Close();
1000 : }
1001 : else
1002 : {
1003 : CPLError(CE_Failure, CPLE_AppDefined,
1004 0 : "Cannot open %s in update mode", osFilename.c_str());
1005 : }
1006 : }
1007 322 : delete poPageDictCopy;
1008 322 : poPageDictCopy = NULL;
1009 322 : delete poCatalogDictCopy;
1010 322 : poCatalogDictCopy = NULL;
1011 :
1012 322 : if( nGCPCount > 0 )
1013 : {
1014 10 : GDALDeinitGCPs( nGCPCount, pasGCPList );
1015 10 : CPLFree( pasGCPList );
1016 10 : pasGCPList = NULL;
1017 10 : nGCPCount = 0;
1018 : }
1019 322 : CPLFree(pszWKT);
1020 322 : pszWKT = NULL;
1021 322 : }
1022 :
1023 : /************************************************************************/
1024 : /* Identify() */
1025 : /************************************************************************/
1026 :
1027 21826 : int PDFDataset::Identify( GDALOpenInfo * poOpenInfo )
1028 : {
1029 21826 : if (strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0)
1030 2 : return TRUE;
1031 :
1032 21824 : if (poOpenInfo->nHeaderBytes < 128)
1033 21202 : return FALSE;
1034 :
1035 622 : return strncmp((const char*)poOpenInfo->pabyHeader, "%PDF", 4) == 0;
1036 : }
1037 :
1038 : /************************************************************************/
1039 : /* PDFDatasetErrorFunction() */
1040 : /************************************************************************/
1041 :
1042 : #ifdef HAVE_POPPLER
1043 : #ifdef POPPLER_0_20_OR_LATER
1044 0 : static void PDFDatasetErrorFunction(void* userData, ErrorCategory eErrCatagory, int nPos, char *pszMsg)
1045 : {
1046 0 : CPLString osError;
1047 :
1048 0 : if (nPos >= 0)
1049 0 : osError.Printf("Pos = %d, ", nPos);
1050 0 : osError += pszMsg;
1051 :
1052 0 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
1053 : return;
1054 :
1055 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
1056 : }
1057 : #else
1058 : static void PDFDatasetErrorFunction(int nPos, char *pszMsg, va_list args)
1059 : {
1060 : CPLString osError;
1061 :
1062 : if (nPos >= 0)
1063 : osError.Printf("Pos = %d, ", nPos);
1064 : osError += CPLString().vPrintf(pszMsg, args);
1065 :
1066 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
1067 : return;
1068 :
1069 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
1070 : }
1071 : #endif
1072 : #endif
1073 :
1074 : /************************************************************************/
1075 : /* GDALPDFParseStreamContentOnlyDrawForm() */
1076 : /************************************************************************/
1077 :
1078 : static
1079 4 : CPLString GDALPDFParseStreamContentOnlyDrawForm(const char* pszContent)
1080 : {
1081 4 : CPLString osToken;
1082 : char ch;
1083 4 : int nCurIdx = 0;
1084 4 : CPLString osCurrentForm;
1085 :
1086 76 : while((ch = *pszContent) != '\0')
1087 : {
1088 68 : if (ch == '%')
1089 : {
1090 : /* Skip comments until end-of-line */
1091 0 : while((ch = *pszContent) != '\0')
1092 : {
1093 0 : if (ch == '\r' || ch == '\n')
1094 0 : break;
1095 0 : pszContent ++;
1096 : }
1097 0 : if (ch == 0)
1098 0 : break;
1099 : }
1100 76 : else if (ch == ' ' || ch == '\r' || ch == '\n')
1101 : {
1102 8 : if (osToken.size())
1103 : {
1104 8 : if (nCurIdx == 0 && osToken[0] == '/')
1105 : {
1106 4 : osCurrentForm = osToken.substr(1);
1107 4 : nCurIdx ++;
1108 : }
1109 4 : else if (nCurIdx == 1 && osToken == "Do")
1110 : {
1111 4 : nCurIdx ++;
1112 : }
1113 : else
1114 : {
1115 0 : return "";
1116 : }
1117 : }
1118 8 : osToken = "";
1119 : }
1120 : else
1121 60 : osToken += ch;
1122 68 : pszContent ++;
1123 : }
1124 :
1125 4 : return osCurrentForm;
1126 : }
1127 :
1128 : /************************************************************************/
1129 : /* GDALPDFParseStreamContent() */
1130 : /************************************************************************/
1131 :
1132 : typedef enum
1133 : {
1134 : STATE_INIT,
1135 : STATE_AFTER_q,
1136 : STATE_AFTER_cm,
1137 : STATE_AFTER_Do
1138 : } PDFStreamState;
1139 :
1140 : /* This parser is reduced to understanding sequences that draw rasters, such as :
1141 : q
1142 : scaleX 0 0 scaleY translateX translateY cm
1143 : /ImXXX Do
1144 : Q
1145 :
1146 : All other sequences will abort the parsing.
1147 :
1148 : Returns TRUE if the stream only contains images.
1149 : */
1150 :
1151 : static
1152 4 : int GDALPDFParseStreamContent(const char* pszContent,
1153 : GDALPDFDictionary* poXObjectDict,
1154 : double* pdfDPI,
1155 : int* pbDPISet,
1156 : int* pnBands)
1157 : {
1158 4 : CPLString osToken;
1159 : char ch;
1160 4 : PDFStreamState nState = STATE_INIT;
1161 4 : int nCurIdx = 0;
1162 : double adfVals[6];
1163 4 : CPLString osCurrentImage;
1164 :
1165 4 : double dfDPI = 72.0;
1166 4 : *pbDPISet = FALSE;
1167 :
1168 832 : while((ch = *pszContent) != '\0')
1169 : {
1170 824 : if (ch == '%')
1171 : {
1172 : /* Skip comments until end-of-line */
1173 0 : while((ch = *pszContent) != '\0')
1174 : {
1175 0 : if (ch == '\r' || ch == '\n')
1176 0 : break;
1177 0 : pszContent ++;
1178 : }
1179 0 : if (ch == 0)
1180 0 : break;
1181 : }
1182 1000 : else if (ch == ' ' || ch == '\r' || ch == '\n')
1183 : {
1184 176 : if (osToken.size())
1185 : {
1186 176 : if (nState == STATE_INIT)
1187 : {
1188 16 : if (osToken == "q")
1189 : {
1190 16 : nState = STATE_AFTER_q;
1191 16 : nCurIdx = 0;
1192 : }
1193 : else
1194 0 : return FALSE;
1195 : }
1196 160 : else if (nState == STATE_AFTER_q)
1197 : {
1198 112 : if (nCurIdx < 6)
1199 : {
1200 96 : adfVals[nCurIdx ++] = CPLAtof(osToken);
1201 : }
1202 16 : else if (osToken == "cm")
1203 : {
1204 16 : nState = STATE_AFTER_cm;
1205 16 : nCurIdx = 0;
1206 : }
1207 : else
1208 0 : return FALSE;
1209 : }
1210 48 : else if (nState == STATE_AFTER_cm)
1211 : {
1212 32 : if (nCurIdx == 0 && osToken[0] == '/')
1213 : {
1214 16 : osCurrentImage = osToken.substr(1);
1215 : }
1216 16 : else if (osToken == "Do")
1217 : {
1218 16 : nState = STATE_AFTER_Do;
1219 : }
1220 : else
1221 0 : return FALSE;
1222 : }
1223 16 : else if (nState == STATE_AFTER_Do)
1224 : {
1225 16 : if (osToken == "Q")
1226 : {
1227 16 : GDALPDFObject* poImage = poXObjectDict->Get(osCurrentImage);
1228 16 : if (poImage != NULL && poImage->GetType() == PDFObjectType_Dictionary)
1229 : {
1230 16 : GDALPDFDictionary* poImageDict = poImage->GetDictionary();
1231 16 : GDALPDFObject* poWidth = poImageDict->Get("Width");
1232 16 : GDALPDFObject* poHeight = poImageDict->Get("Height");
1233 16 : GDALPDFObject* poColorSpace = poImageDict->Get("ColorSpace");
1234 16 : if (poColorSpace && poColorSpace->GetType() == PDFObjectType_Name)
1235 : {
1236 16 : if (poColorSpace->GetName() == "DeviceRGB" && *pnBands < 3)
1237 0 : *pnBands = 3;
1238 16 : else if (poColorSpace->GetName() == "DeviceGray")
1239 16 : *pnBands = 1;
1240 : }
1241 :
1242 16 : if (poWidth && poHeight && adfVals[1] == 0.0 && adfVals[2] == 0.0)
1243 : {
1244 16 : double dfWidth = Get(poWidth);
1245 16 : double dfHeight = Get(poHeight);
1246 16 : double dfScaleX = adfVals[0];
1247 16 : double dfScaleY = adfVals[3];
1248 16 : double dfDPI_X = ROUND_TO_INT_IF_CLOSE(dfWidth / dfScaleX * 72, 1e-3);
1249 16 : double dfDPI_Y = ROUND_TO_INT_IF_CLOSE(dfHeight / dfScaleY * 72, 1e-3);
1250 : //CPLDebug("PDF", "Image %s, width = %.16g, height = %.16g, scaleX = %.16g, scaleY = %.16g --> DPI_X = %.16g, DPI_Y = %.16g",
1251 : // osCurrentImage.c_str(), dfWidth, dfHeight, dfScaleX, dfScaleY, dfDPI_X, dfDPI_Y);
1252 16 : if (dfDPI_X > dfDPI) dfDPI = dfDPI_X;
1253 16 : if (dfDPI_Y > dfDPI) dfDPI = dfDPI_Y;
1254 16 : *pbDPISet = TRUE;
1255 16 : *pdfDPI = dfDPI;
1256 : }
1257 : }
1258 16 : nState = STATE_INIT;
1259 : }
1260 : else
1261 0 : return FALSE;
1262 : }
1263 : }
1264 176 : osToken = "";
1265 : }
1266 : else
1267 648 : osToken += ch;
1268 824 : pszContent ++;
1269 : }
1270 :
1271 4 : return TRUE;
1272 : }
1273 :
1274 : /************************************************************************/
1275 : /* GuessDPI() */
1276 : /************************************************************************/
1277 :
1278 322 : void PDFDataset::GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands)
1279 : {
1280 322 : const char* pszDPI = CPLGetConfigOption("GDAL_PDF_DPI", NULL);
1281 322 : if (pszDPI != NULL)
1282 : {
1283 2 : dfDPI = atof(pszDPI);
1284 : }
1285 : else
1286 : {
1287 320 : GDALPDFObject* poUserUnit = NULL;
1288 680 : if ( (poUserUnit = poPageDict->Get("UserUnit")) != NULL &&
1289 302 : (poUserUnit->GetType() == PDFObjectType_Int ||
1290 58 : poUserUnit->GetType() == PDFObjectType_Real) )
1291 : {
1292 302 : dfDPI = ROUND_TO_INT_IF_CLOSE(Get(poUserUnit) * 72.0);
1293 302 : CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", dfDPI);
1294 302 : SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI));
1295 : }
1296 : else
1297 : {
1298 18 : dfDPI = 150.0;
1299 :
1300 : /* Try to get a better value from the images that are drawn */
1301 : /* Very simplistic logic. Will only work for raster only PDF */
1302 :
1303 18 : GDALPDFObject* poContents = poPageDict->Get("Contents");
1304 18 : if (poContents != NULL && poContents->GetType() == PDFObjectType_Array)
1305 : {
1306 6 : GDALPDFArray* poContentsArray = poContents->GetArray();
1307 6 : if (poContentsArray->GetLength() == 1)
1308 : {
1309 4 : poContents = poContentsArray->Get(0);
1310 : }
1311 : }
1312 :
1313 18 : GDALPDFObject* poResources = poPageDict->Get("Resources");
1314 18 : GDALPDFObject* poXObject = NULL;
1315 36 : if (poResources != NULL &&
1316 18 : poResources->GetType() == PDFObjectType_Dictionary)
1317 18 : poXObject = poResources->GetDictionary()->Get("XObject");
1318 :
1319 40 : if (poContents != NULL &&
1320 18 : poContents->GetType() == PDFObjectType_Dictionary &&
1321 : poXObject != NULL &&
1322 4 : poXObject->GetType() == PDFObjectType_Dictionary)
1323 : {
1324 4 : GDALPDFDictionary* poXObjectDict = poXObject->GetDictionary();
1325 4 : GDALPDFStream* poPageStream = poContents->GetStream();
1326 4 : if (poPageStream != NULL)
1327 : {
1328 4 : char* pszContent = NULL;
1329 4 : int nLength = poPageStream->GetLength();
1330 4 : if( nLength < 100000 )
1331 : {
1332 4 : pszContent = poPageStream->GetBytes();
1333 :
1334 4 : CPLString osForm = GDALPDFParseStreamContentOnlyDrawForm(pszContent);
1335 4 : if (osForm.size())
1336 : {
1337 4 : CPLFree(pszContent);
1338 4 : pszContent = NULL;
1339 :
1340 4 : GDALPDFObject* poObjForm = poXObjectDict->Get(osForm);
1341 12 : if (poObjForm != NULL &&
1342 4 : poObjForm->GetType() == PDFObjectType_Dictionary &&
1343 4 : (poPageStream = poObjForm->GetStream()) != NULL)
1344 : {
1345 4 : GDALPDFDictionary* poObjFormDict = poObjForm->GetDictionary();
1346 : GDALPDFObject* poSubtype;
1347 12 : if ((poSubtype = poObjFormDict->Get("Subtype")) != NULL &&
1348 4 : poSubtype->GetType() == PDFObjectType_Name &&
1349 4 : poSubtype->GetName() == "Form")
1350 : {
1351 4 : int nLength = poPageStream->GetLength();
1352 4 : if( nLength < 100000 )
1353 : {
1354 4 : pszContent = poPageStream->GetBytes();
1355 : }
1356 : }
1357 : }
1358 4 : }
1359 : }
1360 :
1361 4 : if (pszContent != NULL)
1362 : {
1363 4 : int bDPISet = FALSE;
1364 : GDALPDFParseStreamContent(pszContent,
1365 : poXObjectDict,
1366 : &(dfDPI),
1367 : &bDPISet,
1368 4 : pnBands);
1369 4 : CPLFree(pszContent);
1370 4 : if (bDPISet)
1371 : {
1372 4 : CPLDebug("PDF", "DPI guessed from contents stream = %.16g", dfDPI);
1373 4 : SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI));
1374 : }
1375 : }
1376 : }
1377 : }
1378 : }
1379 : }
1380 :
1381 322 : if (dfDPI < 1 || dfDPI > 7200)
1382 : {
1383 : CPLError(CE_Warning, CPLE_AppDefined,
1384 0 : "Invalid value for GDAL_PDF_DPI. Using default value instead");
1385 0 : dfDPI = 150;
1386 : }
1387 322 : }
1388 :
1389 : /************************************************************************/
1390 : /* FindXMP() */
1391 : /************************************************************************/
1392 :
1393 768 : void PDFDataset::FindXMP(GDALPDFObject* poObj)
1394 : {
1395 768 : if (poObj->GetType() != PDFObjectType_Dictionary)
1396 120 : return;
1397 :
1398 648 : GDALPDFDictionary* poDict = poObj->GetDictionary();
1399 648 : GDALPDFObject* poType = poDict->Get("Type");
1400 648 : GDALPDFObject* poSubtype = poDict->Get("Subtype");
1401 1592 : if (poType == NULL ||
1402 464 : poType->GetType() != PDFObjectType_Name ||
1403 464 : poType->GetName() != "Metadata" ||
1404 : poSubtype == NULL ||
1405 8 : poSubtype->GetType() != PDFObjectType_Name ||
1406 8 : poSubtype->GetName() != "XML")
1407 : {
1408 640 : return;
1409 : }
1410 :
1411 8 : GDALPDFStream* poStream = poObj->GetStream();
1412 8 : if (poStream == NULL)
1413 0 : return;
1414 :
1415 8 : char* pszContent = poStream->GetBytes();
1416 8 : int nLength = (int)poStream->GetLength();
1417 8 : if (pszContent != NULL && nLength > 15 &&
1418 : strncmp(pszContent, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
1419 : {
1420 : char *apszMDList[2];
1421 8 : apszMDList[0] = pszContent;
1422 8 : apszMDList[1] = NULL;
1423 8 : SetMetadata(apszMDList, "xml:XMP");
1424 : }
1425 8 : CPLFree(pszContent);
1426 : }
1427 :
1428 : /************************************************************************/
1429 : /* ParseInfo() */
1430 : /************************************************************************/
1431 :
1432 322 : void PDFDataset::ParseInfo(GDALPDFObject* poInfoObj)
1433 : {
1434 322 : if (poInfoObj->GetType() != PDFObjectType_Dictionary)
1435 226 : return;
1436 :
1437 96 : GDALPDFDictionary* poInfoObjDict = poInfoObj->GetDictionary();
1438 96 : GDALPDFObject* poItem = NULL;
1439 96 : int bOneMDISet = FALSE;
1440 116 : if ((poItem = poInfoObjDict->Get("Author")) != NULL &&
1441 20 : poItem->GetType() == PDFObjectType_String)
1442 : {
1443 20 : SetMetadataItem("AUTHOR", poItem->GetString().c_str());
1444 20 : bOneMDISet = TRUE;
1445 : }
1446 120 : if ((poItem = poInfoObjDict->Get("Creator")) != NULL &&
1447 24 : poItem->GetType() == PDFObjectType_String)
1448 : {
1449 24 : SetMetadataItem("CREATOR", poItem->GetString().c_str());
1450 24 : bOneMDISet = TRUE;
1451 : }
1452 100 : if ((poItem = poInfoObjDict->Get("Keywords")) != NULL &&
1453 4 : poItem->GetType() == PDFObjectType_String)
1454 : {
1455 4 : SetMetadataItem("KEYWORDS", poItem->GetString().c_str());
1456 4 : bOneMDISet = TRUE;
1457 : }
1458 104 : if ((poItem = poInfoObjDict->Get("Subject")) != NULL &&
1459 8 : poItem->GetType() == PDFObjectType_String)
1460 : {
1461 8 : SetMetadataItem("SUBJECT", poItem->GetString().c_str());
1462 8 : bOneMDISet = TRUE;
1463 : }
1464 104 : if ((poItem = poInfoObjDict->Get("Title")) != NULL &&
1465 8 : poItem->GetType() == PDFObjectType_String)
1466 : {
1467 8 : SetMetadataItem("TITLE", poItem->GetString().c_str());
1468 8 : bOneMDISet = TRUE;
1469 : }
1470 152 : if ((poItem = poInfoObjDict->Get("Producer")) != NULL &&
1471 56 : poItem->GetType() == PDFObjectType_String)
1472 : {
1473 56 : if (bOneMDISet || poItem->GetString() != "PoDoFo - http://podofo.sf.net")
1474 : {
1475 8 : SetMetadataItem("PRODUCER", poItem->GetString().c_str());
1476 8 : bOneMDISet = TRUE;
1477 : }
1478 : }
1479 164 : if ((poItem = poInfoObjDict->Get("CreationDate")) != NULL &&
1480 68 : poItem->GetType() == PDFObjectType_String)
1481 : {
1482 68 : if (bOneMDISet)
1483 20 : SetMetadataItem("CREATION_DATE", poItem->GetString().c_str());
1484 : }
1485 : }
1486 :
1487 : #ifdef HAVE_POPPLER
1488 :
1489 : /************************************************************************/
1490 : /* PDFSanitizeLayerName() */
1491 : /************************************************************************/
1492 :
1493 114 : CPLString PDFSanitizeLayerName(const char* pszName)
1494 : {
1495 114 : CPLString osName;
1496 1370 : for(int i=0; pszName[i] != '\0'; i++)
1497 : {
1498 1332 : if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',')
1499 76 : osName += "_";
1500 : else
1501 1180 : osName += pszName[i];
1502 : }
1503 0 : return osName;
1504 : }
1505 :
1506 : /************************************************************************/
1507 : /* AddLayer() */
1508 : /************************************************************************/
1509 :
1510 114 : void PDFDataset::AddLayer(const char* pszLayerName, OptionalContentGroup* ocg)
1511 : {
1512 114 : int nNewIndex = osLayerList.size() /*/ 2*/;
1513 :
1514 114 : if (nNewIndex == 100)
1515 : {
1516 0 : CPLStringList osNewLayerList;
1517 0 : for(int i=0;i<100;i++)
1518 : {
1519 : osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_NAME", i),
1520 0 : osLayerList[/*2 * */ i] + strlen("LAYER_00_NAME="));
1521 : /*osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_INIT_STATE", i),
1522 : osLayerList[i * 2 + 1] + strlen("LAYER_00_INIT_STATE="));*/
1523 : }
1524 0 : osLayerList = osNewLayerList;
1525 : }
1526 :
1527 : char szFormatName[64];
1528 : /* char szFormatInitState[64]; */
1529 114 : sprintf(szFormatName, "LAYER_%%0%dd_NAME", nNewIndex >= 100 ? 3 : 2);
1530 : /* sprintf(szFormatInitState, "LAYER_%%0%dd_INIT_STATE", nNewIndex >= 100 ? 3 : 2); */
1531 :
1532 : osLayerList.AddNameValue(CPLSPrintf(szFormatName, nNewIndex),
1533 114 : pszLayerName);
1534 : /*osLayerList.AddNameValue(CPLSPrintf(szFormatInitState, nNewIndex),
1535 : (ocg == NULL || ocg->getState() == OptionalContentGroup::On) ? "ON" : "OFF");*/
1536 114 : oLayerOCGMap[pszLayerName] = ocg;
1537 :
1538 : //if (ocg != NULL && ocg->getState() == OptionalContentGroup::Off)
1539 : // bUseOCG = TRUE;
1540 114 : }
1541 :
1542 : /************************************************************************/
1543 : /* ExploreLayers() */
1544 : /************************************************************************/
1545 :
1546 58 : void PDFDataset::ExploreLayers(GDALPDFArray* poArray, CPLString osTopLayer)
1547 : {
1548 58 : int nLength = poArray->GetLength();
1549 58 : CPLString osCurLayer;
1550 204 : for(int i=0;i<nLength;i++)
1551 : {
1552 146 : GDALPDFObject* poObj = poArray->Get(i);
1553 146 : if (i == 0 && poObj->GetType() == PDFObjectType_String)
1554 : {
1555 2 : CPLString osName = PDFSanitizeLayerName(poObj->GetString().c_str());
1556 2 : if (osTopLayer.size())
1557 0 : osTopLayer = osTopLayer + "." + osName;
1558 : else
1559 2 : osTopLayer = osName;
1560 2 : AddLayer(osTopLayer.c_str(), NULL);
1561 : }
1562 144 : else if (poObj->GetType() == PDFObjectType_Array)
1563 : {
1564 32 : ExploreLayers(poObj->GetArray(), osCurLayer);
1565 32 : osCurLayer = "";
1566 : }
1567 112 : else if (poObj->GetType() == PDFObjectType_Dictionary)
1568 : {
1569 112 : GDALPDFDictionary* poDict = poObj->GetDictionary();
1570 112 : GDALPDFObject* poName = poDict->Get("Name");
1571 112 : if (poName != NULL && poName->GetType() == PDFObjectType_String)
1572 : {
1573 112 : CPLString osName = PDFSanitizeLayerName(poName->GetString().c_str());
1574 112 : if (osTopLayer.size())
1575 70 : osCurLayer = osTopLayer + "." + osName;
1576 : else
1577 42 : osCurLayer = osName;
1578 : //CPLDebug("PDF", "Layer %s", osCurLayer.c_str());
1579 :
1580 112 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
1581 : struct Ref r;
1582 112 : r.num = poObj->GetRefNum();
1583 112 : r.gen = poObj->GetRefGen();
1584 112 : OptionalContentGroup* ocg = optContentConfig->findOcgByRef(r);
1585 112 : if (ocg)
1586 : {
1587 112 : AddLayer(osCurLayer.c_str(), ocg);
1588 112 : }
1589 : }
1590 : }
1591 58 : }
1592 58 : }
1593 :
1594 : /************************************************************************/
1595 : /* FindLayers() */
1596 : /************************************************************************/
1597 :
1598 262 : void PDFDataset::FindLayers()
1599 : {
1600 262 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
1601 262 : if (optContentConfig == NULL || !optContentConfig->isOk() )
1602 236 : return;
1603 :
1604 26 : Array* array = optContentConfig->getOrderArray();
1605 26 : if (array)
1606 : {
1607 26 : GDALPDFArray* poArray = GDALPDFCreateArray(array);
1608 26 : ExploreLayers(poArray);
1609 52 : delete poArray;
1610 : }
1611 : else
1612 : {
1613 0 : GooList* ocgList = optContentConfig->getOCGs();
1614 0 : for(int i=0;i<ocgList->getLength();i++)
1615 : {
1616 0 : OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
1617 0 : AddLayer((const char*)ocg->getName()->getCString(), ocg);
1618 : }
1619 : }
1620 :
1621 26 : oMDMD.SetMetadata(osLayerList.List(), "LAYERS");
1622 : }
1623 :
1624 : /************************************************************************/
1625 : /* TurnLayersOnOff() */
1626 : /************************************************************************/
1627 :
1628 262 : void PDFDataset::TurnLayersOnOff()
1629 : {
1630 262 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
1631 262 : if (optContentConfig == NULL || !optContentConfig->isOk() )
1632 236 : return;
1633 :
1634 : // Which layers to turn ON ?
1635 26 : const char* pszLayers = CPLGetConfigOption("GDAL_PDF_LAYERS", NULL);
1636 26 : if (pszLayers)
1637 : {
1638 : int i;
1639 4 : int bAll = EQUAL(pszLayers, "ALL");
1640 4 : GooList* ocgList = optContentConfig->getOCGs();
1641 24 : for(i=0;i<ocgList->getLength();i++)
1642 : {
1643 20 : OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
1644 20 : ocg->setState( (bAll) ? OptionalContentGroup::On : OptionalContentGroup::Off );
1645 : }
1646 :
1647 4 : char** papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
1648 8 : for(i=0;!bAll && papszLayers[i] != NULL;i++)
1649 : {
1650 : std::map<CPLString, OptionalContentGroup*>::iterator oIter =
1651 4 : oLayerOCGMap.find(papszLayers[i]);
1652 4 : if (oIter != oLayerOCGMap.end())
1653 : {
1654 4 : if (oIter->second)
1655 : {
1656 : //CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
1657 4 : oIter->second->setState(OptionalContentGroup::On);
1658 : }
1659 :
1660 : // Turn child layers on, unless there's one of them explicitely listed
1661 : // in the list.
1662 4 : int nLen = strlen(papszLayers[i]);
1663 4 : int bFoundChildLayer = FALSE;
1664 4 : oIter = oLayerOCGMap.begin();
1665 24 : for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++)
1666 : {
1667 30 : if ((int)oIter->first.size() > nLen &&
1668 10 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
1669 : oIter->first[nLen] == '.')
1670 : {
1671 8 : for(int j=0;papszLayers[j] != NULL;j++)
1672 : {
1673 4 : if (strcmp(papszLayers[j], oIter->first.c_str()) == 0)
1674 0 : bFoundChildLayer = TRUE;
1675 : }
1676 : }
1677 : }
1678 :
1679 4 : if( !bFoundChildLayer )
1680 : {
1681 4 : oIter = oLayerOCGMap.begin();
1682 24 : for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++)
1683 : {
1684 30 : if ((int)oIter->first.size() > nLen &&
1685 10 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
1686 : oIter->first[nLen] == '.')
1687 : {
1688 4 : if (oIter->second)
1689 : {
1690 : //CPLDebug("PDF", "Turn '%s' on too", oIter->first.c_str());
1691 4 : oIter->second->setState(OptionalContentGroup::On);
1692 : }
1693 : }
1694 : }
1695 : }
1696 :
1697 : // Turn parent layers on too
1698 : char* pszLastDot;
1699 10 : while( (pszLastDot = strrchr(papszLayers[i], '.')) != NULL)
1700 : {
1701 2 : *pszLastDot = '\0';
1702 2 : oIter = oLayerOCGMap.find(papszLayers[i]);
1703 2 : if (oIter != oLayerOCGMap.end())
1704 : {
1705 2 : if (oIter->second)
1706 : {
1707 : //CPLDebug("PDF", "Turn '%s' on too", papszLayers[i]);
1708 2 : oIter->second->setState(OptionalContentGroup::On);
1709 : }
1710 : }
1711 : }
1712 : }
1713 : else
1714 : {
1715 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
1716 0 : papszLayers[i]);
1717 : }
1718 : }
1719 4 : CSLDestroy(papszLayers);
1720 :
1721 4 : bUseOCG = TRUE;
1722 : }
1723 :
1724 : // Which layers to turn OFF ?
1725 26 : const char* pszLayersOFF = CPLGetConfigOption("GDAL_PDF_LAYERS_OFF", NULL);
1726 26 : if (pszLayersOFF)
1727 : {
1728 2 : char** papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
1729 4 : for(int i=0;papszLayersOFF[i] != NULL;i++)
1730 : {
1731 : std::map<CPLString, OptionalContentGroup*>::iterator oIter =
1732 2 : oLayerOCGMap.find(papszLayersOFF[i]);
1733 2 : if (oIter != oLayerOCGMap.end())
1734 : {
1735 2 : if (oIter->second)
1736 : {
1737 : //CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]);
1738 2 : oIter->second->setState(OptionalContentGroup::Off);
1739 : }
1740 :
1741 : // Turn child layers off too
1742 2 : int nLen = strlen(papszLayersOFF[i]);
1743 2 : oIter = oLayerOCGMap.begin();
1744 12 : for( ; oIter != oLayerOCGMap.end(); oIter ++)
1745 : {
1746 16 : if ((int)oIter->first.size() > nLen &&
1747 6 : strncmp(oIter->first.c_str(), papszLayersOFF[i], nLen) == 0 &&
1748 : oIter->first[nLen] == '.')
1749 : {
1750 2 : if (oIter->second)
1751 : {
1752 : //CPLDebug("PDF", "Turn '%s' off too", oIter->first.c_str());
1753 2 : oIter->second->setState(OptionalContentGroup::Off);
1754 : }
1755 : }
1756 : }
1757 : }
1758 : else
1759 : {
1760 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
1761 0 : papszLayersOFF[i]);
1762 : }
1763 : }
1764 2 : CSLDestroy(papszLayersOFF);
1765 :
1766 2 : bUseOCG = TRUE;
1767 : }
1768 : }
1769 :
1770 : #endif
1771 :
1772 : /************************************************************************/
1773 : /* Open() */
1774 : /************************************************************************/
1775 :
1776 2932 : GDALDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo )
1777 :
1778 : {
1779 2932 : if (!Identify(poOpenInfo))
1780 2610 : return NULL;
1781 :
1782 322 : const char* pszUserPwd = CPLGetConfigOption("PDF_USER_PWD", NULL);
1783 :
1784 322 : int bOpenSubdataset = strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0;
1785 322 : int iPage = -1;
1786 322 : const char* pszFilename = poOpenInfo->pszFilename;
1787 : char szPassword[81];
1788 :
1789 322 : if (bOpenSubdataset)
1790 : {
1791 2 : iPage = atoi(pszFilename + 4);
1792 2 : if (iPage <= 0)
1793 0 : return NULL;
1794 2 : pszFilename = strchr(pszFilename + 4, ':');
1795 2 : if (pszFilename == NULL)
1796 0 : return NULL;
1797 2 : pszFilename ++;
1798 : }
1799 : else
1800 320 : iPage = 1;
1801 :
1802 : int bUsePoppler;
1803 : #if defined(HAVE_POPPLER) && !defined(HAVE_PODOFO)
1804 : bUsePoppler = TRUE;
1805 : #elif !defined(HAVE_POPPLER) && defined(HAVE_PODOFO)
1806 : bUsePoppler = FALSE;
1807 : #elif defined(HAVE_POPPLER) && defined(HAVE_PODOFO)
1808 322 : const char* pszPDFLib = CPLGetConfigOption("GDAL_PDF_LIB", "POPPLER");
1809 322 : if (EQUAL(pszPDFLib, "POPPLER"))
1810 262 : bUsePoppler = TRUE;
1811 60 : else if (EQUAL(pszPDFLib, "PODOFO"))
1812 60 : bUsePoppler = FALSE;
1813 : else
1814 : {
1815 0 : CPLDebug("PDF", "Invalid value for GDAL_PDF_LIB config option");
1816 0 : bUsePoppler = TRUE;
1817 : }
1818 : #else
1819 : return NULL;
1820 : #endif
1821 :
1822 322 : GDALPDFObject* poPageObj = NULL;
1823 : #ifdef HAVE_POPPLER
1824 322 : PDFDoc* poDocPoppler = NULL;
1825 322 : ObjectAutoFree oObj;
1826 322 : Page* poPagePoppler = NULL;
1827 322 : Catalog* poCatalogPoppler = NULL;
1828 : #endif
1829 : #ifdef HAVE_PODOFO
1830 322 : PoDoFo::PdfMemDocument* poDocPodofo = NULL;
1831 322 : PoDoFo::PdfPage* poPagePodofo = NULL;
1832 : #endif
1833 322 : int nPages = 0;
1834 :
1835 : #ifdef HAVE_POPPLER
1836 322 : if(bUsePoppler)
1837 : {
1838 262 : GooString* poUserPwd = NULL;
1839 :
1840 : /* Set custom error handler for poppler errors */
1841 : #ifdef POPPLER_0_20_OR_LATER
1842 262 : setErrorCallback(PDFDatasetErrorFunction, NULL);
1843 : #else
1844 : setErrorFunction(PDFDatasetErrorFunction);
1845 : #endif
1846 :
1847 : /* poppler global variable */
1848 262 : if (globalParams == NULL)
1849 2 : globalParams = new GlobalParams();
1850 :
1851 : globalParams->setPrintCommands(CSLTestBoolean(
1852 262 : CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE")));
1853 :
1854 0 : while(TRUE)
1855 : {
1856 262 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
1857 262 : if (fp == NULL)
1858 0 : return NULL;
1859 :
1860 262 : fp = (VSILFILE*)VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp);
1861 :
1862 262 : if (pszUserPwd)
1863 0 : poUserPwd = new GooString(pszUserPwd);
1864 :
1865 262 : oObj.initNull();
1866 262 : poDocPoppler = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, 0, gFalse, 0, &oObj), NULL, poUserPwd);
1867 262 : delete poUserPwd;
1868 :
1869 262 : if ( !poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0 )
1870 : {
1871 0 : if (poDocPoppler->getErrorCode() == errEncrypted)
1872 : {
1873 0 : if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
1874 : {
1875 0 : printf( "Enter password (will be echo'ed in the console): " );
1876 0 : fgets( szPassword, sizeof(szPassword), stdin );
1877 0 : szPassword[sizeof(szPassword)-1] = 0;
1878 0 : char* sz10 = strchr(szPassword, '\n');
1879 0 : if (sz10)
1880 0 : *sz10 = 0;
1881 0 : pszUserPwd = szPassword;
1882 0 : PDFFreeDoc(poDocPoppler);
1883 0 : continue;
1884 : }
1885 0 : else if (pszUserPwd == NULL)
1886 : {
1887 : CPLError(CE_Failure, CPLE_AppDefined,
1888 : "A password is needed. You can specify it through the PDF_USER_PWD "
1889 0 : "configuration option (that can be set to ASK_INTERACTIVE)");
1890 : }
1891 : else
1892 : {
1893 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
1894 : }
1895 : }
1896 : else
1897 : {
1898 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
1899 : }
1900 :
1901 0 : PDFFreeDoc(poDocPoppler);
1902 :
1903 0 : return NULL;
1904 : }
1905 : else
1906 : break;
1907 : }
1908 :
1909 262 : poCatalogPoppler = poDocPoppler->getCatalog();
1910 262 : if ( poCatalogPoppler == NULL || !poCatalogPoppler->isOk() )
1911 : {
1912 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid catalog");
1913 0 : PDFFreeDoc(poDocPoppler);
1914 0 : return NULL;
1915 : }
1916 :
1917 : #ifdef dump_catalog
1918 : {
1919 : ObjectAutoFree oCatalog;
1920 : poDocPoppler->getXRef()->getCatalog(&oCatalog);
1921 : GDALPDFObjectPoppler oCatalogGDAL(&oCatalog, FALSE);
1922 : GDALPDFDumper oDumper(stderr);
1923 : oDumper.Dump(&oCatalogGDAL);
1924 : }
1925 : #endif
1926 :
1927 262 : nPages = poDocPoppler->getNumPages();
1928 262 : if (iPage < 1 || iPage > nPages)
1929 : {
1930 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
1931 0 : iPage, nPages);
1932 0 : PDFFreeDoc(poDocPoppler);
1933 0 : return NULL;
1934 : }
1935 :
1936 262 : poPagePoppler = poCatalogPoppler->getPage(iPage);
1937 262 : if ( poPagePoppler == NULL || !poPagePoppler->isOk() )
1938 : {
1939 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
1940 0 : PDFFreeDoc(poDocPoppler);
1941 0 : return NULL;
1942 : }
1943 :
1944 : /* Here's the dirty part: this is a private member */
1945 : /* so we had to #define private public to get it ! */
1946 262 : Object& oPageObj = poPagePoppler->pageObj;
1947 262 : if ( !oPageObj.isDict() )
1948 : {
1949 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : !oPageObj.isDict()");
1950 0 : PDFFreeDoc(poDocPoppler);
1951 0 : return NULL;
1952 : }
1953 :
1954 262 : poPageObj = new GDALPDFObjectPoppler(&oPageObj, FALSE);
1955 524 : Ref* poPageRef = poCatalogPoppler->getPageRef(iPage);
1956 262 : if (poPageRef != NULL)
1957 : {
1958 262 : ((GDALPDFObjectPoppler*)poPageObj)->SetRefNumAndGen(poPageRef->num, poPageRef->gen);
1959 : }
1960 : }
1961 : #endif
1962 :
1963 : #ifdef HAVE_PODOFO
1964 322 : if (!bUsePoppler)
1965 : {
1966 60 : PoDoFo::PdfError::EnableDebug( false );
1967 60 : PoDoFo::PdfError::EnableLogging( false );
1968 :
1969 60 : poDocPodofo = new PoDoFo::PdfMemDocument();
1970 : try
1971 : {
1972 60 : poDocPodofo->Load(pszFilename);
1973 : }
1974 0 : catch(PoDoFo::PdfError& oError)
1975 : {
1976 0 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
1977 : {
1978 0 : if (pszUserPwd)
1979 : {
1980 0 : if (EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
1981 : {
1982 0 : printf( "Enter password (will be echo'ed in the console): " );
1983 0 : fgets( szPassword, sizeof(szPassword), stdin );
1984 0 : szPassword[sizeof(szPassword)-1] = 0;
1985 0 : char* sz10 = strchr(szPassword, '\n');
1986 0 : if (sz10)
1987 0 : *sz10 = 0;
1988 0 : pszUserPwd = szPassword;
1989 : }
1990 :
1991 : try
1992 : {
1993 0 : poDocPodofo->SetPassword(pszUserPwd);
1994 : }
1995 0 : catch(PoDoFo::PdfError& oError)
1996 : {
1997 0 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
1998 : {
1999 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
2000 : }
2001 : else
2002 : {
2003 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
2004 : }
2005 0 : delete poDocPodofo;
2006 0 : return NULL;
2007 : }
2008 0 : catch(...)
2009 : {
2010 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
2011 0 : delete poDocPodofo;
2012 0 : return NULL;
2013 : }
2014 : }
2015 : else
2016 : {
2017 : CPLError(CE_Failure, CPLE_AppDefined,
2018 : "A password is needed. You can specify it through the PDF_USER_PWD "
2019 0 : "configuration option (that can be set to ASK_INTERACTIVE)");
2020 0 : delete poDocPodofo;
2021 0 : return NULL;
2022 : }
2023 : }
2024 : else
2025 : {
2026 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
2027 0 : delete poDocPodofo;
2028 0 : return NULL;
2029 : }
2030 : }
2031 0 : catch(...)
2032 : {
2033 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
2034 0 : delete poDocPodofo;
2035 0 : return NULL;
2036 : }
2037 :
2038 60 : nPages = poDocPodofo->GetPageCount();
2039 60 : if (iPage < 1 || iPage > nPages)
2040 : {
2041 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
2042 0 : iPage, nPages);
2043 0 : delete poDocPodofo;
2044 0 : return NULL;
2045 : }
2046 :
2047 60 : poPagePodofo = poDocPodofo->GetPage(iPage - 1);
2048 60 : if ( poPagePodofo == NULL )
2049 : {
2050 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
2051 0 : delete poDocPodofo;
2052 0 : return NULL;
2053 : }
2054 :
2055 60 : PoDoFo::PdfObject* pObj = poPagePodofo->GetObject();
2056 60 : poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects());
2057 : }
2058 : #endif
2059 :
2060 322 : GDALPDFDictionary* poPageDict = poPageObj->GetDictionary();
2061 322 : if ( poPageDict == NULL )
2062 : {
2063 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : poPageDict == NULL");
2064 : #ifdef HAVE_POPPLER
2065 0 : PDFFreeDoc(poDocPoppler);
2066 : #endif
2067 : #ifdef HAVE_PODOFO
2068 0 : delete poDocPodofo;
2069 : #endif
2070 0 : return NULL;
2071 : }
2072 :
2073 322 : const char* pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", NULL);
2074 322 : if (pszDumpObject != NULL)
2075 : {
2076 : FILE* f;
2077 0 : if (strcmp(pszDumpObject, "stderr") == 0)
2078 0 : f = stderr;
2079 0 : else if (EQUAL(pszDumpObject, "YES"))
2080 0 : f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), "wt");
2081 : else
2082 0 : f = fopen(pszDumpObject, "wt");
2083 0 : if (f == NULL)
2084 0 : f = stderr;
2085 :
2086 0 : GDALPDFDumper oDumper(f);
2087 0 : oDumper.Dump(poPageObj);
2088 0 : if (f != stderr)
2089 0 : fclose(f);
2090 : }
2091 :
2092 322 : PDFDataset* poDS = new PDFDataset();
2093 322 : poDS->bUsePoppler = bUsePoppler;
2094 644 : poDS->osFilename = pszFilename;
2095 322 : poDS->eAccess = poOpenInfo->eAccess;
2096 :
2097 322 : if ( nPages > 1 && !bOpenSubdataset )
2098 : {
2099 : int i;
2100 2 : CPLStringList aosList;
2101 10 : for(i=0;i<nPages;i++)
2102 : {
2103 : char szKey[32];
2104 8 : sprintf( szKey, "SUBDATASET_%d_NAME", i+1 );
2105 8 : aosList.AddNameValue(szKey, CPLSPrintf("PDF:%d:%s", i+1, poOpenInfo->pszFilename));
2106 8 : sprintf( szKey, "SUBDATASET_%d_DESC", i+1 );
2107 8 : aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i+1, poOpenInfo->pszFilename));
2108 : }
2109 2 : poDS->SetMetadata( aosList.List(), "SUBDATASETS" );
2110 : }
2111 :
2112 : #ifdef HAVE_POPPLER
2113 322 : poDS->poDocPoppler = poDocPoppler;
2114 : #endif
2115 : #ifdef HAVE_PODOFO
2116 322 : poDS->poDocPodofo = poDocPodofo;
2117 : #endif
2118 322 : poDS->poPageObj = poPageObj;
2119 322 : poDS->osUserPwd = pszUserPwd ? pszUserPwd : "";
2120 322 : poDS->iPage = iPage;
2121 :
2122 322 : int nBandsGuessed = 0;
2123 322 : poDS->GuessDPI(poPageDict, &nBandsGuessed);
2124 :
2125 322 : double dfX1 = 0.0, dfY1 = 0.0, dfX2 = 0.0, dfY2 = 0.0;
2126 :
2127 : #ifdef HAVE_POPPLER
2128 322 : if (bUsePoppler)
2129 : {
2130 262 : PDFRectangle* psMediaBox = poPagePoppler->getMediaBox();
2131 262 : dfX1 = psMediaBox->x1;
2132 262 : dfY1 = psMediaBox->y1;
2133 262 : dfX2 = psMediaBox->x2;
2134 262 : dfY2 = psMediaBox->y2;
2135 : }
2136 : #endif
2137 :
2138 : #ifdef HAVE_PODOFO
2139 322 : if (!bUsePoppler)
2140 : {
2141 60 : PoDoFo::PdfRect oMediaBox = poPagePodofo->GetMediaBox();
2142 60 : dfX1 = oMediaBox.GetLeft();
2143 60 : dfY1 = oMediaBox.GetBottom();
2144 60 : dfX2 = dfX1 + oMediaBox.GetWidth();
2145 60 : dfY2 = dfY1 + oMediaBox.GetHeight();
2146 : }
2147 : #endif
2148 :
2149 322 : double dfUserUnit = poDS->dfDPI / 72.0;
2150 322 : poDS->nRasterXSize = (int) ((dfX2 - dfX1) * dfUserUnit);
2151 322 : poDS->nRasterYSize = (int) ((dfY2 - dfY1) * dfUserUnit);
2152 :
2153 322 : double dfRotation = 0;
2154 : #ifdef HAVE_POPPLER
2155 322 : if (bUsePoppler)
2156 262 : dfRotation = poDocPoppler->getPageRotate(iPage);
2157 : #endif
2158 :
2159 : #ifdef HAVE_PODOFO
2160 322 : if (!bUsePoppler)
2161 60 : dfRotation = poPagePodofo->GetRotation();
2162 : #endif
2163 322 : if ( dfRotation == 90 ||
2164 : dfRotation == 270 )
2165 : {
2166 : /* FIXME: the non poppler case should be implemented. This needs to rotate */
2167 : /* the output of pdftoppm */
2168 : #ifdef HAVE_POPPLER
2169 0 : if (bUsePoppler)
2170 : {
2171 : /* Wondering how it would work with a georeferenced image */
2172 : /* Has only been tested with ungeoreferenced image */
2173 0 : int nTmp = poDS->nRasterXSize;
2174 0 : poDS->nRasterXSize = poDS->nRasterYSize;
2175 0 : poDS->nRasterYSize = nTmp;
2176 : }
2177 : #endif
2178 : }
2179 :
2180 322 : GDALPDFObject* poLGIDict = NULL;
2181 322 : GDALPDFObject* poVP = NULL;
2182 322 : int bIsOGCBP = FALSE;
2183 322 : if ( (poLGIDict = poPageDict->Get("LGIDict")) != NULL )
2184 : {
2185 : /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */
2186 44 : CPLDebug("PDF", "OGC Encoding Best Practice style detected");
2187 44 : if (poDS->ParseLGIDictObject(poLGIDict))
2188 : {
2189 44 : if (poDS->bHasCTM)
2190 : {
2191 38 : poDS->adfGeoTransform[0] = poDS->adfCTM[4] + poDS->adfCTM[0] * dfX1 + poDS->adfCTM[2] * dfY2;
2192 38 : poDS->adfGeoTransform[1] = poDS->adfCTM[0] / dfUserUnit;
2193 38 : poDS->adfGeoTransform[2] = poDS->adfCTM[1] / dfUserUnit;
2194 38 : poDS->adfGeoTransform[3] = poDS->adfCTM[5] + poDS->adfCTM[1] * dfX1 + poDS->adfCTM[3] * dfY2;
2195 38 : poDS->adfGeoTransform[4] = - poDS->adfCTM[2] / dfUserUnit;
2196 38 : poDS->adfGeoTransform[5] = - poDS->adfCTM[3] / dfUserUnit;
2197 38 : poDS->bGeoTransformValid = TRUE;
2198 : }
2199 :
2200 44 : bIsOGCBP = TRUE;
2201 :
2202 : int i;
2203 74 : for(i=0;i<poDS->nGCPCount;i++)
2204 : {
2205 30 : poDS->pasGCPList[i].dfGCPPixel = poDS->pasGCPList[i].dfGCPPixel * dfUserUnit;
2206 30 : poDS->pasGCPList[i].dfGCPLine = poDS->nRasterYSize - poDS->pasGCPList[i].dfGCPLine * dfUserUnit;
2207 : }
2208 : }
2209 : }
2210 278 : else if ( (poVP = poPageDict->Get("VP")) != NULL )
2211 : {
2212 : /* Cf adobe_supplement_iso32000.pdf */
2213 242 : CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?");
2214 242 : if (dfX1 != 0 || dfY1 != 0)
2215 : {
2216 0 : CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case...");
2217 : }
2218 242 : poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1);
2219 : }
2220 : else
2221 : {
2222 : /* Not a geospatial PDF doc */
2223 : }
2224 :
2225 : /* If pixel size or top left coordinates are very close to an int, round them to the int */
2226 322 : double dfEps = ( fabs(poDS->adfGeoTransform[0]) > 1e5 &&
2227 322 : fabs(poDS->adfGeoTransform[3]) > 1e5 ) ? 1e-5 : 1e-8;
2228 322 : poDS->adfGeoTransform[0] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[0], dfEps);
2229 322 : poDS->adfGeoTransform[1] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[1]);
2230 322 : poDS->adfGeoTransform[3] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[3], dfEps);
2231 322 : poDS->adfGeoTransform[5] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[5]);
2232 :
2233 322 : if (poDS->poNeatLine)
2234 : {
2235 286 : char* pszNeatLineWkt = NULL;
2236 286 : OGRLinearRing* poRing = poDS->poNeatLine->getExteriorRing();
2237 : /* Adobe style is already in target SRS units */
2238 286 : if (bIsOGCBP)
2239 : {
2240 44 : int nPoints = poRing->getNumPoints();
2241 : int i;
2242 :
2243 232 : for(i=0;i<nPoints;i++)
2244 : {
2245 188 : double x = poRing->getX(i) * dfUserUnit;
2246 188 : double y = poDS->nRasterYSize - poRing->getY(i) * dfUserUnit;
2247 188 : double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] +
2248 188 : y * poDS->adfGeoTransform[2];
2249 188 : double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] +
2250 188 : y * poDS->adfGeoTransform[5];
2251 188 : poRing->setPoint(i, X, Y);
2252 : }
2253 : }
2254 286 : poRing->closeRings();
2255 :
2256 286 : poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
2257 286 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
2258 286 : CPLFree(pszNeatLineWkt);
2259 : }
2260 :
2261 :
2262 : #ifdef HAVE_POPPLER
2263 322 : if (bUsePoppler)
2264 : {
2265 262 : GooString* poMetadata = poCatalogPoppler->readMetadata();
2266 262 : if (poMetadata)
2267 : {
2268 20 : char* pszContent = poMetadata->getCString();
2269 20 : if (pszContent != NULL &&
2270 : strncmp(pszContent, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
2271 : {
2272 : char *apszMDList[2];
2273 20 : apszMDList[0] = pszContent;
2274 20 : apszMDList[1] = NULL;
2275 20 : poDS->SetMetadata(apszMDList, "xml:XMP");
2276 : }
2277 20 : delete poMetadata;
2278 : }
2279 :
2280 : /* Read Info object */
2281 262 : Object oInfo;
2282 262 : poDocPoppler->getDocInfo(&oInfo);
2283 262 : GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE);
2284 262 : poDS->ParseInfo(&oInfoObjPoppler);
2285 262 : oInfo.free();
2286 :
2287 : /* Find layers */
2288 262 : poDS->FindLayers();
2289 :
2290 : /* Turn user specified layers on or off */
2291 262 : poDS->TurnLayersOnOff();
2292 : }
2293 : #endif
2294 :
2295 : #ifdef HAVE_PODOFO
2296 322 : if (!bUsePoppler)
2297 : {
2298 60 : PoDoFo::TIVecObjects it = poDocPodofo->GetObjects().begin();
2299 828 : for( ; it != poDocPodofo->GetObjects().end(); ++it )
2300 : {
2301 768 : GDALPDFObjectPodofo oObjPodofo((*it), poDocPodofo->GetObjects());
2302 768 : poDS->FindXMP(&oObjPodofo);
2303 : }
2304 :
2305 : /* Read Info object */
2306 60 : PoDoFo::PdfInfo* poInfo = poDocPodofo->GetInfo();
2307 60 : if (poInfo != NULL)
2308 : {
2309 60 : GDALPDFObjectPodofo oInfoObjPodofo(poInfo->GetObject(), poDocPodofo->GetObjects());
2310 60 : poDS->ParseInfo(&oInfoObjPodofo);
2311 : }
2312 : }
2313 : #endif
2314 :
2315 322 : int nBands = 3;
2316 : /*if( nBandsGuessed )
2317 : nBands = nBandsGuessed;*/
2318 322 : const char* pszPDFBands = CPLGetConfigOption("GDAL_PDF_BANDS", NULL);
2319 322 : if( pszPDFBands )
2320 2 : nBands = atoi(pszPDFBands);
2321 322 : if (nBands != 3 && nBands != 4)
2322 : {
2323 : CPLError(CE_Warning, CPLE_NotSupported,
2324 0 : "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback");
2325 0 : nBands = 3;
2326 : }
2327 : #ifdef HAVE_PODOFO
2328 322 : if (!bUsePoppler && nBands == 4)
2329 : {
2330 : CPLError(CE_Warning, CPLE_NotSupported,
2331 : "GDAL_PDF_BANDS=4 only supported when PDF driver is compiled against Poppler. "
2332 0 : "Using 3 as a fallback");
2333 0 : nBands = 3;
2334 : }
2335 : #endif
2336 :
2337 : int iBand;
2338 1290 : for(iBand = 1; iBand <= nBands; iBand ++)
2339 968 : poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand));
2340 :
2341 : /* -------------------------------------------------------------------- */
2342 : /* Initialize any PAM information. */
2343 : /* -------------------------------------------------------------------- */
2344 322 : poDS->SetDescription( poOpenInfo->pszFilename );
2345 322 : poDS->TryLoadXML();
2346 :
2347 : /* -------------------------------------------------------------------- */
2348 : /* Support overviews. */
2349 : /* -------------------------------------------------------------------- */
2350 322 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
2351 :
2352 : /* Clear dirty flag */
2353 322 : poDS->bProjDirty = FALSE;
2354 322 : poDS->bNeatLineDirty = FALSE;
2355 322 : poDS->bInfoDirty = FALSE;
2356 322 : poDS->bXMPDirty = FALSE;
2357 :
2358 322 : return( poDS );
2359 : }
2360 :
2361 : /************************************************************************/
2362 : /* ParseLGIDictObject() */
2363 : /************************************************************************/
2364 :
2365 44 : int PDFDataset::ParseLGIDictObject(GDALPDFObject* poLGIDict)
2366 : {
2367 : int i;
2368 44 : int bOK = FALSE;
2369 44 : if (poLGIDict->GetType() == PDFObjectType_Array)
2370 : {
2371 4 : GDALPDFArray* poArray = poLGIDict->GetArray();
2372 4 : int nArrayLength = poArray->GetLength();
2373 4 : int iMax = -1;
2374 : GDALPDFObject* poArrayElt;
2375 8 : for (i=0; i<nArrayLength; i++)
2376 : {
2377 8 : if ( (poArrayElt = poArray->Get(i)) == NULL ||
2378 4 : poArrayElt->GetType() != PDFObjectType_Dictionary )
2379 : {
2380 : CPLError(CE_Failure, CPLE_AppDefined,
2381 0 : "LGIDict[%d] is not a dictionary", i);
2382 0 : return FALSE;
2383 : }
2384 :
2385 4 : int bIsLargestArea = FALSE;
2386 4 : if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), &bIsLargestArea))
2387 : {
2388 4 : if (bIsLargestArea || iMax < 0)
2389 4 : iMax = i;
2390 : }
2391 : }
2392 :
2393 4 : if (iMax < 0)
2394 0 : return FALSE;
2395 :
2396 4 : poArrayElt = poArray->Get(iMax);
2397 4 : bOK = ParseLGIDictDictSecondPass(poArrayElt->GetDictionary());
2398 : }
2399 40 : else if (poLGIDict->GetType() == PDFObjectType_Dictionary)
2400 : {
2401 40 : bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) &&
2402 40 : ParseLGIDictDictSecondPass(poLGIDict->GetDictionary());
2403 : }
2404 : else
2405 : {
2406 : CPLError(CE_Failure, CPLE_AppDefined,
2407 0 : "LGIDict is of type %s", poLGIDict->GetTypeName());
2408 : }
2409 :
2410 44 : return bOK;
2411 : }
2412 :
2413 : /************************************************************************/
2414 : /* Get() */
2415 : /************************************************************************/
2416 :
2417 18162 : static double Get(GDALPDFObject* poObj, int nIndice)
2418 : {
2419 18162 : if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0)
2420 : {
2421 8900 : poObj = poObj->GetArray()->Get(nIndice);
2422 8900 : if (poObj == NULL)
2423 0 : return 0;
2424 8900 : return Get(poObj);
2425 : }
2426 9262 : else if (poObj->GetType() == PDFObjectType_Int)
2427 6846 : return poObj->GetInt();
2428 2416 : else if (poObj->GetType() == PDFObjectType_Real)
2429 1914 : return poObj->GetReal();
2430 502 : else if (poObj->GetType() == PDFObjectType_String)
2431 : {
2432 502 : const char* pszStr = poObj->GetString().c_str();
2433 502 : int nLen = strlen(pszStr);
2434 : /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" */
2435 502 : char chLast = pszStr[nLen-1];
2436 502 : if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
2437 : {
2438 0 : double dfDeg = atof(pszStr);
2439 0 : double dfMin = 0, dfSec = 0;
2440 0 : const char* pszNext = strchr(pszStr, ' ');
2441 0 : if (pszNext)
2442 0 : pszNext ++;
2443 0 : if (pszNext)
2444 0 : dfMin = atof(pszNext);
2445 0 : if (pszNext)
2446 0 : pszNext = strchr(pszNext, ' ');
2447 0 : if (pszNext)
2448 0 : pszNext ++;
2449 0 : if (pszNext)
2450 0 : dfSec = atof(pszNext);
2451 0 : double dfVal = dfDeg + dfMin / 60 + dfSec / 3600;
2452 0 : if (chLast == 'W' || chLast == 'S')
2453 0 : return -dfVal;
2454 : else
2455 0 : return dfVal;
2456 : }
2457 502 : return atof(pszStr);
2458 : }
2459 : else
2460 : {
2461 : CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s",
2462 0 : poObj->GetTypeName());
2463 0 : return 0;
2464 : }
2465 : }
2466 :
2467 : /************************************************************************/
2468 : /* Get() */
2469 : /************************************************************************/
2470 :
2471 28 : static double Get(GDALPDFDictionary* poDict, const char* pszName)
2472 : {
2473 : GDALPDFObject* poObj;
2474 28 : if ( (poObj = poDict->Get(pszName)) != NULL )
2475 28 : return Get(poObj);
2476 : CPLError(CE_Failure, CPLE_AppDefined,
2477 0 : "Cannot find parameter %s", pszName);
2478 0 : return 0;
2479 : }
2480 :
2481 : /************************************************************************/
2482 : /* ParseLGIDictDictFirstPass() */
2483 : /************************************************************************/
2484 :
2485 44 : int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict,
2486 : int* pbIsLargestArea)
2487 : {
2488 : int i;
2489 :
2490 44 : if (pbIsLargestArea)
2491 4 : *pbIsLargestArea = FALSE;
2492 :
2493 44 : if (poLGIDict == NULL)
2494 0 : return FALSE;
2495 :
2496 : /* -------------------------------------------------------------------- */
2497 : /* Extract Type attribute */
2498 : /* -------------------------------------------------------------------- */
2499 : GDALPDFObject* poType;
2500 44 : if ((poType = poLGIDict->Get("Type")) == NULL)
2501 : {
2502 : CPLError(CE_Failure, CPLE_AppDefined,
2503 0 : "Cannot find Type of LGIDict object");
2504 0 : return FALSE;
2505 : }
2506 :
2507 44 : if ( poType->GetType() != PDFObjectType_Name )
2508 : {
2509 : CPLError(CE_Failure, CPLE_AppDefined,
2510 0 : "Invalid type for Type of LGIDict object");
2511 0 : return FALSE;
2512 : }
2513 :
2514 44 : if ( strcmp(poType->GetName().c_str(), "LGIDict") != 0 )
2515 : {
2516 : CPLError(CE_Failure, CPLE_AppDefined,
2517 : "Invalid value for Type of LGIDict object : %s",
2518 0 : poType->GetName().c_str());
2519 0 : return FALSE;
2520 : }
2521 :
2522 : /* -------------------------------------------------------------------- */
2523 : /* Extract Version attribute */
2524 : /* -------------------------------------------------------------------- */
2525 : GDALPDFObject* poVersion;
2526 44 : if ((poVersion = poLGIDict->Get("Version")) == NULL)
2527 : {
2528 : CPLError(CE_Failure, CPLE_AppDefined,
2529 0 : "Cannot find Version of LGIDict object");
2530 0 : return FALSE;
2531 : }
2532 :
2533 44 : if ( poVersion->GetType() == PDFObjectType_String )
2534 : {
2535 : /* OGC best practice is 2.1 */
2536 : CPLDebug("PDF", "LGIDict Version : %s",
2537 40 : poVersion->GetString().c_str());
2538 : }
2539 4 : else if (poVersion->GetType() == PDFObjectType_Int)
2540 : {
2541 : /* Old TerraGo is 2 */
2542 : CPLDebug("PDF", "LGIDict Version : %d",
2543 4 : poVersion->GetInt());
2544 : }
2545 :
2546 : /* -------------------------------------------------------------------- */
2547 : /* Extract Neatline attribute */
2548 : /* -------------------------------------------------------------------- */
2549 : GDALPDFObject* poNeatline;
2550 88 : if ((poNeatline = poLGIDict->Get("Neatline")) != NULL &&
2551 44 : poNeatline->GetType() == PDFObjectType_Array)
2552 : {
2553 44 : int nLength = poNeatline->GetArray()->GetLength();
2554 44 : if ( (nLength % 2) != 0 || nLength < 4 )
2555 : {
2556 : CPLError(CE_Failure, CPLE_AppDefined,
2557 0 : "Invalid length for Neatline");
2558 0 : return FALSE;
2559 : }
2560 :
2561 44 : double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0;
2562 232 : for(i=0;i<nLength;i+=2)
2563 : {
2564 188 : double dfX = Get(poNeatline, i);
2565 188 : double dfY = Get(poNeatline, i + 1);
2566 188 : if (i == 0 || dfX < dfMinX) dfMinX = dfX;
2567 188 : if (i == 0 || dfY < dfMinY) dfMinY = dfY;
2568 188 : if (i == 0 || dfX > dfMaxX) dfMaxX = dfX;
2569 188 : if (i == 0 || dfY > dfMaxY) dfMaxY = dfY;
2570 : }
2571 44 : double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY);
2572 44 : if (dfArea < dfMaxArea)
2573 : {
2574 0 : CPLDebug("PDF", "Not the largest neatline. Skipping it");
2575 0 : return TRUE;
2576 : }
2577 :
2578 44 : CPLDebug("PDF", "This is a the largest neatline for now");
2579 44 : dfMaxArea = dfArea;
2580 44 : if (pbIsLargestArea)
2581 4 : *pbIsLargestArea = TRUE;
2582 :
2583 44 : delete poNeatLine;
2584 44 : poNeatLine = new OGRPolygon();
2585 88 : OGRLinearRing* poRing = new OGRLinearRing();
2586 44 : if (nLength == 4)
2587 : {
2588 : /* 2 points only ? They are the bounding box */
2589 0 : double dfX1 = Get(poNeatline, 0);
2590 0 : double dfY1 = Get(poNeatline, 1);
2591 0 : double dfX2 = Get(poNeatline, 2);
2592 0 : double dfY2 = Get(poNeatline, 3);
2593 0 : poRing->addPoint(dfX1, dfY1);
2594 0 : poRing->addPoint(dfX2, dfY1);
2595 0 : poRing->addPoint(dfX2, dfY2);
2596 0 : poRing->addPoint(dfX1, dfY2);
2597 : }
2598 : else
2599 : {
2600 232 : for(i=0;i<nLength;i+=2)
2601 : {
2602 188 : double dfX = Get(poNeatline, i);
2603 188 : double dfY = Get(poNeatline, i + 1);
2604 188 : poRing->addPoint(dfX, dfY);
2605 : }
2606 : }
2607 44 : poNeatLine->addRingDirectly(poRing);
2608 : }
2609 :
2610 44 : return TRUE;
2611 : }
2612 :
2613 : /************************************************************************/
2614 : /* ParseLGIDictDictSecondPass() */
2615 : /************************************************************************/
2616 :
2617 44 : int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict)
2618 : {
2619 : int i;
2620 :
2621 : /* -------------------------------------------------------------------- */
2622 : /* Extract Description attribute */
2623 : /* -------------------------------------------------------------------- */
2624 : GDALPDFObject* poDescription;
2625 88 : if ( (poDescription = poLGIDict->Get("Description")) != NULL &&
2626 44 : poDescription->GetType() == PDFObjectType_String )
2627 : {
2628 44 : CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str());
2629 : }
2630 :
2631 : /* -------------------------------------------------------------------- */
2632 : /* Extract CTM attribute */
2633 : /* -------------------------------------------------------------------- */
2634 : GDALPDFObject* poCTM;
2635 44 : bHasCTM = FALSE;
2636 82 : if ((poCTM = poLGIDict->Get("CTM")) != NULL &&
2637 38 : poCTM->GetType() == PDFObjectType_Array)
2638 : {
2639 38 : int nLength = poCTM->GetArray()->GetLength();
2640 38 : if ( nLength != 6 )
2641 : {
2642 : CPLError(CE_Failure, CPLE_AppDefined,
2643 0 : "Invalid length for CTM");
2644 0 : return FALSE;
2645 : }
2646 :
2647 38 : bHasCTM = TRUE;
2648 266 : for(i=0;i<nLength;i++)
2649 : {
2650 228 : adfCTM[i] = Get(poCTM, i);
2651 : /* Nullify rotation terms that are significantly smaller than */
2652 : /* scaling termes */
2653 228 : if ((i == 1 || i == 2) && fabs(adfCTM[i]) < fabs(adfCTM[0]) * 1e-10)
2654 76 : adfCTM[i] = 0;
2655 228 : CPLDebug("PDF", "CTM[%d] = %.16g", i, adfCTM[i]);
2656 : }
2657 : }
2658 :
2659 : /* -------------------------------------------------------------------- */
2660 : /* Extract Registration attribute */
2661 : /* -------------------------------------------------------------------- */
2662 : GDALPDFObject* poRegistration;
2663 52 : if ((poRegistration = poLGIDict->Get("Registration")) != NULL &&
2664 8 : poRegistration->GetType() == PDFObjectType_Array)
2665 : {
2666 8 : GDALPDFArray* poRegistrationArray = poRegistration->GetArray();
2667 8 : int nLength = poRegistrationArray->GetLength();
2668 8 : if (nLength > 4 || (!bHasCTM && nLength >= 2) )
2669 : {
2670 6 : nGCPCount = 0;
2671 6 : pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nLength);
2672 :
2673 36 : for(i=0;i<nLength;i++)
2674 : {
2675 30 : GDALPDFObject* poGCP = poRegistrationArray->Get(i);
2676 120 : if ( poGCP != NULL &&
2677 30 : poGCP->GetType() == PDFObjectType_Array &&
2678 60 : poGCP->GetArray()->GetLength() == 4 )
2679 : {
2680 30 : double dfUserX = Get(poGCP, 0);
2681 30 : double dfUserY = Get(poGCP, 1);
2682 30 : double dfX = Get(poGCP, 2);
2683 30 : double dfY = Get(poGCP, 3);
2684 30 : CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX);
2685 30 : CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY);
2686 30 : CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX);
2687 30 : CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY);
2688 :
2689 : char szID[32];
2690 30 : sprintf( szID, "%d", nGCPCount+1 );
2691 30 : pasGCPList[nGCPCount].pszId = CPLStrdup( szID );
2692 30 : pasGCPList[nGCPCount].pszInfo = CPLStrdup("");
2693 30 : pasGCPList[nGCPCount].dfGCPPixel = dfUserX;
2694 30 : pasGCPList[nGCPCount].dfGCPLine = dfUserY;
2695 30 : pasGCPList[nGCPCount].dfGCPX = dfX;
2696 30 : pasGCPList[nGCPCount].dfGCPY = dfY;
2697 30 : nGCPCount ++;
2698 : }
2699 : }
2700 : }
2701 : }
2702 :
2703 44 : if (!bHasCTM && nGCPCount == 0)
2704 : {
2705 0 : CPLDebug("PDF", "Neither CTM nor Registration found");
2706 0 : return FALSE;
2707 : }
2708 :
2709 : /* -------------------------------------------------------------------- */
2710 : /* Extract Projection attribute */
2711 : /* -------------------------------------------------------------------- */
2712 : GDALPDFObject* poProjection;
2713 88 : if ((poProjection = poLGIDict->Get("Projection")) == NULL ||
2714 44 : poProjection->GetType() != PDFObjectType_Dictionary)
2715 : {
2716 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection");
2717 0 : return FALSE;
2718 : }
2719 :
2720 44 : return ParseProjDict(poProjection->GetDictionary());
2721 : }
2722 :
2723 : /************************************************************************/
2724 : /* ParseProjDict() */
2725 : /************************************************************************/
2726 :
2727 44 : int PDFDataset::ParseProjDict(GDALPDFDictionary* poProjDict)
2728 : {
2729 44 : if (poProjDict == NULL)
2730 0 : return FALSE;
2731 44 : OGRSpatialReference oSRS;
2732 :
2733 : /* -------------------------------------------------------------------- */
2734 : /* Extract WKT attribute (GDAL extension) */
2735 : /* -------------------------------------------------------------------- */
2736 : GDALPDFObject* poWKT;
2737 60 : if ( (poWKT = poProjDict->Get("WKT")) != NULL &&
2738 16 : poWKT->GetType() == PDFObjectType_String &&
2739 : CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE") ) )
2740 : {
2741 16 : CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it");
2742 16 : const char* pszWKTRead = poWKT->GetString().c_str();
2743 16 : CPLFree(pszWKT);
2744 16 : pszWKT = CPLStrdup(pszWKTRead);
2745 16 : return TRUE;
2746 : }
2747 :
2748 : /* -------------------------------------------------------------------- */
2749 : /* Extract Type attribute */
2750 : /* -------------------------------------------------------------------- */
2751 : GDALPDFObject* poType;
2752 28 : if ((poType = poProjDict->Get("Type")) == NULL)
2753 : {
2754 : CPLError(CE_Failure, CPLE_AppDefined,
2755 0 : "Cannot find Type of Projection object");
2756 0 : return FALSE;
2757 : }
2758 :
2759 28 : if ( poType->GetType() != PDFObjectType_Name )
2760 : {
2761 : CPLError(CE_Failure, CPLE_AppDefined,
2762 0 : "Invalid type for Type of Projection object");
2763 0 : return FALSE;
2764 : }
2765 :
2766 28 : if ( strcmp(poType->GetName().c_str(), "Projection") != 0 )
2767 : {
2768 : CPLError(CE_Failure, CPLE_AppDefined,
2769 : "Invalid value for Type of Projection object : %s",
2770 0 : poType->GetName().c_str());
2771 0 : return FALSE;
2772 : }
2773 :
2774 : /* -------------------------------------------------------------------- */
2775 : /* Extract Datum attribute */
2776 : /* -------------------------------------------------------------------- */
2777 28 : int bIsWGS84 = FALSE;
2778 28 : int bIsNAD83 = FALSE;
2779 28 : int bIsNAD27 = FALSE;
2780 :
2781 : GDALPDFObject* poDatum;
2782 28 : if ((poDatum = poProjDict->Get("Datum")) != NULL)
2783 : {
2784 28 : if (poDatum->GetType() == PDFObjectType_String)
2785 : {
2786 28 : const char* pszDatum = poDatum->GetString().c_str();
2787 28 : CPLDebug("PDF", "Datum = %s", pszDatum);
2788 36 : if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE"))
2789 : {
2790 8 : bIsWGS84 = TRUE;
2791 8 : oSRS.SetWellKnownGeogCS("WGS84");
2792 : }
2793 20 : else if (EQUAL(pszDatum, "NAR") || EQUALN(pszDatum, "NAR-", 4))
2794 : {
2795 0 : bIsNAD83 = TRUE;
2796 0 : oSRS.SetWellKnownGeogCS("NAD83");
2797 : }
2798 38 : else if (EQUAL(pszDatum, "NAS") || EQUALN(pszDatum, "NAS-", 4))
2799 : {
2800 18 : bIsNAD27 = TRUE;
2801 18 : oSRS.SetWellKnownGeogCS("NAD27");
2802 : }
2803 2 : else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */
2804 : {
2805 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
2806 : "unknown" /*const char * pszDatumName */,
2807 : "International 1924",
2808 2 : 6378388,297);
2809 2 : oSRS.SetTOWGS84(-333,-222,114);
2810 : }
2811 0 : else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */
2812 : {
2813 0 : oSRS.importFromEPSG(4131);
2814 : }
2815 0 : else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */
2816 : {
2817 0 : oSRS.importFromEPSG(4283);
2818 : }
2819 : else
2820 : {
2821 : CPLError(CE_Warning, CPLE_AppDefined,
2822 : "Unhandled (yet) value for Datum : %s. Defaulting to WGS84...",
2823 0 : pszDatum);
2824 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
2825 : "unknown" /*const char * pszDatumName */,
2826 : "unknown",
2827 0 : 6378137,298.257223563);
2828 : }
2829 : }
2830 0 : else if (poDatum->GetType() == PDFObjectType_Dictionary)
2831 : {
2832 0 : GDALPDFDictionary* poDatumDict = poDatum->GetDictionary();
2833 :
2834 0 : GDALPDFObject* poDatumDescription = poDatumDict->Get("Description");
2835 0 : const char* pszDatumDescription = "unknown";
2836 0 : if (poDatumDescription != NULL &&
2837 0 : poDatumDescription->GetType() == PDFObjectType_String)
2838 0 : pszDatumDescription = poDatumDescription->GetString().c_str();
2839 0 : CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription);
2840 :
2841 0 : GDALPDFObject* poEllipsoid = poDatumDict->Get("Ellipsoid");
2842 0 : if (poEllipsoid == NULL ||
2843 0 : !(poEllipsoid->GetType() == PDFObjectType_String ||
2844 0 : poEllipsoid->GetType() == PDFObjectType_Dictionary))
2845 : {
2846 : CPLError(CE_Warning, CPLE_AppDefined,
2847 0 : "Cannot find Ellipsoid in Datum. Defaulting to WGS84...");
2848 : oSRS.SetGeogCS( "unknown",
2849 : pszDatumDescription,
2850 : "unknown",
2851 0 : 6378137,298.257223563);
2852 : }
2853 0 : else if (poEllipsoid->GetType() == PDFObjectType_String)
2854 : {
2855 0 : const char* pszEllipsoid = poEllipsoid->GetString().c_str();
2856 0 : CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid);
2857 0 : if( EQUAL(pszEllipsoid, "WE") )
2858 : {
2859 : oSRS.SetGeogCS( "unknown",
2860 : pszDatumDescription,
2861 : "WGS 84",
2862 0 : 6378137,298.257223563);
2863 : }
2864 : else
2865 : {
2866 : CPLError(CE_Warning, CPLE_AppDefined,
2867 : "Unhandled (yet) value for Ellipsoid : %s. Defaulting to WGS84...",
2868 0 : pszEllipsoid);
2869 : oSRS.SetGeogCS( "unknown",
2870 : pszDatumDescription,
2871 : pszEllipsoid,
2872 0 : 6378137,298.257223563);
2873 : }
2874 : }
2875 : else// if (poEllipsoid->GetType() == PDFObjectType_Dictionary)
2876 : {
2877 0 : GDALPDFDictionary* poEllipsoidDict = poEllipsoid->GetDictionary();
2878 :
2879 0 : GDALPDFObject* poEllipsoidDescription = poEllipsoidDict->Get("Description");
2880 0 : const char* pszEllipsoidDescription = "unknown";
2881 0 : if (poEllipsoidDescription != NULL &&
2882 0 : poEllipsoidDescription->GetType() == PDFObjectType_String)
2883 0 : pszEllipsoidDescription = poEllipsoidDescription->GetString().c_str();
2884 0 : CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", pszEllipsoidDescription);
2885 :
2886 0 : double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis");
2887 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", dfSemiMajor);
2888 0 : double dfInvFlattening = -1.0;
2889 :
2890 0 : if( poEllipsoidDict->Get("InvFlattening") )
2891 : {
2892 0 : dfInvFlattening = Get(poEllipsoidDict, "InvFlattening");
2893 0 : CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", dfInvFlattening);
2894 : }
2895 0 : else if( poEllipsoidDict->Get("SemiMinorAxis") )
2896 : {
2897 0 : double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis");
2898 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", dfSemiMinor);
2899 0 : if( ABS(dfSemiMajor/dfSemiMinor) - 1.0 < 0.0000000000001 )
2900 0 : dfInvFlattening = 0.0;
2901 : else
2902 0 : dfInvFlattening = -1.0 / (dfSemiMinor/dfSemiMajor - 1.0);
2903 : }
2904 :
2905 0 : if( dfSemiMajor != 0.0 && dfInvFlattening != -1.0 )
2906 : {
2907 : oSRS.SetGeogCS( "unknown",
2908 : pszDatumDescription,
2909 : pszEllipsoidDescription,
2910 0 : dfSemiMajor, dfInvFlattening);
2911 : }
2912 : else
2913 : {
2914 : CPLError(CE_Warning, CPLE_AppDefined,
2915 0 : "Invalid Ellipsoid object. Defaulting to WGS84...");
2916 : oSRS.SetGeogCS( "unknown",
2917 : pszDatumDescription,
2918 : pszEllipsoidDescription,
2919 0 : 6378137,298.257223563);
2920 : }
2921 :
2922 :
2923 : }
2924 :
2925 0 : GDALPDFObject* poTOWGS84 = poDatumDict->Get("ToWGS84");
2926 0 : if( poTOWGS84 != NULL && poTOWGS84->GetType() == PDFObjectType_Dictionary )
2927 : {
2928 0 : GDALPDFDictionary* poTOWGS84Dict = poTOWGS84->GetDictionary();
2929 0 : double dx = Get(poTOWGS84Dict, "dx");
2930 0 : double dy = Get(poTOWGS84Dict, "dy");
2931 0 : double dz = Get(poTOWGS84Dict, "dz");
2932 0 : if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") &&
2933 0 : poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf"))
2934 : {
2935 0 : double rx = Get(poTOWGS84Dict, "rx");
2936 0 : double ry = Get(poTOWGS84Dict, "ry");
2937 0 : double rz = Get(poTOWGS84Dict, "rz");
2938 0 : double sf = Get(poTOWGS84Dict, "sf");
2939 0 : oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf);
2940 : }
2941 : else
2942 : {
2943 0 : oSRS.SetTOWGS84(dx, dy, dz);
2944 : }
2945 : }
2946 : }
2947 : }
2948 :
2949 : /* -------------------------------------------------------------------- */
2950 : /* Extract Hemisphere attribute */
2951 : /* -------------------------------------------------------------------- */
2952 28 : CPLString osHemisphere;
2953 : GDALPDFObject* poHemisphere;
2954 46 : if ((poHemisphere = poProjDict->Get("Hemisphere")) != NULL &&
2955 18 : poHemisphere->GetType() == PDFObjectType_String)
2956 : {
2957 18 : osHemisphere = poHemisphere->GetString();
2958 : }
2959 :
2960 : /* -------------------------------------------------------------------- */
2961 : /* Extract ProjectionType attribute */
2962 : /* -------------------------------------------------------------------- */
2963 : GDALPDFObject* poProjectionType;
2964 56 : if ((poProjectionType = poProjDict->Get("ProjectionType")) == NULL ||
2965 28 : poProjectionType->GetType() != PDFObjectType_String)
2966 : {
2967 : CPLError(CE_Failure, CPLE_AppDefined,
2968 0 : "Cannot find ProjectionType of Projection object");
2969 0 : return FALSE;
2970 : }
2971 28 : CPLString osProjectionType(poProjectionType->GetString());
2972 28 : CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str());
2973 :
2974 : /* Unhandled: NONE, GEODETIC */
2975 :
2976 28 : if (EQUAL(osProjectionType, "GEOGRAPHIC"))
2977 : {
2978 : /* Nothing to do */
2979 : }
2980 :
2981 : /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */
2982 :
2983 20 : else if (EQUAL(osProjectionType, "UT")) /* UTM */
2984 : {
2985 18 : int nZone = (int)Get(poProjDict, "Zone");
2986 18 : int bNorth = EQUAL(osHemisphere, "N");
2987 18 : if (bIsWGS84)
2988 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
2989 : else
2990 18 : oSRS.SetUTM( nZone, bNorth );
2991 : }
2992 :
2993 2 : else if (EQUAL(osProjectionType, "UP")) /* Universal Polar Stereographic (UPS) */
2994 : {
2995 0 : int bNorth = EQUAL(osHemisphere, "N");
2996 0 : if (bIsWGS84)
2997 0 : oSRS.importFromEPSG( (bNorth) ? 32661 : 32761 );
2998 : else
2999 : oSRS.SetPS( (bNorth) ? 90 : -90, 0,
3000 0 : 0.994, 200000, 200000 );
3001 : }
3002 :
3003 2 : else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */
3004 : {
3005 0 : int nZone = (int)Get(poProjDict, "Zone");
3006 0 : oSRS.SetStatePlane( nZone, bIsNAD83 );
3007 : }
3008 :
3009 2 : else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */
3010 : {
3011 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
3012 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
3013 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3014 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3015 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3016 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3017 : oSRS.SetACEA( dfStdP1, dfStdP2,
3018 : dfCenterLat, dfCenterLong,
3019 0 : dfFalseEasting, dfFalseNorthing );
3020 : }
3021 :
3022 2 : else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */
3023 : {
3024 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3025 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3026 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3027 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3028 : oSRS.SetAE( dfCenterLat, dfCenterLong,
3029 0 : dfFalseEasting, dfFalseNorthing );
3030 : }
3031 :
3032 2 : else if (EQUAL(osProjectionType, "BF")) /* Bonne */
3033 : {
3034 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
3035 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
3036 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3037 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3038 : oSRS.SetBonne( dfStdP1, dfCentralMeridian,
3039 0 : dfFalseEasting, dfFalseNorthing );
3040 : }
3041 :
3042 2 : else if (EQUAL(osProjectionType, "CS")) /* Cassini */
3043 : {
3044 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3045 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3046 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3047 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3048 : oSRS.SetCS( dfCenterLat, dfCenterLong,
3049 0 : dfFalseEasting, dfFalseNorthing );
3050 : }
3051 :
3052 2 : else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */
3053 : {
3054 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
3055 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
3056 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3057 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3058 : oSRS.SetCEA( dfStdP1, dfCentralMeridian,
3059 0 : dfFalseEasting, dfFalseNorthing );
3060 : }
3061 :
3062 2 : else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */
3063 : {
3064 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
3065 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3066 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3067 : oSRS.SetEckertIV( dfCentralMeridian,
3068 0 : dfFalseEasting, dfFalseNorthing );
3069 : }
3070 :
3071 2 : else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */
3072 : {
3073 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
3074 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3075 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3076 : oSRS.SetEckertVI( dfCentralMeridian,
3077 0 : dfFalseEasting, dfFalseNorthing );
3078 : }
3079 :
3080 2 : else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */
3081 : {
3082 0 : double dfCenterLat = Get(poProjDict, "StandardParallel");
3083 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3084 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3085 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3086 : oSRS.SetEquirectangular( dfCenterLat, dfCenterLong,
3087 0 : dfFalseEasting, dfFalseNorthing );
3088 : }
3089 :
3090 2 : else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */
3091 : {
3092 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3093 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3094 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3095 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3096 : oSRS.SetGnomonic(dfCenterLat, dfCenterLong,
3097 0 : dfFalseEasting, dfFalseNorthing );
3098 : }
3099 :
3100 2 : else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */
3101 : {
3102 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
3103 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
3104 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3105 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3106 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3107 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3108 : oSRS.SetLCC( dfStdP1, dfStdP2,
3109 : dfCenterLat, dfCenterLong,
3110 0 : dfFalseEasting, dfFalseNorthing );
3111 : }
3112 :
3113 2 : else if (EQUAL(osProjectionType, "MC")) /* Mercator */
3114 : {
3115 : #ifdef not_supported
3116 : if (poProjDict->Get("StandardParallelOne") == NULL)
3117 : {
3118 : #endif
3119 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3120 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3121 0 : double dfScale = Get(poProjDict, "ScaleFactor");
3122 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3123 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3124 : oSRS.SetMercator( dfCenterLat, dfCenterLong,
3125 : dfScale,
3126 0 : dfFalseEasting, dfFalseNorthing );
3127 : #ifdef not_supported
3128 : }
3129 : else
3130 : {
3131 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
3132 : double dfCenterLat = poProjDict->Get("OriginLatitude") ? Get(poProjDict, "OriginLatitude") : 0;
3133 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3134 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3135 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3136 : oSRS.SetMercator2SP( dfStdP1,
3137 : dfCenterLat, dfCenterLong,
3138 : dfFalseEasting, dfFalseNorthing );
3139 : }
3140 : #endif
3141 : }
3142 :
3143 2 : else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */
3144 : {
3145 0 : double dfCenterLat = 0 /* ? */;
3146 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3147 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3148 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3149 : oSRS.SetMC( dfCenterLat, dfCenterLong,
3150 0 : dfFalseEasting, dfFalseNorthing );
3151 : }
3152 :
3153 2 : else if (EQUAL(osProjectionType, "MP")) /* Mollweide */
3154 : {
3155 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
3156 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3157 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3158 : oSRS.SetMollweide( dfCentralMeridian,
3159 0 : dfFalseEasting, dfFalseNorthing );
3160 : }
3161 :
3162 : /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */
3163 :
3164 2 : else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */
3165 : {
3166 : /* No parameter specified in the PDF, so let's take the ones of EPSG:27200 */
3167 0 : double dfCenterLat = -41;
3168 0 : double dfCenterLong = 173;
3169 0 : double dfFalseEasting = 2510000;
3170 0 : double dfFalseNorthing = 6023150;
3171 : oSRS.SetNZMG( dfCenterLat, dfCenterLong,
3172 0 : dfFalseEasting, dfFalseNorthing );
3173 : }
3174 :
3175 2 : else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */
3176 : {
3177 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3178 0 : double dfLat1 = Get(poProjDict, "LatitudeOne");
3179 0 : double dfLong1 = Get(poProjDict, "LongitudeOne");
3180 0 : double dfLat2 = Get(poProjDict, "LatitudeTwo");
3181 0 : double dfLong2 = Get(poProjDict, "LongitudeTwo");
3182 0 : double dfScale = Get(poProjDict, "ScaleFactor");
3183 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3184 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3185 : oSRS.SetHOM2PNO( dfCenterLat,
3186 : dfLat1, dfLong1,
3187 : dfLat2, dfLong2,
3188 : dfScale,
3189 : dfFalseEasting,
3190 0 : dfFalseNorthing );
3191 : }
3192 :
3193 2 : else if (EQUAL(osProjectionType, "OD")) /* Orthographic */
3194 : {
3195 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3196 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3197 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3198 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3199 : oSRS.SetOrthographic( dfCenterLat, dfCenterLong,
3200 0 : dfFalseEasting, dfFalseNorthing );
3201 : }
3202 :
3203 2 : else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */
3204 : {
3205 0 : double dfCenterLat = Get(poProjDict, "LatitudeTrueScale");
3206 0 : double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole");
3207 0 : double dfScale = 1.0;
3208 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3209 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3210 : oSRS.SetPS( dfCenterLat, dfCenterLong,
3211 : dfScale,
3212 0 : dfFalseEasting, dfFalseNorthing);
3213 : }
3214 :
3215 2 : else if (EQUAL(osProjectionType, "PH")) /* Polyconic */
3216 : {
3217 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3218 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3219 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3220 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3221 : oSRS.SetPolyconic( dfCenterLat, dfCenterLong,
3222 0 : dfFalseEasting, dfFalseNorthing );
3223 : }
3224 :
3225 2 : else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */
3226 : {
3227 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3228 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3229 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3230 : oSRS.SetSinusoidal( dfCenterLong,
3231 0 : dfFalseEasting, dfFalseNorthing );
3232 : }
3233 :
3234 2 : else if (EQUAL(osProjectionType, "SD")) /* Stereographic */
3235 : {
3236 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3237 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3238 0 : double dfScale = 1.0;
3239 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3240 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3241 : oSRS.SetStereographic( dfCenterLat, dfCenterLong,
3242 : dfScale,
3243 0 : dfFalseEasting, dfFalseNorthing);
3244 : }
3245 :
3246 2 : else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */
3247 : {
3248 2 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
3249 2 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3250 2 : double dfScale = Get(poProjDict, "ScaleFactor");
3251 2 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3252 2 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3253 4 : if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfFalseEasting == 500000 &&
3254 : (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0))
3255 : {
3256 2 : int nZone = (int) floor( (dfCenterLong + 180.0) / 6.0 ) + 1;
3257 2 : int bNorth = dfFalseNorthing == 0;
3258 2 : if (bIsWGS84)
3259 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
3260 2 : else if (bIsNAD83 && bNorth)
3261 0 : oSRS.importFromEPSG( 26900 + nZone );
3262 : else
3263 2 : oSRS.SetUTM( nZone, bNorth );
3264 : }
3265 : else
3266 : {
3267 : oSRS.SetTM( dfCenterLat, dfCenterLong,
3268 : dfScale,
3269 0 : dfFalseEasting, dfFalseNorthing );
3270 : }
3271 : }
3272 :
3273 : /* Unhandled TX : Transverse Cylindrical Equal Area */
3274 :
3275 0 : else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */
3276 : {
3277 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
3278 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
3279 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
3280 : oSRS.SetVDG( dfCenterLong,
3281 0 : dfFalseEasting, dfFalseNorthing );
3282 : }
3283 :
3284 : else
3285 : {
3286 : CPLError(CE_Failure, CPLE_AppDefined,
3287 : "Unhandled (yet) value for ProjectionType : %s",
3288 0 : osProjectionType.c_str());
3289 0 : return FALSE;
3290 : }
3291 :
3292 : /* -------------------------------------------------------------------- */
3293 : /* Extract Units attribute */
3294 : /* -------------------------------------------------------------------- */
3295 28 : CPLString osUnits;
3296 : GDALPDFObject* poUnits;
3297 46 : if ((poUnits = poProjDict->Get("Units")) != NULL &&
3298 18 : poUnits->GetType() == PDFObjectType_String)
3299 : {
3300 18 : osUnits = poUnits->GetString();
3301 18 : CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str());
3302 :
3303 18 : if (EQUAL(osUnits, "M"))
3304 18 : oSRS.SetLinearUnits( "Meter", 1.0 );
3305 0 : else if (EQUAL(osUnits, "FT"))
3306 0 : oSRS.SetLinearUnits( "foot", 0.3048 );
3307 : }
3308 :
3309 : /* -------------------------------------------------------------------- */
3310 : /* Export SpatialRef */
3311 : /* -------------------------------------------------------------------- */
3312 28 : CPLFree(pszWKT);
3313 28 : pszWKT = NULL;
3314 28 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
3315 : {
3316 0 : CPLFree(pszWKT);
3317 0 : pszWKT = NULL;
3318 : }
3319 :
3320 28 : return TRUE;
3321 : }
3322 :
3323 : /************************************************************************/
3324 : /* ParseVP() */
3325 : /************************************************************************/
3326 :
3327 242 : int PDFDataset::ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight)
3328 : {
3329 : int i;
3330 :
3331 242 : if (poVP->GetType() != PDFObjectType_Array)
3332 0 : return FALSE;
3333 :
3334 242 : GDALPDFArray* poVPArray = poVP->GetArray();
3335 :
3336 242 : int nLength = poVPArray->GetLength();
3337 242 : CPLDebug("PDF", "VP length = %d", nLength);
3338 242 : if (nLength < 1)
3339 0 : return FALSE;
3340 :
3341 : /* -------------------------------------------------------------------- */
3342 : /* Find the largest BBox */
3343 : /* -------------------------------------------------------------------- */
3344 242 : int iLargest = 0;
3345 242 : double dfLargestArea = 0;
3346 :
3347 498 : for(i=0;i<nLength;i++)
3348 : {
3349 256 : GDALPDFObject* poVPElt = poVPArray->Get(i);
3350 256 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
3351 : {
3352 0 : return FALSE;
3353 : }
3354 :
3355 256 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
3356 :
3357 : GDALPDFObject* poBBox;
3358 512 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
3359 256 : poBBox->GetType() != PDFObjectType_Array )
3360 : {
3361 : CPLError(CE_Failure, CPLE_AppDefined,
3362 0 : "Cannot find Bbox object");
3363 0 : return FALSE;
3364 : }
3365 :
3366 256 : int nBboxLength = poBBox->GetArray()->GetLength();
3367 256 : if (nBboxLength != 4)
3368 : {
3369 : CPLError(CE_Failure, CPLE_AppDefined,
3370 0 : "Invalid length for Bbox object");
3371 0 : return FALSE;
3372 : }
3373 :
3374 : double adfBBox[4];
3375 256 : adfBBox[0] = Get(poBBox, 0);
3376 256 : adfBBox[1] = Get(poBBox, 1);
3377 256 : adfBBox[2] = Get(poBBox, 2);
3378 256 : adfBBox[3] = Get(poBBox, 3);
3379 256 : double dfArea = fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]);
3380 256 : if (dfArea > dfLargestArea)
3381 : {
3382 242 : iLargest = i;
3383 242 : dfLargestArea = dfArea;
3384 : }
3385 : }
3386 :
3387 242 : if (nLength > 1)
3388 : {
3389 14 : CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest);
3390 : }
3391 :
3392 :
3393 242 : GDALPDFObject* poVPElt = poVPArray->Get(iLargest);
3394 242 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
3395 : {
3396 0 : return FALSE;
3397 : }
3398 :
3399 242 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
3400 :
3401 : GDALPDFObject* poBBox;
3402 484 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
3403 242 : poBBox->GetType() != PDFObjectType_Array )
3404 : {
3405 : CPLError(CE_Failure, CPLE_AppDefined,
3406 0 : "Cannot find Bbox object");
3407 0 : return FALSE;
3408 : }
3409 :
3410 242 : int nBboxLength = poBBox->GetArray()->GetLength();
3411 242 : if (nBboxLength != 4)
3412 : {
3413 : CPLError(CE_Failure, CPLE_AppDefined,
3414 0 : "Invalid length for Bbox object");
3415 0 : return FALSE;
3416 : }
3417 :
3418 242 : double dfULX = Get(poBBox, 0);
3419 242 : double dfULY = dfMediaBoxHeight - Get(poBBox, 1);
3420 242 : double dfLRX = Get(poBBox, 2);
3421 242 : double dfLRY = dfMediaBoxHeight - Get(poBBox, 3);
3422 :
3423 : /* -------------------------------------------------------------------- */
3424 : /* Extract Measure attribute */
3425 : /* -------------------------------------------------------------------- */
3426 : GDALPDFObject* poMeasure;
3427 484 : if( (poMeasure = poVPEltDict->Get("Measure")) == NULL ||
3428 242 : poMeasure->GetType() != PDFObjectType_Dictionary )
3429 : {
3430 : CPLError(CE_Failure, CPLE_AppDefined,
3431 0 : "Cannot find Measure object");
3432 0 : return FALSE;
3433 : }
3434 :
3435 242 : GDALPDFDictionary* poMeasureDict = poMeasure->GetDictionary();
3436 :
3437 : /* -------------------------------------------------------------------- */
3438 : /* Extract Subtype attribute */
3439 : /* -------------------------------------------------------------------- */
3440 : GDALPDFObject* poSubtype;
3441 484 : if( (poSubtype = poMeasureDict->Get("Subtype")) == NULL ||
3442 242 : poSubtype->GetType() != PDFObjectType_Name )
3443 : {
3444 : CPLError(CE_Failure, CPLE_AppDefined,
3445 0 : "Cannot find Subtype object");
3446 0 : return FALSE;
3447 : }
3448 :
3449 242 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
3450 :
3451 : /* -------------------------------------------------------------------- */
3452 : /* Extract Bounds attribute (optional) */
3453 : /* -------------------------------------------------------------------- */
3454 :
3455 : /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf */
3456 : /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */
3457 : /* LPTS, GPTS and Bounds. Use those ones */
3458 :
3459 : GDALPDFObject* poBounds;
3460 242 : if( (poBounds = poMeasureDict->Get("lgit:Bounds")) != NULL &&
3461 0 : poBounds->GetType() == PDFObjectType_Array )
3462 : {
3463 0 : CPLDebug("PDF", "Using lgit:Bounds");
3464 : }
3465 484 : else if( (poBounds = poMeasureDict->Get("Bounds")) == NULL ||
3466 242 : poBounds->GetType() != PDFObjectType_Array )
3467 : {
3468 0 : poBounds = NULL;
3469 : }
3470 :
3471 242 : if (poBounds != NULL)
3472 : {
3473 242 : int nBoundsLength = poBounds->GetArray()->GetLength();
3474 242 : if (nBoundsLength == 8)
3475 : {
3476 : double adfBounds[8];
3477 2178 : for(i=0;i<8;i++)
3478 : {
3479 1936 : adfBounds[i] = Get(poBounds, i);
3480 1936 : CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
3481 : }
3482 :
3483 : // TODO we should use it to restrict the neatline but
3484 : // I have yet to set a sample where bounds are not the four
3485 : // corners of the unit square.
3486 : }
3487 : }
3488 :
3489 : /* -------------------------------------------------------------------- */
3490 : /* Extract GPTS attribute */
3491 : /* -------------------------------------------------------------------- */
3492 : GDALPDFObject* poGPTS;
3493 242 : if( (poGPTS = poMeasureDict->Get("lgit:GPTS")) != NULL &&
3494 0 : poGPTS->GetType() == PDFObjectType_Array )
3495 : {
3496 0 : CPLDebug("PDF", "Using lgit:GPTS");
3497 : }
3498 484 : else if( (poGPTS = poMeasureDict->Get("GPTS")) == NULL ||
3499 242 : poGPTS->GetType() != PDFObjectType_Array )
3500 : {
3501 : CPLError(CE_Failure, CPLE_AppDefined,
3502 0 : "Cannot find GPTS object");
3503 0 : return FALSE;
3504 : }
3505 :
3506 242 : int nGPTSLength = poGPTS->GetArray()->GetLength();
3507 242 : if (nGPTSLength != 8)
3508 : {
3509 : CPLError(CE_Failure, CPLE_AppDefined,
3510 0 : "Invalid length for GPTS object");
3511 0 : return FALSE;
3512 : }
3513 :
3514 : double adfGPTS[8];
3515 2178 : for(i=0;i<8;i++)
3516 : {
3517 1936 : adfGPTS[i] = Get(poGPTS, i);
3518 1936 : CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
3519 : }
3520 :
3521 : /* -------------------------------------------------------------------- */
3522 : /* Extract LPTS attribute */
3523 : /* -------------------------------------------------------------------- */
3524 : GDALPDFObject* poLPTS;
3525 242 : if( (poLPTS = poMeasureDict->Get("lgit:LPTS")) != NULL &&
3526 0 : poLPTS->GetType() == PDFObjectType_Array )
3527 : {
3528 0 : CPLDebug("PDF", "Using lgit:LPTS");
3529 : }
3530 484 : else if( (poLPTS = poMeasureDict->Get("LPTS")) == NULL ||
3531 242 : poLPTS->GetType() != PDFObjectType_Array )
3532 : {
3533 : CPLError(CE_Failure, CPLE_AppDefined,
3534 0 : "Cannot find LPTS object");
3535 0 : return FALSE;
3536 : }
3537 :
3538 242 : int nLPTSLength = poLPTS->GetArray()->GetLength();
3539 242 : if (nLPTSLength != 8)
3540 : {
3541 : CPLError(CE_Failure, CPLE_AppDefined,
3542 0 : "Invalid length for LPTS object");
3543 0 : return FALSE;
3544 : }
3545 :
3546 : double adfLPTS[8];
3547 2178 : for(i=0;i<8;i++)
3548 : {
3549 1936 : adfLPTS[i] = Get(poLPTS, i);
3550 1936 : CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
3551 : }
3552 :
3553 : /* -------------------------------------------------------------------- */
3554 : /* Extract GCS attribute */
3555 : /* -------------------------------------------------------------------- */
3556 : GDALPDFObject* poGCS;
3557 484 : if( (poGCS = poMeasureDict->Get("GCS")) == NULL ||
3558 242 : poGCS->GetType() != PDFObjectType_Dictionary )
3559 : {
3560 : CPLError(CE_Failure, CPLE_AppDefined,
3561 0 : "Cannot find GCS object");
3562 0 : return FALSE;
3563 : }
3564 :
3565 242 : GDALPDFDictionary* poGCSDict = poGCS->GetDictionary();
3566 :
3567 : /* -------------------------------------------------------------------- */
3568 : /* Extract GCS.Type attribute */
3569 : /* -------------------------------------------------------------------- */
3570 : GDALPDFObject* poGCSType;
3571 484 : if( (poGCSType = poGCSDict->Get("Type")) == NULL ||
3572 242 : poGCSType->GetType() != PDFObjectType_Name )
3573 : {
3574 : CPLError(CE_Failure, CPLE_AppDefined,
3575 0 : "Cannot find GCS.Type object");
3576 0 : return FALSE;
3577 : }
3578 :
3579 242 : CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str());
3580 :
3581 : /* -------------------------------------------------------------------- */
3582 : /* Extract EPSG attribute */
3583 : /* -------------------------------------------------------------------- */
3584 : GDALPDFObject* poEPSG;
3585 242 : int nEPSGCode = 0;
3586 466 : if( (poEPSG = poGCSDict->Get("EPSG")) != NULL &&
3587 224 : poEPSG->GetType() == PDFObjectType_Int )
3588 : {
3589 224 : nEPSGCode = poEPSG->GetInt();
3590 224 : CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode);
3591 : }
3592 :
3593 : /* -------------------------------------------------------------------- */
3594 : /* Extract GCS.WKT attribute */
3595 : /* -------------------------------------------------------------------- */
3596 242 : GDALPDFObject* poGCSWKT = poGCSDict->Get("WKT");
3597 484 : if( poGCSWKT != NULL &&
3598 242 : poGCSWKT->GetType() != PDFObjectType_String )
3599 : {
3600 0 : poGCSWKT = NULL;
3601 : }
3602 :
3603 242 : if (poGCSWKT != NULL)
3604 242 : CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str());
3605 :
3606 242 : if (nEPSGCode <= 0 && poGCSWKT == NULL)
3607 : {
3608 : CPLError(CE_Failure, CPLE_AppDefined,
3609 0 : "Cannot find GCS.WKT or GCS.EPSG objects");
3610 0 : return FALSE;
3611 : }
3612 :
3613 242 : OGRSpatialReference oSRS;
3614 242 : int bSRSOK = FALSE;
3615 242 : if (nEPSGCode != 0 &&
3616 : oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
3617 : {
3618 224 : bSRSOK = TRUE;
3619 224 : CPLFree(pszWKT);
3620 224 : pszWKT = NULL;
3621 224 : oSRS.exportToWkt(&pszWKT);
3622 : }
3623 : else
3624 : {
3625 18 : if (poGCSWKT == NULL)
3626 : {
3627 : CPLError(CE_Failure, CPLE_AppDefined,
3628 0 : "Cannot resolve EPSG object, and GCS.WKT not found");
3629 0 : return FALSE;
3630 : }
3631 :
3632 18 : CPLFree(pszWKT);
3633 18 : pszWKT = CPLStrdup(poGCSWKT->GetString().c_str());
3634 : }
3635 :
3636 242 : if (!bSRSOK)
3637 : {
3638 18 : char* pszWktTemp = pszWKT;
3639 18 : if (oSRS.importFromWkt(&pszWktTemp) != OGRERR_NONE)
3640 : {
3641 0 : CPLFree(pszWKT);
3642 0 : pszWKT = NULL;
3643 0 : return FALSE;
3644 : }
3645 : }
3646 :
3647 : /* For http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */
3648 : /* or http://www.agmkt.state.ny.us/soilwater/aem/gis_mapping_tools/HUC12_Albany.pdf */
3649 242 : const char* pszDatum = oSRS.GetAttrValue("Datum");
3650 242 : if (pszDatum && strncmp(pszDatum, "D_", 2) == 0)
3651 : {
3652 18 : oSRS.morphFromESRI();
3653 :
3654 18 : CPLFree(pszWKT);
3655 18 : pszWKT = NULL;
3656 18 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
3657 : {
3658 0 : CPLFree(pszWKT);
3659 0 : pszWKT = NULL;
3660 : }
3661 : else
3662 : {
3663 18 : CPLDebug("PDF", "WKT after morphFromESRI() = %s", pszWKT);
3664 : }
3665 : }
3666 :
3667 : /* -------------------------------------------------------------------- */
3668 : /* Compute geotransform */
3669 : /* -------------------------------------------------------------------- */
3670 242 : OGRSpatialReference* poSRSGeog = oSRS.CloneGeogCS();
3671 :
3672 : /* Files found at http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ */
3673 : /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, long) as required by the */
3674 : /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is able to understand that, */
3675 : /* so let's also try to do it with a heuristics. */
3676 :
3677 242 : int bReproject = TRUE;
3678 1810 : if (oSRS.IsProjected() &&
3679 784 : (fabs(adfGPTS[0]) > 91 || fabs(adfGPTS[2]) > 91 || fabs(adfGPTS[4]) > 91 || fabs(adfGPTS[6]) > 91 ||
3680 784 : fabs(adfGPTS[1]) > 361 || fabs(adfGPTS[3]) > 361 || fabs(adfGPTS[5]) > 361 || fabs(adfGPTS[7]) > 361))
3681 : {
3682 0 : CPLDebug("PDF", "GPTS coordinates seems to be in (northing, easting), which is non-standard");
3683 0 : bReproject = FALSE;
3684 : }
3685 :
3686 242 : OGRCoordinateTransformation* poCT = NULL;
3687 242 : if (bReproject)
3688 : {
3689 242 : poCT = OGRCreateCoordinateTransformation( poSRSGeog, &oSRS);
3690 242 : if (poCT == NULL)
3691 : {
3692 0 : delete poSRSGeog;
3693 0 : CPLFree(pszWKT);
3694 0 : pszWKT = NULL;
3695 0 : return FALSE;
3696 : }
3697 : }
3698 :
3699 : GDAL_GCP asGCPS[4];
3700 :
3701 : /* Create NEATLINE */
3702 242 : poNeatLine = new OGRPolygon();
3703 484 : OGRLinearRing* poRing = new OGRLinearRing();
3704 242 : poNeatLine->addRingDirectly(poRing);
3705 :
3706 1210 : for(i=0;i<4;i++)
3707 : {
3708 : /* We probably assume LPTS is 0 or 1 */
3709 968 : asGCPS[i].dfGCPPixel = (dfULX * (1 - adfLPTS[2*i+0]) + dfLRX * adfLPTS[2*i+0]) / dfMediaBoxWidth * nRasterXSize;
3710 968 : asGCPS[i].dfGCPLine = (dfULY * (1 - adfLPTS[2*i+1]) + dfLRY * adfLPTS[2*i+1]) / dfMediaBoxHeight * nRasterYSize;
3711 :
3712 968 : double lat = adfGPTS[2*i], lon = adfGPTS[2*i+1];
3713 968 : double x = lon, y = lat;
3714 968 : if (bReproject)
3715 : {
3716 968 : if (!poCT->Transform(1, &x, &y, NULL))
3717 : {
3718 : CPLError(CE_Failure, CPLE_AppDefined,
3719 0 : "Cannot reproject (%f, %f)", lon, lat);
3720 0 : delete poSRSGeog;
3721 0 : delete poCT;
3722 0 : CPLFree(pszWKT);
3723 0 : pszWKT = NULL;
3724 0 : return FALSE;
3725 : }
3726 : }
3727 :
3728 968 : x = ROUND_TO_INT_IF_CLOSE(x);
3729 968 : y = ROUND_TO_INT_IF_CLOSE(y);
3730 :
3731 968 : asGCPS[i].dfGCPX = x;
3732 968 : asGCPS[i].dfGCPY = y;
3733 :
3734 968 : poRing->addPoint(x, y);
3735 : }
3736 :
3737 242 : delete poSRSGeog;
3738 242 : delete poCT;
3739 :
3740 242 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
3741 : adfGeoTransform, FALSE))
3742 : {
3743 0 : CPLDebug("PDF", "Could not compute GT with exact match. Try with approximate");
3744 0 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
3745 : adfGeoTransform, TRUE))
3746 : {
3747 : CPLError(CE_Failure, CPLE_AppDefined,
3748 0 : "Could not compute GT with approximate match.");
3749 0 : return FALSE;
3750 : }
3751 : }
3752 242 : bGeoTransformValid = TRUE;
3753 :
3754 : /* If the non scaling terms of the geotransform are significantly smaller than */
3755 : /* the pixel size, then nullify them as being just artifacts of reprojection and */
3756 : /* GDALGCPsToGeoTransform() numerical imprecisions */
3757 242 : double dfPixelSize = MIN(fabs(adfGeoTransform[1]), fabs(adfGeoTransform[5]));
3758 242 : double dfRotationShearTerm = MAX(fabs(adfGeoTransform[2]), fabs(adfGeoTransform[4]));
3759 242 : if (dfRotationShearTerm < 1e-5 * dfPixelSize)
3760 : {
3761 242 : double dfLRX = adfGeoTransform[0] + nRasterXSize * adfGeoTransform[1] + nRasterYSize * adfGeoTransform[2];
3762 242 : double dfLRY = adfGeoTransform[3] + nRasterXSize * adfGeoTransform[4] + nRasterYSize * adfGeoTransform[5];
3763 242 : adfGeoTransform[1] = (dfLRX - adfGeoTransform[0]) / nRasterXSize;
3764 242 : adfGeoTransform[5] = (dfLRY - adfGeoTransform[3]) / nRasterYSize;
3765 242 : adfGeoTransform[2] = adfGeoTransform[4] = 0;
3766 : }
3767 :
3768 : /* -------------------------------------------------------------------- */
3769 : /* Extract PointData attribute */
3770 : /* -------------------------------------------------------------------- */
3771 : GDALPDFObject* poPointData;
3772 242 : if( (poPointData = poVPEltDict->Get("PtData")) != NULL &&
3773 0 : poPointData->GetType() == PDFObjectType_Dictionary )
3774 : {
3775 0 : CPLDebug("PDF", "Found PointData");
3776 : }
3777 :
3778 242 : return TRUE;
3779 : }
3780 :
3781 : /************************************************************************/
3782 : /* GetProjectionRef() */
3783 : /************************************************************************/
3784 :
3785 74 : const char* PDFDataset::GetProjectionRef()
3786 : {
3787 74 : if (pszWKT && bGeoTransformValid)
3788 68 : return pszWKT;
3789 6 : return "";
3790 : }
3791 :
3792 : /************************************************************************/
3793 : /* GetGeoTransform() */
3794 : /************************************************************************/
3795 :
3796 72 : CPLErr PDFDataset::GetGeoTransform( double * padfTransform )
3797 :
3798 : {
3799 72 : memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
3800 :
3801 72 : return( (bGeoTransformValid) ? CE_None : CE_Failure );
3802 : }
3803 :
3804 : /************************************************************************/
3805 : /* SetProjection() */
3806 : /************************************************************************/
3807 :
3808 12 : CPLErr PDFDataset::SetProjection(const char* pszWKTIn)
3809 : {
3810 12 : CPLFree(pszWKT);
3811 12 : pszWKT = pszWKTIn ? CPLStrdup(pszWKTIn) : CPLStrdup("");
3812 12 : bProjDirty = TRUE;
3813 12 : return CE_None;
3814 : }
3815 :
3816 : /************************************************************************/
3817 : /* SetGeoTransform() */
3818 : /************************************************************************/
3819 :
3820 8 : CPLErr PDFDataset::SetGeoTransform(double* padfGeoTransform)
3821 : {
3822 8 : memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double));
3823 8 : bGeoTransformValid = TRUE;
3824 8 : bProjDirty = TRUE;
3825 :
3826 : /* Reset NEATLINE if not explicitely set by the user */
3827 8 : if (!bNeatLineDirty)
3828 8 : SetMetadataItem("NEATLINE", NULL);
3829 8 : return CE_None;
3830 : }
3831 :
3832 : /************************************************************************/
3833 : /* GetMetadata() */
3834 : /************************************************************************/
3835 :
3836 48 : char **PDFDataset::GetMetadata( const char * pszDomain )
3837 : {
3838 48 : return oMDMD.GetMetadata(pszDomain);
3839 : }
3840 :
3841 : /************************************************************************/
3842 : /* SetMetadata() */
3843 : /************************************************************************/
3844 :
3845 42 : CPLErr PDFDataset::SetMetadata( char ** papszMetadata,
3846 : const char * pszDomain )
3847 : {
3848 42 : if (pszDomain == NULL || EQUAL(pszDomain, ""))
3849 : {
3850 0 : if (CSLFindString(papszMetadata, "NEATLINE") != -1)
3851 : {
3852 0 : bProjDirty = TRUE;
3853 0 : bNeatLineDirty = TRUE;
3854 : }
3855 0 : bInfoDirty = TRUE;
3856 : }
3857 42 : else if (EQUAL(pszDomain, "xml:XMP"))
3858 40 : bXMPDirty = TRUE;
3859 42 : return oMDMD.SetMetadata(papszMetadata, pszDomain);
3860 : }
3861 :
3862 : /************************************************************************/
3863 : /* GetMetadataItem() */
3864 : /************************************************************************/
3865 :
3866 3560 : const char *PDFDataset::GetMetadataItem( const char * pszName,
3867 : const char * pszDomain )
3868 : {
3869 3560 : if ( (pszDomain == NULL || EQUAL(pszDomain, "")) && EQUAL(pszName, "PDF_PAGE_OBJECT") )
3870 : {
3871 4 : return CPLSPrintf("%p", poPageObj);
3872 : }
3873 3556 : if ( (pszDomain == NULL || EQUAL(pszDomain, "")) && EQUAL(pszName, "PDF_CATALOG_OBJECT") )
3874 : {
3875 4 : return CPLSPrintf("%p", GetCatalog());
3876 : }
3877 :
3878 3552 : return oMDMD.GetMetadataItem(pszName, pszDomain);
3879 : }
3880 :
3881 : /************************************************************************/
3882 : /* SetMetadataItem() */
3883 : /************************************************************************/
3884 :
3885 712 : CPLErr PDFDataset::SetMetadataItem( const char * pszName,
3886 : const char * pszValue,
3887 : const char * pszDomain )
3888 : {
3889 1424 : if (pszDomain == NULL || EQUAL(pszDomain, ""))
3890 : {
3891 712 : if (EQUAL(pszName, "NEATLINE"))
3892 : {
3893 302 : bProjDirty = TRUE;
3894 302 : bNeatLineDirty = TRUE;
3895 : }
3896 : else
3897 : {
3898 410 : if (pszValue == NULL)
3899 4 : pszValue = "";
3900 410 : bInfoDirty = TRUE;
3901 : }
3902 : }
3903 0 : else if (EQUAL(pszDomain, "xml:XMP"))
3904 0 : bXMPDirty = TRUE;
3905 712 : return oMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
3906 : }
3907 :
3908 : /************************************************************************/
3909 : /* GetGCPCount() */
3910 : /************************************************************************/
3911 :
3912 36 : int PDFDataset::GetGCPCount()
3913 : {
3914 36 : return nGCPCount;
3915 : }
3916 :
3917 : /************************************************************************/
3918 : /* GetGCPProjection() */
3919 : /************************************************************************/
3920 :
3921 10 : const char * PDFDataset::GetGCPProjection()
3922 : {
3923 10 : if (pszWKT != NULL && nGCPCount != 0)
3924 6 : return pszWKT;
3925 4 : return "";
3926 : }
3927 :
3928 : /************************************************************************/
3929 : /* GetGCPs() */
3930 : /************************************************************************/
3931 :
3932 10 : const GDAL_GCP * PDFDataset::GetGCPs()
3933 : {
3934 10 : return pasGCPList;
3935 : }
3936 :
3937 : /************************************************************************/
3938 : /* SetGCPs() */
3939 : /************************************************************************/
3940 :
3941 4 : CPLErr PDFDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
3942 : const char *pszGCPProjectionIn )
3943 : {
3944 : const char* pszGEO_ENCODING =
3945 4 : CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
3946 4 : if( nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000"))
3947 : {
3948 : CPLError(CE_Failure, CPLE_NotSupported,
3949 : "PDF driver only supports writing 4 GCPs when "
3950 0 : "GDAL_PDF_GEO_ENCODING=ISO32000.");
3951 0 : return CE_Failure;
3952 : }
3953 :
3954 : /* Free previous GCPs */
3955 4 : GDALDeinitGCPs( nGCPCount, pasGCPList );
3956 4 : CPLFree( pasGCPList );
3957 :
3958 : /* Duplicate in GCPs */
3959 4 : nGCPCount = nGCPCountIn;
3960 4 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
3961 :
3962 4 : CPLFree(pszWKT);
3963 4 : pszWKT = CPLStrdup(pszGCPProjectionIn);
3964 :
3965 4 : bProjDirty = TRUE;
3966 :
3967 : /* Reset NEATLINE if not explicitely set by the user */
3968 4 : if (!bNeatLineDirty)
3969 4 : SetMetadataItem("NEATLINE", NULL);
3970 :
3971 4 : return CE_None;
3972 : }
3973 :
3974 : #endif // #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
3975 :
3976 :
3977 : /************************************************************************/
3978 : /* GDALPDFOpen() */
3979 : /************************************************************************/
3980 :
3981 4 : GDALDataset* GDALPDFOpen(const char* pszFilename, GDALAccess eAccess)
3982 : {
3983 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
3984 4 : GDALOpenInfo oOpenInfo(pszFilename, eAccess);
3985 4 : return PDFDataset::Open(&oOpenInfo);
3986 : #else
3987 : return NULL;
3988 : #endif
3989 : }
3990 :
3991 : /************************************************************************/
3992 : /* GDALRegister_PDF() */
3993 : /************************************************************************/
3994 :
3995 1135 : void GDALRegister_PDF()
3996 :
3997 : {
3998 : GDALDriver *poDriver;
3999 :
4000 1135 : if (! GDAL_CHECK_VERSION("PDF driver"))
4001 0 : return;
4002 :
4003 1135 : if( GDALGetDriverByName( "PDF" ) == NULL )
4004 : {
4005 1093 : poDriver = new GDALDriver();
4006 :
4007 1093 : poDriver->SetDescription( "PDF" );
4008 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
4009 1093 : "Geospatial PDF" );
4010 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
4011 1093 : "frmt_pdf.html" );
4012 1093 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pdf" );
4013 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
4014 1093 : "Byte" );
4015 : #ifdef HAVE_POPPLER
4016 1093 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
4017 1093 : poDriver->SetMetadataItem( "HAVE_POPPLER", "YES" );
4018 : #endif
4019 : #ifdef HAVE_PODOFO
4020 1093 : poDriver->SetMetadataItem( "HAVE_PODOFO", "YES" );
4021 : #endif
4022 :
4023 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
4024 : "<CreationOptionList>\n"
4025 : " <Option name='COMPRESS' type='string-select' description='Compression method for raster data' default='DEFLATE'>\n"
4026 : " <Value>NONE</Value>\n"
4027 : " <Value>DEFLATE</Value>\n"
4028 : " <Value>JPEG</Value>\n"
4029 : " <Value>JPEG2000</Value>\n"
4030 : " </Option>\n"
4031 : " <Option name='STREAM_COMPRESS' type='string-select' description='Compression method for stream objects' default='DEFLATE'>\n"
4032 : " <Value>NONE</Value>\n"
4033 : " <Value>DEFLATE</Value>\n"
4034 : " </Option>\n"
4035 : " <Option name='GEO_ENCODING' type='string-select' description='Format of geo-encoding' default='ISO32000'>\n"
4036 : " <Value>NONE</Value>\n"
4037 : " <Value>ISO32000</Value>\n"
4038 : " <Value>OGC_BP</Value>\n"
4039 : " <Value>BOTH</Value>\n"
4040 : " </Option>\n"
4041 : " <Option name='NEATLINE' type='string' description='Neatline'/>\n"
4042 : " <Option name='DPI' type='float' description='DPI' default='72'/>\n"
4043 : " <Option name='PREDICTOR' type='int' description='Predictor Type (for DEFLATE compression)'/>\n"
4044 : " <Option name='JPEG_QUALITY' type='int' description='JPEG quality 1-100' default='75'/>\n"
4045 : " <Option name='JPEG2000_DRIVER' type='string'/>\n"
4046 : " <Option name='TILED' type='boolean' description='Switch to tiled format' default='NO'/>\n"
4047 : " <Option name='BLOCKXSIZE' type='int' description='Block Width'/>\n"
4048 : " <Option name='BLOCKYSIZE' type='int' description='Block Height'/>\n"
4049 : " <Option name='LAYER_NAME' type='string' description='Layer name for raster content'/>\n"
4050 : " <Option name='EXTRA_STREAM' type='string' description='Extra data to insert into the page content stream'/>\n"
4051 : " <Option name='EXTRA_IMAGES' type='string' description='List of image_file_name,x,y,scale (possibly repeated)'/>\n"
4052 : " <Option name='EXTRA_LAYER_NAME' type='string' description='Layer name for extra content'/>\n"
4053 : " <Option name='MARGIN' type='int' description='Margin around image in user units'/>\n"
4054 : " <Option name='LEFT_MARGIN' type='int' description='Left margin in user units'/>\n"
4055 : " <Option name='RIGHT_MARGIN' type='int' description='Right margin in user units'/>\n"
4056 : " <Option name='TOP_MARGIN' type='int' description='Top margin in user units'/>\n"
4057 : " <Option name='BOTTOM_MARGIN' type='int' description='Bottom margin in user units'/>\n"
4058 : " <Option name='OGR_DATASOURCE' type='string' description='Name of OGR datasource to display on top of the raster layer'/>\n"
4059 : " <Option name='OGR_DISPLAY_FIELD' type='string' description='Name of field to use as the display field in the feature tree'/>\n"
4060 : " <Option name='OGR_DISPLAY_LAYER_NAMES' type='string' description='Comma separated list of OGR layer names to display in the feature tree'/>\n"
4061 : " <Option name='OGR_WRITE_ATTRIBUTES' type='boolean' description='Whether to write attributes of OGR features' default='YES'/>\n"
4062 : " <Option name='XMP' type='string' description='xml:XMP metadata'/>\n"
4063 : " <Option name='WRITE_INFO' type='boolean' description='to control whether a Info block must be written' default='YES'/>\n"
4064 : " <Option name='AUTHOR' type='string'/>\n"
4065 : " <Option name='CREATOR' type='string'/>\n"
4066 : " <Option name='CREATION_DATE' type='string'/>\n"
4067 : " <Option name='KEYWORDS' type='string'/>\n"
4068 : " <Option name='PRODUCER' type='string'/>\n"
4069 : " <Option name='SUBJECT' type='string'/>\n"
4070 : " <Option name='TITLE' type='string'/>\n"
4071 1093 : "</CreationOptionList>\n" );
4072 :
4073 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
4074 1093 : poDriver->pfnOpen = PDFDataset::Open;
4075 1093 : poDriver->pfnIdentify = PDFDataset::Identify;
4076 : #endif
4077 :
4078 1093 : poDriver->pfnCreateCopy = GDALPDFCreateCopy;
4079 :
4080 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
4081 : }
4082 : }
4083 :
|