1 : /******************************************************************************
2 : * $Id: pdfdataset.cpp 25598 2013-02-05 22:24:35Z 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 : #include "cpl_spawn.h"
41 :
42 : #ifdef HAVE_POPPLER
43 : #include "cpl_multiproc.h"
44 : #include "pdfio.h"
45 : #include <goo/GooList.h>
46 : #endif
47 :
48 : #include "pdfobject.h"
49 : #include "pdfcreatecopy.h"
50 :
51 : #include <set>
52 : #include <map>
53 :
54 : #define DEFAULT_DPI 150.0
55 :
56 : /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */
57 :
58 : CPL_CVSID("$Id: pdfdataset.cpp 25598 2013-02-05 22:24:35Z rouault $");
59 :
60 : CPL_C_START
61 : void GDALRegister_PDF(void);
62 : CPL_C_END
63 :
64 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
65 :
66 : static double Get(GDALPDFObject* poObj, int nIndice = -1);
67 :
68 : #ifdef HAVE_POPPLER
69 :
70 : static void* hGlobalParamsMutex = NULL;
71 :
72 : /************************************************************************/
73 : /* ObjectAutoFree */
74 : /************************************************************************/
75 :
76 : class ObjectAutoFree : public Object
77 : {
78 : public:
79 190 : ObjectAutoFree() {}
80 190 : ~ObjectAutoFree() { free(); }
81 : };
82 :
83 :
84 : /************************************************************************/
85 : /* GDALPDFOutputDev */
86 : /************************************************************************/
87 :
88 : class GDALPDFOutputDev : public SplashOutputDev
89 48 : {
90 : private:
91 : int bEnableVector;
92 : int bEnableText;
93 : int bEnableBitmap;
94 :
95 0 : void skipBytes(Stream *str,
96 : int width, int height,
97 : int nComps, int nBits)
98 : {
99 0 : int nVals = width * nComps;
100 0 : int nLineSize = (nVals * nBits + 7) >> 3;
101 0 : int nBytes = nLineSize * height;
102 0 : for (int i = 0; i < nBytes; i++)
103 : {
104 0 : if( str->getChar() == EOF)
105 0 : break;
106 : }
107 0 : }
108 :
109 : public:
110 48 : GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
111 : GBool reverseVideoA, SplashColorPtr paperColorA,
112 : GBool bitmapTopDownA = gTrue,
113 : GBool allowAntialiasA = gTrue) :
114 : SplashOutputDev(colorModeA, bitmapRowPadA,
115 : reverseVideoA, paperColorA,
116 : bitmapTopDownA, allowAntialiasA),
117 : bEnableVector(TRUE),
118 : bEnableText(TRUE),
119 48 : bEnableBitmap(TRUE) {}
120 :
121 5 : void SetEnableVector(int bFlag) { bEnableVector = bFlag; }
122 4 : void SetEnableText(int bFlag) { bEnableText = bFlag; }
123 5 : void SetEnableBitmap(int bFlag) { bEnableBitmap = bFlag; }
124 :
125 48 : virtual void startPage(int pageNum, GfxState *state
126 : #ifdef POPPLER_0_23_OR_LATER
127 : ,XRef* xref
128 : #endif
129 : )
130 : {
131 : SplashOutputDev::startPage(pageNum, state
132 : #ifdef POPPLER_0_23_OR_LATER
133 : ,xref
134 : #endif
135 48 : );
136 48 : SplashBitmap* poBitmap = getBitmap();
137 48 : memset(poBitmap->getDataPtr(), 255, poBitmap->getRowSize() * poBitmap->getHeight());
138 48 : }
139 :
140 1631 : virtual void stroke(GfxState * state)
141 : {
142 1631 : if (bEnableVector)
143 1628 : SplashOutputDev::stroke(state);
144 1631 : }
145 :
146 0 : virtual void fill(GfxState * state)
147 : {
148 0 : if (bEnableVector)
149 0 : SplashOutputDev::fill(state);
150 0 : }
151 :
152 8 : virtual void eoFill(GfxState * state)
153 : {
154 8 : if (bEnableVector)
155 6 : SplashOutputDev::eoFill(state);
156 8 : }
157 :
158 4247 : virtual void drawChar(GfxState *state, double x, double y,
159 : double dx, double dy,
160 : double originX, double originY,
161 : CharCode code, int nBytes, Unicode *u, int uLen)
162 : {
163 4247 : if (bEnableText)
164 : SplashOutputDev::drawChar(state, x, y, dx, dy,
165 : originX, originY,
166 4223 : code, nBytes, u, uLen);
167 4247 : }
168 :
169 677 : virtual void beginTextObject(GfxState *state)
170 : {
171 677 : if (bEnableText)
172 675 : SplashOutputDev::beginTextObject(state);
173 677 : }
174 :
175 : #ifndef POPPLER_0_23_OR_LATER
176 : virtual GBool deviceHasTextClip(GfxState *state)
177 : {
178 : if (bEnableText)
179 : return SplashOutputDev::deviceHasTextClip(state);
180 : return gFalse;
181 : }
182 : #endif
183 :
184 677 : virtual void endTextObject(GfxState *state)
185 : {
186 677 : if (bEnableText)
187 675 : SplashOutputDev::endTextObject(state);
188 677 : }
189 :
190 0 : virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
191 : int width, int height, GBool invert,
192 : GBool interpolate, GBool inlineImg)
193 : {
194 0 : if (bEnableBitmap)
195 : SplashOutputDev::drawImageMask(state, ref, str,
196 : width, height, invert,
197 0 : interpolate, inlineImg);
198 : else
199 : {
200 0 : str->reset();
201 0 : if (inlineImg)
202 : {
203 0 : skipBytes(str, width, height, 1, 1);
204 : }
205 0 : str->close();
206 : }
207 0 : }
208 :
209 : #ifdef POPPLER_0_20_OR_LATER
210 0 : virtual void setSoftMaskFromImageMask(GfxState *state,
211 : Object *ref, Stream *str,
212 : int width, int height, GBool invert,
213 : GBool inlineImg, double *baseMatrix)
214 : {
215 0 : if (bEnableBitmap)
216 : SplashOutputDev::setSoftMaskFromImageMask(state, ref, str,
217 : width, height, invert,
218 0 : inlineImg, baseMatrix);
219 : else
220 0 : str->close();
221 0 : }
222 :
223 0 : virtual void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix)
224 : {
225 0 : if (bEnableBitmap)
226 0 : SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix);
227 0 : }
228 : #endif
229 :
230 42 : virtual void drawImage(GfxState *state, Object *ref, Stream *str,
231 : int width, int height, GfxImageColorMap *colorMap,
232 : GBool interpolate, int *maskColors, GBool inlineImg)
233 : {
234 42 : if (bEnableBitmap)
235 : SplashOutputDev::drawImage(state, ref, str,
236 : width, height, colorMap,
237 41 : interpolate, maskColors, inlineImg);
238 : else
239 : {
240 1 : str->reset();
241 1 : if (inlineImg)
242 : {
243 : skipBytes(str, width, height,
244 : colorMap->getNumPixelComps(),
245 0 : colorMap->getBits());
246 : }
247 1 : str->close();
248 : }
249 42 : }
250 :
251 0 : virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
252 : int width, int height,
253 : GfxImageColorMap *colorMap,
254 : GBool interpolate,
255 : Stream *maskStr, int maskWidth, int maskHeight,
256 : GBool maskInvert, GBool maskInterpolate)
257 : {
258 0 : if (bEnableBitmap)
259 : SplashOutputDev::drawMaskedImage(state, ref, str,
260 : width, height, colorMap,
261 : interpolate,
262 : maskStr, maskWidth, maskHeight,
263 0 : maskInvert, maskInterpolate);
264 : else
265 0 : str->close();
266 0 : }
267 :
268 3 : virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
269 : int width, int height,
270 : GfxImageColorMap *colorMap,
271 : GBool interpolate,
272 : Stream *maskStr,
273 : int maskWidth, int maskHeight,
274 : GfxImageColorMap *maskColorMap,
275 : GBool maskInterpolate)
276 : {
277 3 : if (bEnableBitmap)
278 : {
279 3 : if( maskColorMap->getBits() <= 0 ) /* workaround poppler bug (robustness) */
280 : {
281 0 : str->close();
282 0 : return;
283 : }
284 : SplashOutputDev::drawSoftMaskedImage(state, ref, str,
285 : width, height, colorMap,
286 : interpolate,
287 : maskStr, maskWidth, maskHeight,
288 3 : maskColorMap, maskInterpolate);
289 : }
290 : else
291 0 : str->close();
292 : }
293 : };
294 :
295 : #endif
296 :
297 : /************************************************************************/
298 : /* Dump routines */
299 : /************************************************************************/
300 :
301 : class GDALPDFDumper
302 0 : {
303 : private:
304 : FILE* f;
305 : int nDepthLimit;
306 : std::set< int > aoSetObjectExplored;
307 : int bDumpParent;
308 :
309 : void DumpSimplified(GDALPDFObject* poObj);
310 :
311 : public:
312 0 : GDALPDFDumper(FILE* fIn, int nDepthLimitIn = -1) : f(fIn), nDepthLimit(nDepthLimitIn)
313 : {
314 0 : bDumpParent = CSLTestBoolean(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE"));
315 0 : }
316 :
317 : void Dump(GDALPDFObject* poObj, int nDepth = 0);
318 : void Dump(GDALPDFDictionary* poDict, int nDepth = 0);
319 : void Dump(GDALPDFArray* poArray, int nDepth = 0);
320 : };
321 :
322 0 : void GDALPDFDumper::Dump(GDALPDFArray* poArray, int nDepth)
323 : {
324 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
325 0 : return;
326 :
327 0 : int nLength = poArray->GetLength();
328 : int i;
329 0 : CPLString osIndent;
330 0 : for(i=0;i<nDepth;i++)
331 0 : osIndent += " ";
332 0 : for(i=0;i<nLength;i++)
333 : {
334 0 : fprintf(f, "%sItem[%d]:", osIndent.c_str(), i);
335 0 : GDALPDFObject* poObj = NULL;
336 0 : if ((poObj = poArray->Get(i)) != NULL)
337 : {
338 0 : if (poObj->GetType() == PDFObjectType_String ||
339 0 : poObj->GetType() == PDFObjectType_Null ||
340 0 : poObj->GetType() == PDFObjectType_Bool ||
341 0 : poObj->GetType() == PDFObjectType_Int ||
342 0 : poObj->GetType() == PDFObjectType_Real ||
343 0 : poObj->GetType() == PDFObjectType_Name)
344 : {
345 0 : fprintf(f, " ");
346 0 : DumpSimplified(poObj);
347 0 : fprintf(f, "\n");
348 : }
349 : else
350 : {
351 0 : fprintf(f, "\n");
352 0 : Dump( poObj, nDepth+1);
353 : }
354 : }
355 0 : }
356 : }
357 :
358 0 : void GDALPDFDumper::DumpSimplified(GDALPDFObject* poObj)
359 : {
360 0 : switch(poObj->GetType())
361 : {
362 : case PDFObjectType_String:
363 0 : fprintf(f, "%s (string)", poObj->GetString().c_str());
364 0 : break;
365 :
366 : case PDFObjectType_Null:
367 0 : fprintf(f, "null");
368 0 : break;
369 :
370 : case PDFObjectType_Bool:
371 0 : fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false");
372 0 : break;
373 :
374 : case PDFObjectType_Int:
375 0 : fprintf(f, "%d (int)", poObj->GetInt());
376 0 : break;
377 :
378 : case PDFObjectType_Real:
379 0 : fprintf(f, "%f (real)", poObj->GetReal());
380 0 : break;
381 :
382 : case PDFObjectType_Name:
383 0 : fprintf(f, "%s (name)", poObj->GetName().c_str());
384 0 : break;
385 :
386 : default:
387 0 : fprintf(f, "unknown !");
388 : break;
389 : }
390 0 : }
391 :
392 0 : void GDALPDFDumper::Dump(GDALPDFObject* poObj, int nDepth)
393 : {
394 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
395 0 : return;
396 :
397 : int i;
398 0 : CPLString osIndent;
399 0 : for(i=0;i<nDepth;i++)
400 0 : osIndent += " ";
401 : fprintf(f, "%sType = %s",
402 0 : osIndent.c_str(), poObj->GetTypeName());
403 0 : int nRefNum = poObj->GetRefNum();
404 0 : if (nRefNum != 0)
405 : fprintf(f, ", Num = %d, Gen = %d",
406 0 : nRefNum, poObj->GetRefGen());
407 0 : fprintf(f, "\n");
408 :
409 0 : if (nRefNum != 0)
410 : {
411 0 : if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end())
412 : return;
413 0 : aoSetObjectExplored.insert(nRefNum);
414 : }
415 :
416 0 : switch(poObj->GetType())
417 : {
418 : case PDFObjectType_Array:
419 0 : Dump(poObj->GetArray(), nDepth+1);
420 0 : break;
421 :
422 : case PDFObjectType_Dictionary:
423 0 : Dump(poObj->GetDictionary(), nDepth+1);
424 0 : break;
425 :
426 : case PDFObjectType_String:
427 : case PDFObjectType_Null:
428 : case PDFObjectType_Bool:
429 : case PDFObjectType_Int:
430 : case PDFObjectType_Real:
431 : case PDFObjectType_Name:
432 0 : fprintf(f, "%s", osIndent.c_str());
433 0 : DumpSimplified(poObj);
434 0 : fprintf(f, "\n");
435 0 : break;
436 :
437 : default:
438 0 : fprintf(f, "%s", osIndent.c_str());
439 0 : fprintf(f, "unknown !\n");
440 : break;
441 : }
442 :
443 0 : GDALPDFStream* poStream = poObj->GetStream();
444 0 : if (poStream != NULL)
445 : {
446 0 : fprintf(f, "%sHas stream (%d bytes)\n", osIndent.c_str(), poStream->GetLength());
447 0 : }
448 : }
449 :
450 0 : void GDALPDFDumper::Dump(GDALPDFDictionary* poDict, int nDepth)
451 : {
452 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
453 0 : return;
454 :
455 0 : std::map<CPLString, GDALPDFObject*>& oMap = poDict->GetValues();
456 0 : std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
457 0 : std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
458 : int i;
459 0 : CPLString osIndent;
460 0 : for(i=0;i<nDepth;i++)
461 0 : osIndent += " ";
462 0 : for(i=0;oIter != oEnd;++oIter, i++)
463 : {
464 0 : const char* pszKey = oIter->first.c_str();
465 0 : fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, pszKey);
466 0 : GDALPDFObject* poObj = oIter->second;
467 0 : if (strcmp(pszKey, "Parent") == 0 && !bDumpParent)
468 : {
469 0 : if (poObj->GetRefNum())
470 : fprintf(f, ", Num = %d, Gen = %d",
471 0 : poObj->GetRefNum(), poObj->GetRefGen());
472 0 : fprintf(f, "\n");
473 0 : continue;
474 : }
475 0 : if (poObj != NULL)
476 : {
477 0 : if (poObj->GetType() == PDFObjectType_String ||
478 0 : poObj->GetType() == PDFObjectType_Null ||
479 0 : poObj->GetType() == PDFObjectType_Bool ||
480 0 : poObj->GetType() == PDFObjectType_Int ||
481 0 : poObj->GetType() == PDFObjectType_Real ||
482 0 : poObj->GetType() == PDFObjectType_Name)
483 : {
484 0 : fprintf(f, " = ");
485 0 : DumpSimplified(poObj);
486 0 : fprintf(f, "\n");
487 : }
488 : else
489 : {
490 0 : fprintf(f, "\n");
491 0 : Dump(poObj, nDepth+1);
492 : }
493 : }
494 0 : }
495 : }
496 :
497 : /************************************************************************/
498 : /* GDALPDFTileDesc */
499 : /************************************************************************/
500 :
501 : typedef struct
502 : {
503 : GDALPDFObject* poImage;
504 : double adfCM[6];
505 : double dfWidth;
506 : double dfHeight;
507 : int nBands;
508 0 : } GDALPDFTileDesc;
509 :
510 : /************************************************************************/
511 : /* ==================================================================== */
512 : /* PDFDataset */
513 : /* ==================================================================== */
514 : /************************************************************************/
515 :
516 : class PDFRasterBand;
517 : class PDFImageRasterBand;
518 :
519 : class PDFDataset : public GDALPamDataset
520 : {
521 : friend class PDFRasterBand;
522 : friend class PDFImageRasterBand;
523 :
524 : CPLString osFilename;
525 : CPLString osUserPwd;
526 : char *pszWKT;
527 : double dfDPI;
528 : int bHasCTM;
529 : double adfCTM[6];
530 : double adfGeoTransform[6];
531 : int bGeoTransformValid;
532 : int nGCPCount;
533 : GDAL_GCP *pasGCPList;
534 : int bProjDirty;
535 : int bNeatLineDirty;
536 :
537 : GDALMultiDomainMetadata oMDMD;
538 : int bInfoDirty;
539 : int bXMPDirty;
540 :
541 : int bUsePoppler;
542 : #ifdef HAVE_POPPLER
543 : PDFDoc* poDocPoppler;
544 : #endif
545 : #ifdef HAVE_PODOFO
546 : PoDoFo::PdfMemDocument* poDocPodofo;
547 : int bPdfToPpmFailed;
548 : #endif
549 : GDALPDFObject* poPageObj;
550 :
551 : int iPage;
552 :
553 : GDALPDFObject *poImageObj;
554 :
555 : double dfMaxArea;
556 : int ParseLGIDictObject(GDALPDFObject* poLGIDict);
557 : int ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict, int* pbIsBestCandidate = NULL);
558 : int ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict);
559 : int ParseProjDict(GDALPDFDictionary* poProjDict);
560 : int ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight);
561 : int ParseMeasure(GDALPDFObject* poMeasure,
562 : double dfMediaBoxWidth, double dfMediaBoxHeight,
563 : double dfULX, double dfULY, double dfLRX, double dfLRY);
564 :
565 : int bTried;
566 : GByte *pabyCachedData;
567 : int nLastBlockXOff, nLastBlockYOff;
568 :
569 : OGRPolygon* poNeatLine;
570 :
571 : std::vector<GDALPDFTileDesc> asTiles; /* in the order of the PDF file */
572 : std::vector<int> aiTiles; /* in the order of blocks */
573 : int nBlockXSize;
574 : int nBlockYSize;
575 : int CheckTiledRaster();
576 :
577 : void GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands);
578 : void FindXMP(GDALPDFObject* poObj);
579 : void ParseInfo(GDALPDFObject* poObj);
580 :
581 : #ifdef HAVE_POPPLER
582 : ObjectAutoFree* poCatalogObjectPoppler;
583 : #endif
584 : GDALPDFObject* poCatalogObject;
585 : GDALPDFObject* GetCatalog();
586 :
587 : #ifdef HAVE_POPPLER
588 : void AddLayer(const char* pszLayerName, OptionalContentGroup* ocg);
589 : void ExploreLayers(GDALPDFArray* poArray, int nRecLevel, CPLString osTopLayer = "");
590 : void FindLayers();
591 : void TurnLayersOnOff();
592 : CPLStringList osLayerList;
593 : std::map<CPLString, OptionalContentGroup*> oLayerOCGMap;
594 : #endif
595 :
596 : CPLStringList osLayerWithRefList;
597 : CPLString FindLayerOCG(GDALPDFDictionary* poPageDict,
598 : const char* pszLayerName);
599 : void FindLayersGeneric(GDALPDFDictionary* poPageDict);
600 :
601 : int bUseOCG;
602 :
603 : public:
604 : PDFDataset();
605 : virtual ~PDFDataset();
606 :
607 : virtual const char* GetProjectionRef();
608 : virtual CPLErr GetGeoTransform( double * );
609 :
610 : virtual CPLErr SetProjection(const char* pszWKTIn);
611 : virtual CPLErr SetGeoTransform(double* padfGeoTransform);
612 :
613 : virtual char **GetMetadata( const char * pszDomain = "" );
614 : virtual CPLErr SetMetadata( char ** papszMetadata,
615 : const char * pszDomain = "" );
616 : virtual const char *GetMetadataItem( const char * pszName,
617 : const char * pszDomain = "" );
618 : virtual CPLErr SetMetadataItem( const char * pszName,
619 : const char * pszValue,
620 : const char * pszDomain = "" );
621 :
622 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
623 : void *, int, int, GDALDataType,
624 : int, int *, int, int, int );
625 :
626 : virtual int GetGCPCount();
627 : virtual const char *GetGCPProjection();
628 : virtual const GDAL_GCP *GetGCPs();
629 : virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
630 : const char *pszGCPProjection );
631 :
632 : CPLErr ReadPixels( int nReqXOff, int nReqYOff,
633 : int nReqXSize, int nReqYSize,
634 : int nPixelSpace,
635 : int nLineSpace,
636 : int nBandSpace,
637 : GByte* pabyData );
638 :
639 : static GDALDataset *Open( GDALOpenInfo * );
640 : static int Identify( GDALOpenInfo * );
641 : };
642 :
643 : /************************************************************************/
644 : /* ==================================================================== */
645 : /* PDFRasterBand */
646 : /* ==================================================================== */
647 : /************************************************************************/
648 :
649 : class PDFRasterBand : public GDALPamRasterBand
650 554 : {
651 : friend class PDFDataset;
652 :
653 : CPLErr IReadBlockFromTile( int, int, void * );
654 :
655 : public:
656 :
657 : PDFRasterBand( PDFDataset *, int );
658 :
659 : virtual CPLErr IReadBlock( int, int, void * );
660 : virtual GDALColorInterp GetColorInterpretation();
661 : };
662 :
663 :
664 : /************************************************************************/
665 : /* PDFRasterBand() */
666 : /************************************************************************/
667 :
668 554 : PDFRasterBand::PDFRasterBand( PDFDataset *poDS, int nBand )
669 :
670 : {
671 554 : this->poDS = poDS;
672 554 : this->nBand = nBand;
673 :
674 554 : eDataType = GDT_Byte;
675 :
676 554 : if( poDS->nBlockXSize )
677 : {
678 40 : nBlockXSize = poDS->nBlockXSize;
679 40 : nBlockYSize = poDS->nBlockYSize;
680 : }
681 514 : else if( poDS->GetRasterXSize() < 64 * 1024 * 1024 / poDS->GetRasterYSize() )
682 : {
683 514 : nBlockXSize = poDS->GetRasterXSize();
684 514 : nBlockYSize = 1;
685 : }
686 : else
687 : {
688 0 : nBlockXSize = MIN(1024, poDS->GetRasterXSize());
689 0 : nBlockYSize = MIN(1024, poDS->GetRasterYSize());
690 0 : poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
691 : }
692 554 : }
693 :
694 : /************************************************************************/
695 : /* GetColorInterpretation() */
696 : /************************************************************************/
697 :
698 0 : GDALColorInterp PDFRasterBand::GetColorInterpretation()
699 : {
700 0 : PDFDataset *poGDS = (PDFDataset *) poDS;
701 0 : if (poGDS->nBands == 1)
702 0 : return GCI_GrayIndex;
703 : else
704 0 : return (GDALColorInterp)(GCI_RedBand + (nBand - 1));
705 : }
706 :
707 : /************************************************************************/
708 : /* IReadBlockFromTile() */
709 : /************************************************************************/
710 :
711 284 : CPLErr PDFRasterBand::IReadBlockFromTile( int nBlockXOff, int nBlockYOff,
712 : void * pImage )
713 :
714 : {
715 284 : PDFDataset *poGDS = (PDFDataset *) poDS;
716 :
717 284 : int nReqXSize = nBlockXSize;
718 284 : int nReqYSize = nBlockYSize;
719 284 : if( (nBlockXOff + 1) * nBlockXSize > nRasterXSize )
720 42 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
721 284 : if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize )
722 50 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
723 :
724 284 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
725 284 : int iTile = poGDS->aiTiles[nBlockYOff * nXBlocks + nBlockXOff];
726 284 : GDALPDFTileDesc& sTile = poGDS->asTiles[iTile];
727 284 : GDALPDFObject* poImage = sTile.poImage;
728 :
729 284 : if( iTile < 0 )
730 : {
731 0 : memset(pImage, 0, nBlockXSize * nBlockYSize);
732 0 : return CE_None;
733 : }
734 :
735 284 : if( nBand == 4 )
736 : {
737 60 : GDALPDFDictionary* poImageDict = poImage->GetDictionary();
738 60 : GDALPDFObject* poSMask = poImageDict->Get("SMask");
739 60 : if( poSMask != NULL && poSMask->GetType() == PDFObjectType_Dictionary )
740 : {
741 60 : GDALPDFDictionary* poSMaskDict = poSMask->GetDictionary();
742 60 : GDALPDFObject* poWidth = poSMaskDict->Get("Width");
743 60 : GDALPDFObject* poHeight = poSMaskDict->Get("Height");
744 60 : GDALPDFObject* poColorSpace = poSMaskDict->Get("ColorSpace");
745 60 : GDALPDFObject* poBitsPerComponent = poSMaskDict->Get("BitsPerComponent");
746 60 : int nBits = 0;
747 60 : if( poBitsPerComponent )
748 60 : nBits = (int)Get(poBitsPerComponent);
749 180 : if (poWidth && Get(poWidth) == nReqXSize &&
750 : poHeight && Get(poHeight) == nReqYSize &&
751 60 : poColorSpace && poColorSpace->GetType() == PDFObjectType_Name &&
752 60 : poColorSpace->GetName() == "DeviceGray" &&
753 : (nBits == 1 || nBits == 8) )
754 : {
755 60 : GDALPDFStream* poStream = poSMask->GetStream();
756 60 : GByte* pabyStream = NULL;
757 :
758 60 : if( poStream == NULL )
759 0 : return CE_Failure;
760 :
761 60 : pabyStream = (GByte*) poStream->GetBytes();
762 60 : if( pabyStream == NULL )
763 0 : return CE_Failure;
764 :
765 60 : int nReqXSize1 = (nReqXSize + 7) / 8;
766 76 : if( (nBits == 8 && poStream->GetLength() != nReqXSize * nReqYSize) ||
767 16 : (nBits == 1 && poStream->GetLength() != nReqXSize1 * nReqYSize) )
768 : {
769 0 : VSIFree(pabyStream);
770 0 : return CE_Failure;
771 : }
772 :
773 60 : GByte* pabyData = (GByte*) pImage;
774 60 : if( nReqXSize != nBlockXSize || nReqYSize != nBlockYSize )
775 : {
776 20 : memset(pabyData, 0, nBlockXSize * nBlockYSize);
777 : }
778 :
779 60 : if( nBits == 8 )
780 : {
781 1372 : for(int j = 0; j < nReqYSize; j++)
782 : {
783 43824 : for(int i = 0; i < nReqXSize; i++)
784 : {
785 42496 : pabyData[j * nBlockXSize + i] = pabyStream[j * nReqXSize + i];
786 : }
787 : }
788 : }
789 : else
790 : {
791 488 : for(int j = 0; j < nReqYSize; j++)
792 : {
793 6576 : for(int i = 0; i < nReqXSize; i++)
794 : {
795 6104 : if( pabyStream[j * nReqXSize1 + i / 8] & (1 << (7 - (i % 8))) )
796 1792 : pabyData[j * nBlockXSize + i] = 255;
797 : else
798 4312 : pabyData[j * nBlockXSize + i] = 0;
799 : }
800 : }
801 : }
802 :
803 60 : VSIFree(pabyStream);
804 60 : return CE_None;
805 : }
806 : }
807 :
808 0 : memset(pImage, 255, nBlockXSize * nBlockYSize);
809 0 : return CE_None;
810 : }
811 :
812 224 : if( poGDS->nLastBlockXOff == nBlockXOff &&
813 : poGDS->nLastBlockYOff == nBlockYOff &&
814 : poGDS->pabyCachedData != NULL )
815 : {
816 : CPLDebug("PDF", "Using cached block (%d, %d)",
817 0 : nBlockXOff, nBlockYOff);
818 : // do nothing
819 : }
820 : else
821 : {
822 224 : if (poGDS->bTried == FALSE)
823 : {
824 7 : poGDS->bTried = TRUE;
825 7 : poGDS->pabyCachedData = (GByte*)VSIMalloc3(3, nBlockXSize, nBlockYSize);
826 : }
827 224 : if (poGDS->pabyCachedData == NULL)
828 0 : return CE_Failure;
829 :
830 224 : GDALPDFStream* poStream = poImage->GetStream();
831 224 : GByte* pabyStream = NULL;
832 :
833 224 : if( poStream == NULL )
834 0 : return CE_Failure;
835 :
836 224 : pabyStream = (GByte*) poStream->GetBytes();
837 224 : if( pabyStream == NULL )
838 0 : return CE_Failure;
839 :
840 224 : if( poStream->GetLength() != sTile.nBands * nReqXSize * nReqYSize)
841 : {
842 0 : VSIFree(pabyStream);
843 0 : return CE_Failure;
844 : }
845 :
846 224 : memcpy(poGDS->pabyCachedData, pabyStream, poStream->GetLength());
847 224 : VSIFree(pabyStream);
848 224 : poGDS->nLastBlockXOff = nBlockXOff;
849 224 : poGDS->nLastBlockYOff = nBlockYOff;
850 : }
851 :
852 224 : GByte* pabyData = (GByte*) pImage;
853 224 : if( nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize) )
854 : {
855 63 : memset(pabyData, 0, nBlockXSize * nBlockYSize);
856 : }
857 :
858 404 : if( poGDS->nBands >= 3 && sTile.nBands == 3 )
859 : {
860 5580 : for(int j = 0; j < nReqYSize; j++)
861 : {
862 151200 : for(int i = 0; i < nReqXSize; i++)
863 : {
864 145800 : pabyData[j * nBlockXSize + i] = poGDS->pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1];
865 : }
866 : }
867 : }
868 44 : else if( sTile.nBands == 1 )
869 : {
870 8362 : for(int j = 0; j < nReqYSize; j++)
871 : {
872 2405861 : for(int i = 0; i < nReqXSize; i++)
873 : {
874 2397543 : pabyData[j * nBlockXSize + i] = poGDS->pabyCachedData[j * nReqXSize + i];
875 : }
876 : }
877 : }
878 :
879 224 : return CE_None;
880 : }
881 :
882 : /************************************************************************/
883 : /* IReadBlock() */
884 : /************************************************************************/
885 :
886 21725 : CPLErr PDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
887 : void * pImage )
888 :
889 : {
890 21725 : PDFDataset *poGDS = (PDFDataset *) poDS;
891 :
892 21725 : if (poGDS->aiTiles.size() )
893 : {
894 284 : if ( IReadBlockFromTile(nBlockXOff, nBlockYOff,
895 : pImage) == CE_None )
896 : {
897 284 : return CE_None;
898 : }
899 : else
900 : {
901 0 : poGDS->aiTiles.resize(0);
902 0 : poGDS->bTried = FALSE;
903 0 : CPLFree(poGDS->pabyCachedData);
904 0 : poGDS->pabyCachedData = NULL;
905 0 : poGDS->nLastBlockXOff = -1;
906 0 : poGDS->nLastBlockYOff = -1;
907 : }
908 : }
909 :
910 21441 : int nReqXSize = nBlockXSize;
911 21441 : int nReqYSize = nBlockYSize;
912 21441 : if( (nBlockXOff + 1) * nBlockXSize > nRasterXSize )
913 0 : nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize;
914 21441 : if( nBlockYSize == 1 )
915 21441 : nReqYSize = nRasterYSize;
916 0 : else if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize )
917 0 : nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize;
918 :
919 21441 : if (poGDS->bTried == FALSE)
920 : {
921 52 : poGDS->bTried = TRUE;
922 52 : if( nBlockYSize == 1 )
923 52 : poGDS->pabyCachedData = (GByte*)VSIMalloc3(MAX(3, poGDS->nBands), nRasterXSize, nRasterYSize);
924 : else
925 0 : poGDS->pabyCachedData = (GByte*)VSIMalloc3(MAX(3, poGDS->nBands), nBlockXSize, nBlockYSize);
926 : }
927 21441 : if (poGDS->pabyCachedData == NULL)
928 0 : return CE_Failure;
929 :
930 21441 : if( poGDS->nLastBlockXOff == nBlockXOff &&
931 : (nBlockYSize == 1 || poGDS->nLastBlockYOff == nBlockYOff) &&
932 : poGDS->pabyCachedData != NULL )
933 : {
934 : /*CPLDebug("PDF", "Using cached block (%d, %d)",
935 : nBlockXOff, nBlockYOff);*/
936 : // do nothing
937 : }
938 : else
939 : {
940 : #ifdef HAVE_PODOFO
941 52 : if (!poGDS->bUsePoppler && nBand == 4)
942 : {
943 0 : memset(pImage, 255, nBlockXSize * nBlockYSize);
944 0 : return CE_None;
945 : }
946 : #endif
947 :
948 : CPLErr eErr = poGDS->ReadPixels( nBlockXOff * nBlockXSize,
949 : (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize,
950 : nReqXSize,
951 : nReqYSize,
952 : 1,
953 : nBlockXSize,
954 : nBlockXSize * ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize),
955 52 : poGDS->pabyCachedData);
956 52 : if( eErr == CE_None )
957 : {
958 52 : poGDS->nLastBlockXOff = nBlockXOff;
959 52 : poGDS->nLastBlockYOff = nBlockYOff;
960 : }
961 : else
962 : {
963 0 : CPLFree(poGDS->pabyCachedData);
964 0 : poGDS->pabyCachedData = NULL;
965 : }
966 :
967 : }
968 21441 : if (poGDS->pabyCachedData == NULL)
969 0 : return CE_Failure;
970 :
971 21441 : if( nBlockYSize == 1 )
972 : memcpy(pImage,
973 : poGDS->pabyCachedData + (nBand - 1) * nBlockXSize * nRasterYSize + nBlockYOff * nBlockXSize,
974 21441 : nBlockXSize);
975 : else
976 : memcpy(pImage,
977 : poGDS->pabyCachedData + (nBand - 1) * nBlockXSize * nBlockYSize,
978 0 : nBlockXSize * nBlockYSize);
979 :
980 21441 : return CE_None;
981 : }
982 :
983 : /************************************************************************/
984 : /* ReadPixels() */
985 : /************************************************************************/
986 :
987 52 : CPLErr PDFDataset::ReadPixels( int nReqXOff, int nReqYOff,
988 : int nReqXSize, int nReqYSize,
989 : int nPixelSpace, int nLineSpace, int nBandSpace,
990 : GByte* pabyData )
991 : {
992 52 : CPLErr eErr = CE_None;
993 52 : const char* pszRenderingOptions = CPLGetConfigOption("GDAL_PDF_RENDERING_OPTIONS", NULL);
994 :
995 : #ifdef HAVE_POPPLER
996 52 : if(bUsePoppler)
997 : {
998 : SplashColor sColor;
999 48 : sColor[0] = 255;
1000 48 : sColor[1] = 255;
1001 48 : sColor[2] = 255;
1002 : GDALPDFOutputDev *poSplashOut;
1003 : poSplashOut = new GDALPDFOutputDev((nBands < 4) ? splashModeRGB8 : splashModeXBGR8,
1004 : 4, gFalse,
1005 48 : (nBands < 4) ? sColor : NULL);
1006 :
1007 48 : if (pszRenderingOptions != NULL)
1008 : {
1009 3 : poSplashOut->SetEnableVector(FALSE);
1010 3 : poSplashOut->SetEnableText(FALSE);
1011 3 : poSplashOut->SetEnableBitmap(FALSE);
1012 :
1013 3 : char** papszTokens = CSLTokenizeString2( pszRenderingOptions, " ,", 0 );
1014 8 : for(int i=0;papszTokens[i] != NULL;i++)
1015 : {
1016 5 : if (EQUAL(papszTokens[i], "VECTOR"))
1017 2 : poSplashOut->SetEnableVector(TRUE);
1018 3 : else if (EQUAL(papszTokens[i], "TEXT"))
1019 1 : poSplashOut->SetEnableText(TRUE);
1020 4 : else if (EQUAL(papszTokens[i], "RASTER") ||
1021 0 : EQUAL(papszTokens[i], "BITMAP"))
1022 2 : poSplashOut->SetEnableBitmap(TRUE);
1023 : else
1024 : {
1025 : CPLError(CE_Warning, CPLE_NotSupported,
1026 : "Value %s is not a valid value for GDAL_PDF_RENDERING_OPTIONS",
1027 0 : papszTokens[i]);
1028 : }
1029 : }
1030 3 : CSLDestroy(papszTokens);
1031 : }
1032 :
1033 48 : PDFDoc* poDoc = poDocPoppler;
1034 : #ifdef POPPLER_0_20_OR_LATER
1035 48 : poSplashOut->startDoc(poDoc);
1036 : #else
1037 : poSplashOut->startDoc(poDoc->getXRef());
1038 : #endif
1039 :
1040 : /* EVIL: we modify a private member... */
1041 : /* poppler (at least 0.12 and 0.14 versions) don't render correctly */
1042 : /* some PDFs and display an error message 'Could not find a OCG with Ref' */
1043 : /* in those cases. This processing of optional content is an addition of */
1044 : /* poppler in comparison to original xpdf, which hasn't the issue. All in */
1045 : /* all, nullifying optContent removes the error message and improves the rendering */
1046 : #ifdef POPPLER_HAS_OPTCONTENT
1047 48 : Catalog* poCatalog = poDoc->getCatalog();
1048 48 : OCGs* poOldOCGs = poCatalog->optContent;
1049 48 : if (!bUseOCG)
1050 45 : poCatalog->optContent = NULL;
1051 : #endif
1052 : poDoc->displayPageSlice(poSplashOut,
1053 : iPage,
1054 : dfDPI, dfDPI,
1055 : 0,
1056 : TRUE, gFalse, gFalse,
1057 : nReqXOff, nReqYOff,
1058 48 : nReqXSize, nReqYSize);
1059 :
1060 : /* Restore back */
1061 : #ifdef POPPLER_HAS_OPTCONTENT
1062 48 : poCatalog->optContent = poOldOCGs;
1063 : #endif
1064 :
1065 48 : SplashBitmap* poBitmap = poSplashOut->getBitmap();
1066 48 : if (poBitmap->getWidth() != nReqXSize || poBitmap->getHeight() != nReqYSize)
1067 : {
1068 : CPLError(CE_Failure, CPLE_AppDefined,
1069 : "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)" ,
1070 : poBitmap->getWidth(), poBitmap->getHeight(),
1071 0 : nReqXSize, nReqYSize);
1072 0 : delete poSplashOut;
1073 0 : return CE_Failure;
1074 : }
1075 :
1076 48 : GByte* pabyDataR = pabyData;
1077 48 : GByte* pabyDataG = pabyData + nBandSpace;
1078 48 : GByte* pabyDataB = pabyData + 2 * nBandSpace;
1079 48 : GByte* pabyDataA = pabyData + 3 * nBandSpace;
1080 48 : GByte* pabySrc = poBitmap->getDataPtr();
1081 48 : GByte* pabyAlphaSrc = (GByte*)poBitmap->getAlphaPtr();
1082 : int i, j;
1083 17619 : for(j=0;j<nReqYSize;j++)
1084 : {
1085 17315602 : for(i=0;i<nReqXSize;i++)
1086 : {
1087 17298031 : if (nBands < 4)
1088 : {
1089 17225131 : pabyDataR[i * nPixelSpace] = pabySrc[i * 3 + 0];
1090 17225131 : pabyDataG[i * nPixelSpace] = pabySrc[i * 3 + 1];
1091 17225131 : pabyDataB[i * nPixelSpace] = pabySrc[i * 3 + 2];
1092 : }
1093 : else
1094 : {
1095 72900 : pabyDataR[i * nPixelSpace] = pabySrc[i * 4 + 2];
1096 72900 : pabyDataG[i * nPixelSpace] = pabySrc[i * 4 + 1];
1097 72900 : pabyDataB[i * nPixelSpace] = pabySrc[i * 4 + 0];
1098 72900 : pabyDataA[i * nPixelSpace] = pabyAlphaSrc[i];
1099 : }
1100 : }
1101 17571 : pabyDataR += nLineSpace;
1102 17571 : pabyDataG += nLineSpace;
1103 17571 : pabyDataB += nLineSpace;
1104 17571 : pabyDataA += nLineSpace;
1105 17571 : pabyAlphaSrc += poBitmap->getAlphaRowSize();
1106 17571 : pabySrc += poBitmap->getRowSize();
1107 : }
1108 48 : delete poSplashOut;
1109 : }
1110 : #endif // HAVE_POPPLER
1111 :
1112 : #ifdef HAVE_PODOFO
1113 52 : if (!bUsePoppler)
1114 : {
1115 4 : if( bPdfToPpmFailed )
1116 0 : return CE_Failure;
1117 :
1118 4 : if (pszRenderingOptions != NULL)
1119 : {
1120 : CPLError(CE_Warning, CPLE_NotSupported,
1121 : "GDAL_PDF_RENDERING_OPTIONS only supported "
1122 0 : "when PDF driver is compiled against Poppler.");
1123 : }
1124 :
1125 4 : CPLString osTmpFilename;
1126 : int nRet;
1127 :
1128 : #ifdef notdef
1129 : int bUseSpawn = CSLTestBoolean(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES"));
1130 : if( !bUseSpawn )
1131 : {
1132 : CPLString osCmd = CPLSPrintf("pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"",
1133 : dfDPI,
1134 : nReqXOff,
1135 : nReqYOff,
1136 : nReqXSize,
1137 : nReqYSize,
1138 : iPage, iPage,
1139 : osFilename.c_str());
1140 :
1141 : if (osUserPwd.size() != 0)
1142 : {
1143 : osCmd += " -upw \"";
1144 : osCmd += osUserPwd;
1145 : osCmd += "\"";
1146 : }
1147 :
1148 : CPLString osTmpFilenamePrefix = CPLGenerateTempFilename("pdf");
1149 : osTmpFilename = CPLSPrintf("%s-%d.ppm",
1150 : osTmpFilenamePrefix.c_str(),
1151 : iPage);
1152 : osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str());
1153 :
1154 : CPLDebug("PDF", "Running '%s'", osCmd.c_str());
1155 : nRet = CPLSystem(NULL, osCmd.c_str());
1156 : }
1157 : else
1158 : #endif
1159 : {
1160 4 : char** papszArgs = NULL;
1161 4 : papszArgs = CSLAddString(papszArgs, "pdftoppm");
1162 4 : papszArgs = CSLAddString(papszArgs, "-r");
1163 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", dfDPI));
1164 4 : papszArgs = CSLAddString(papszArgs, "-x");
1165 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff));
1166 4 : papszArgs = CSLAddString(papszArgs, "-y");
1167 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff));
1168 4 : papszArgs = CSLAddString(papszArgs, "-W");
1169 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize));
1170 4 : papszArgs = CSLAddString(papszArgs, "-H");
1171 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize));
1172 4 : papszArgs = CSLAddString(papszArgs, "-f");
1173 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", iPage));
1174 4 : papszArgs = CSLAddString(papszArgs, "-l");
1175 4 : papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", iPage));
1176 4 : if (osUserPwd.size() != 0)
1177 : {
1178 0 : papszArgs = CSLAddString(papszArgs, "-upw");
1179 0 : papszArgs = CSLAddString(papszArgs, osUserPwd.c_str());
1180 : }
1181 4 : papszArgs = CSLAddString(papszArgs, osFilename.c_str());
1182 :
1183 4 : osTmpFilename = CPLSPrintf("/vsimem/pdf/temp_%p.ppm", this);
1184 4 : VSILFILE* fpOut = VSIFOpenL(osTmpFilename, "wb");
1185 4 : if( fpOut != NULL )
1186 : {
1187 4 : nRet = CPLSpawn(papszArgs, NULL, fpOut, FALSE);
1188 4 : VSIFCloseL(fpOut);
1189 : }
1190 : else
1191 0 : nRet = -1;
1192 :
1193 4 : CSLDestroy(papszArgs);
1194 : }
1195 :
1196 4 : if (nRet == 0)
1197 : {
1198 4 : GDALDataset* poDS = (GDALDataset*) GDALOpen(osTmpFilename, GA_ReadOnly);
1199 4 : if (poDS)
1200 : {
1201 4 : if (poDS->GetRasterCount() == 3)
1202 : {
1203 : eErr = poDS->RasterIO(GF_Read, 0, 0,
1204 : nReqXSize,
1205 : nReqYSize,
1206 : pabyData,
1207 : nReqXSize, nReqYSize,
1208 : GDT_Byte, 3, NULL,
1209 4 : nPixelSpace, nLineSpace, nBandSpace);
1210 : }
1211 4 : delete poDS;
1212 : }
1213 : }
1214 : else
1215 : {
1216 0 : CPLDebug("PDF", "Ret code = %d", nRet);
1217 0 : bPdfToPpmFailed = TRUE;
1218 0 : eErr = CE_Failure;
1219 : }
1220 4 : VSIUnlink(osTmpFilename);
1221 : }
1222 : #endif
1223 :
1224 52 : return eErr;
1225 : }
1226 :
1227 : /************************************************************************/
1228 : /* ==================================================================== */
1229 : /* PDFImageRasterBand */
1230 : /* ==================================================================== */
1231 : /************************************************************************/
1232 :
1233 : class PDFImageRasterBand : public PDFRasterBand
1234 4 : {
1235 : friend class PDFDataset;
1236 :
1237 : public:
1238 :
1239 : PDFImageRasterBand( PDFDataset *, int );
1240 :
1241 : virtual CPLErr IReadBlock( int, int, void * );
1242 : };
1243 :
1244 :
1245 : /************************************************************************/
1246 : /* PDFImageRasterBand() */
1247 : /************************************************************************/
1248 :
1249 4 : PDFImageRasterBand::PDFImageRasterBand( PDFDataset *poDS, int nBand ) : PDFRasterBand(poDS, nBand)
1250 :
1251 : {
1252 4 : }
1253 :
1254 : /************************************************************************/
1255 : /* IReadBlock() */
1256 : /************************************************************************/
1257 :
1258 70 : CPLErr PDFImageRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
1259 : void * pImage )
1260 :
1261 : {
1262 70 : PDFDataset *poGDS = (PDFDataset *) poDS;
1263 70 : CPLAssert(poGDS->poImageObj != NULL);
1264 :
1265 70 : if (poGDS->bTried == FALSE)
1266 : {
1267 2 : int nBands = (poGDS->nBands == 1) ? 1 : 3;
1268 2 : poGDS->bTried = TRUE;
1269 2 : if (nBands == 3)
1270 : {
1271 1 : poGDS->pabyCachedData = (GByte*)VSIMalloc3(nBands, nRasterXSize, nRasterYSize);
1272 1 : if (poGDS->pabyCachedData == NULL)
1273 0 : return CE_Failure;
1274 : }
1275 :
1276 2 : GDALPDFStream* poStream = poGDS->poImageObj->GetStream();
1277 2 : GByte* pabyStream = NULL;
1278 :
1279 6 : if (poStream == NULL ||
1280 2 : poStream->GetLength() != nBands * nRasterXSize * nRasterYSize ||
1281 2 : (pabyStream = (GByte*) poStream->GetBytes()) == NULL)
1282 : {
1283 0 : VSIFree(poGDS->pabyCachedData);
1284 0 : poGDS->pabyCachedData = NULL;
1285 0 : return CE_Failure;
1286 : }
1287 :
1288 2 : if (nBands == 3)
1289 : {
1290 : /* pixel interleaved to band interleaved */
1291 2501 : for(int i = 0; i < nRasterXSize * nRasterYSize; i++)
1292 : {
1293 2500 : poGDS->pabyCachedData[0 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 0];
1294 2500 : poGDS->pabyCachedData[1 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 1];
1295 2500 : poGDS->pabyCachedData[2 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 2];
1296 : }
1297 1 : VSIFree(pabyStream);
1298 : }
1299 : else
1300 1 : poGDS->pabyCachedData = pabyStream;
1301 : }
1302 :
1303 70 : if (poGDS->pabyCachedData == NULL)
1304 0 : return CE_Failure;
1305 :
1306 70 : if (nBand == 4)
1307 0 : memset(pImage, 255, nRasterXSize);
1308 : else
1309 : memcpy(pImage,
1310 : poGDS->pabyCachedData + (nBand - 1) * nRasterXSize * nRasterYSize + nBlockYOff * nRasterXSize,
1311 70 : nRasterXSize);
1312 :
1313 70 : return CE_None;
1314 : }
1315 :
1316 : /************************************************************************/
1317 : /* ~PDFDataset() */
1318 : /************************************************************************/
1319 :
1320 182 : PDFDataset::PDFDataset()
1321 : {
1322 182 : bUsePoppler = FALSE;
1323 : #ifdef HAVE_POPPLER
1324 182 : poDocPoppler = NULL;
1325 : #endif
1326 : #ifdef HAVE_PODOFO
1327 182 : poDocPodofo = NULL;
1328 182 : bPdfToPpmFailed = FALSE;
1329 : #endif
1330 182 : poImageObj = NULL;
1331 182 : pszWKT = NULL;
1332 182 : dfDPI = DEFAULT_DPI;
1333 182 : dfMaxArea = 0;
1334 182 : adfGeoTransform[0] = 0;
1335 182 : adfGeoTransform[1] = 1;
1336 182 : adfGeoTransform[2] = 0;
1337 182 : adfGeoTransform[3] = 0;
1338 182 : adfGeoTransform[4] = 0;
1339 182 : adfGeoTransform[5] = 1;
1340 182 : bHasCTM = FALSE;
1341 182 : bGeoTransformValid = FALSE;
1342 182 : nGCPCount = 0;
1343 182 : pasGCPList = NULL;
1344 182 : bProjDirty = FALSE;
1345 182 : bNeatLineDirty = FALSE;
1346 182 : bInfoDirty = FALSE;
1347 182 : bXMPDirty = FALSE;
1348 182 : bTried = FALSE;
1349 182 : pabyCachedData = NULL;
1350 182 : nLastBlockXOff = -1;
1351 182 : nLastBlockYOff = -1;
1352 182 : iPage = -1;
1353 182 : poNeatLine = NULL;
1354 182 : bUseOCG = FALSE;
1355 182 : poCatalogObject = NULL;
1356 : #ifdef HAVE_POPPLER
1357 182 : poCatalogObjectPoppler = NULL;
1358 : #endif
1359 182 : nBlockXSize = 0;
1360 182 : nBlockYSize = 0;
1361 182 : }
1362 :
1363 : /************************************************************************/
1364 : /* PDFFreeDoc() */
1365 : /************************************************************************/
1366 :
1367 : #ifdef HAVE_POPPLER
1368 182 : static void PDFFreeDoc(PDFDoc* poDoc)
1369 : {
1370 182 : if (poDoc)
1371 : {
1372 : /* hack to avoid potential cross heap issues on Win32 */
1373 : /* str is the VSIPDFFileStream object passed in the constructor of PDFDoc */
1374 149 : delete poDoc->str;
1375 149 : poDoc->str = NULL;
1376 :
1377 149 : delete poDoc;
1378 : }
1379 182 : }
1380 : #endif
1381 :
1382 : /************************************************************************/
1383 : /* GetCatalog() */
1384 : /************************************************************************/
1385 :
1386 12 : GDALPDFObject* PDFDataset::GetCatalog()
1387 : {
1388 12 : if (poCatalogObject)
1389 0 : return poCatalogObject;
1390 :
1391 : #ifdef HAVE_POPPLER
1392 12 : if (bUsePoppler)
1393 : {
1394 8 : poCatalogObjectPoppler = new ObjectAutoFree;
1395 8 : poDocPoppler->getXRef()->getCatalog(poCatalogObjectPoppler);
1396 8 : if (!poCatalogObjectPoppler->isNull())
1397 8 : poCatalogObject = new GDALPDFObjectPoppler(poCatalogObjectPoppler, FALSE);
1398 : }
1399 : #endif
1400 :
1401 : #ifdef HAVE_PODOFO
1402 12 : if (!bUsePoppler)
1403 : {
1404 4 : int nCatalogNum = 0, nCatalogGen = 0;
1405 4 : VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb");
1406 4 : if (fp != NULL)
1407 : {
1408 4 : GDALPDFWriter oWriter(fp, TRUE);
1409 4 : if (oWriter.ParseTrailerAndXRef())
1410 : {
1411 4 : nCatalogNum = oWriter.GetCatalogNum();
1412 4 : nCatalogGen = oWriter.GetCatalogGen();
1413 : }
1414 4 : oWriter.Close();
1415 : }
1416 :
1417 : PoDoFo::PdfObject* poCatalogPodofo =
1418 4 : poDocPodofo->GetObjects().GetObject(PoDoFo::PdfReference(nCatalogNum, nCatalogGen));
1419 4 : if (poCatalogPodofo)
1420 4 : poCatalogObject = new GDALPDFObjectPodofo(poCatalogPodofo, poDocPodofo->GetObjects());
1421 : }
1422 : #endif
1423 :
1424 12 : return poCatalogObject;
1425 : }
1426 :
1427 : /************************************************************************/
1428 : /* ~PDFDataset() */
1429 : /************************************************************************/
1430 :
1431 182 : PDFDataset::~PDFDataset()
1432 : {
1433 182 : CPLFree(pabyCachedData);
1434 182 : pabyCachedData = NULL;
1435 :
1436 182 : delete poNeatLine;
1437 182 : poNeatLine = NULL;
1438 :
1439 : /* Collect data necessary to update */
1440 182 : int nNum = poPageObj->GetRefNum();
1441 182 : int nGen = poPageObj->GetRefGen();
1442 182 : GDALPDFDictionaryRW* poPageDictCopy = NULL;
1443 182 : GDALPDFDictionaryRW* poCatalogDictCopy = NULL;
1444 204 : if (eAccess == GA_Update &&
1445 : (bProjDirty || bNeatLineDirty || bInfoDirty || bXMPDirty) &&
1446 : nNum != 0 &&
1447 : poPageObj != NULL &&
1448 22 : poPageObj->GetType() == PDFObjectType_Dictionary)
1449 : {
1450 22 : poPageDictCopy = poPageObj->GetDictionary()->Clone();
1451 :
1452 22 : if (bXMPDirty)
1453 : {
1454 : /* We need the catalog because it points to the XMP Metadata object */
1455 6 : GetCatalog();
1456 6 : if (poCatalogObject && poCatalogObject->GetType() == PDFObjectType_Dictionary)
1457 6 : poCatalogDictCopy = poCatalogObject->GetDictionary()->Clone();
1458 : }
1459 : }
1460 :
1461 : /* Close document (and file descriptor) to be able to open it */
1462 : /* in read-write mode afterwards */
1463 182 : delete poPageObj;
1464 182 : poPageObj = NULL;
1465 182 : delete poCatalogObject;
1466 182 : poCatalogObject = NULL;
1467 : #ifdef HAVE_POPPLER
1468 182 : delete poCatalogObjectPoppler;
1469 182 : PDFFreeDoc(poDocPoppler);
1470 182 : poDocPoppler = NULL;
1471 : #endif
1472 : #ifdef HAVE_PODOFO
1473 182 : delete poDocPodofo;
1474 182 : poDocPodofo = NULL;
1475 : #endif
1476 :
1477 : /* Now do the update */
1478 182 : if (poPageDictCopy)
1479 : {
1480 22 : VSILFILE* fp = VSIFOpenL(osFilename, "rb+");
1481 22 : if (fp != NULL)
1482 : {
1483 22 : GDALPDFWriter oWriter(fp, TRUE);
1484 22 : if (oWriter.ParseTrailerAndXRef())
1485 : {
1486 22 : if ((bProjDirty || bNeatLineDirty) && poPageDictCopy != NULL)
1487 : oWriter.UpdateProj(this, dfDPI,
1488 10 : poPageDictCopy, nNum, nGen);
1489 :
1490 22 : if (bInfoDirty)
1491 6 : oWriter.UpdateInfo(this);
1492 :
1493 22 : if (bXMPDirty && poCatalogDictCopy != NULL)
1494 6 : oWriter.UpdateXMP(this, poCatalogDictCopy);
1495 : }
1496 22 : oWriter.Close();
1497 : }
1498 : else
1499 : {
1500 : CPLError(CE_Failure, CPLE_AppDefined,
1501 0 : "Cannot open %s in update mode", osFilename.c_str());
1502 : }
1503 : }
1504 182 : delete poPageDictCopy;
1505 182 : poPageDictCopy = NULL;
1506 182 : delete poCatalogDictCopy;
1507 182 : poCatalogDictCopy = NULL;
1508 :
1509 182 : if( nGCPCount > 0 )
1510 : {
1511 5 : GDALDeinitGCPs( nGCPCount, pasGCPList );
1512 5 : CPLFree( pasGCPList );
1513 5 : pasGCPList = NULL;
1514 5 : nGCPCount = 0;
1515 : }
1516 182 : CPLFree(pszWKT);
1517 182 : pszWKT = NULL;
1518 182 : }
1519 :
1520 :
1521 : /************************************************************************/
1522 : /* IRasterIO() */
1523 : /************************************************************************/
1524 :
1525 1670 : CPLErr PDFDataset::IRasterIO( GDALRWFlag eRWFlag,
1526 : int nXOff, int nYOff, int nXSize, int nYSize,
1527 : void * pData, int nBufXSize, int nBufYSize,
1528 : GDALDataType eBufType,
1529 : int nBandCount, int *panBandMap,
1530 : int nPixelSpace, int nLineSpace, int nBandSpace )
1531 : {
1532 : int nBandBlockXSize, nBandBlockYSize;
1533 1670 : GetRasterBand(1)->GetBlockSize(&nBandBlockXSize, &nBandBlockYSize);
1534 1670 : if( aiTiles.size() == 0 &&
1535 : eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
1536 : (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) &&
1537 : eBufType == GDT_Byte && nBandCount == nBands &&
1538 0 : (nBands >= 3 && panBandMap[0] == 1 && panBandMap[1] == 2 &&
1539 0 : panBandMap[2] == 3 && ( nBands == 3 || panBandMap[3] == 4)) )
1540 : {
1541 0 : int bReadPixels = TRUE;
1542 : #ifdef HAVE_PODOFO
1543 0 : if (!bUsePoppler && nBands == 4)
1544 : {
1545 0 : bReadPixels = FALSE;
1546 : }
1547 : #endif
1548 0 : if( bReadPixels )
1549 : return ReadPixels(nXOff, nYOff, nXSize, nYSize,
1550 0 : nPixelSpace, nLineSpace, nBandSpace, (GByte*)pData);
1551 : }
1552 :
1553 : return GDALPamDataset::IRasterIO( eRWFlag,
1554 : nXOff, nYOff, nXSize, nYSize,
1555 : pData, nBufXSize, nBufYSize,
1556 : eBufType,
1557 : nBandCount, panBandMap,
1558 1670 : nPixelSpace, nLineSpace, nBandSpace );
1559 : }
1560 :
1561 : /************************************************************************/
1562 : /* Identify() */
1563 : /************************************************************************/
1564 :
1565 11628 : int PDFDataset::Identify( GDALOpenInfo * poOpenInfo )
1566 : {
1567 11628 : if (strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0)
1568 1 : return TRUE;
1569 11627 : if (strncmp(poOpenInfo->pszFilename, "PDF_IMAGE:", 10) == 0)
1570 2 : return TRUE;
1571 :
1572 11625 : if (poOpenInfo->nHeaderBytes < 128)
1573 11284 : return FALSE;
1574 :
1575 341 : return strncmp((const char*)poOpenInfo->pabyHeader, "%PDF", 4) == 0;
1576 : }
1577 :
1578 : /************************************************************************/
1579 : /* PDFDatasetErrorFunction() */
1580 : /************************************************************************/
1581 :
1582 : #ifdef HAVE_POPPLER
1583 : #ifdef POPPLER_0_20_OR_LATER
1584 0 : static void PDFDatasetErrorFunction(void* userData, ErrorCategory eErrCatagory,
1585 : #ifdef POPPLER_0_23_OR_LATER
1586 : Goffset nPos,
1587 : #else
1588 : int nPos,
1589 : #endif
1590 : char *pszMsg)
1591 : {
1592 0 : CPLString osError;
1593 :
1594 0 : if (nPos >= 0)
1595 0 : osError.Printf("Pos = %d, ", (int)nPos);
1596 0 : osError += pszMsg;
1597 :
1598 0 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
1599 : return;
1600 :
1601 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
1602 : }
1603 : #else
1604 : static void PDFDatasetErrorFunction(int nPos, char *pszMsg, va_list args)
1605 : {
1606 : CPLString osError;
1607 :
1608 : if (nPos >= 0)
1609 : osError.Printf("Pos = %d, ", nPos);
1610 : osError += CPLString().vPrintf(pszMsg, args);
1611 :
1612 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
1613 : return;
1614 :
1615 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
1616 : }
1617 : #endif
1618 : #endif
1619 :
1620 : /************************************************************************/
1621 : /* GDALPDFParseStreamContentOnlyDrawForm() */
1622 : /************************************************************************/
1623 :
1624 : static
1625 171 : CPLString GDALPDFParseStreamContentOnlyDrawForm(const char* pszContent)
1626 : {
1627 171 : CPLString osToken;
1628 : char ch;
1629 171 : int nCurIdx = 0;
1630 171 : CPLString osCurrentForm;
1631 :
1632 : //CPLDebug("PDF", "content = %s", pszContent);
1633 :
1634 608 : while((ch = *pszContent) != '\0')
1635 : {
1636 435 : if (ch == '%')
1637 : {
1638 : /* Skip comments until end-of-line */
1639 0 : while((ch = *pszContent) != '\0')
1640 : {
1641 0 : if (ch == '\r' || ch == '\n')
1642 0 : break;
1643 0 : pszContent ++;
1644 : }
1645 0 : if (ch == 0)
1646 0 : break;
1647 : }
1648 446 : else if (ch == ' ' || ch == '\r' || ch == '\n')
1649 : {
1650 180 : if (osToken.size())
1651 : {
1652 180 : if (nCurIdx == 0 && osToken[0] == '/')
1653 : {
1654 9 : osCurrentForm = osToken.substr(1);
1655 9 : nCurIdx ++;
1656 : }
1657 171 : else if (nCurIdx == 1 && osToken == "Do")
1658 : {
1659 2 : nCurIdx ++;
1660 : }
1661 : else
1662 : {
1663 169 : return "";
1664 : }
1665 : }
1666 11 : osToken = "";
1667 : }
1668 : else
1669 255 : osToken += ch;
1670 266 : pszContent ++;
1671 : }
1672 :
1673 2 : return osCurrentForm;
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* GDALPDFParseStreamContent() */
1678 : /************************************************************************/
1679 :
1680 : typedef enum
1681 : {
1682 : STATE_INIT,
1683 : STATE_AFTER_q,
1684 : STATE_AFTER_cm,
1685 : STATE_AFTER_Do
1686 : } PDFStreamState;
1687 :
1688 : /* This parser is reduced to understanding sequences that draw rasters, such as :
1689 : q
1690 : scaleX 0 0 scaleY translateX translateY cm
1691 : /ImXXX Do
1692 : Q
1693 :
1694 : All other sequences will abort the parsing.
1695 :
1696 : Returns TRUE if the stream only contains images.
1697 : */
1698 :
1699 : static
1700 171 : int GDALPDFParseStreamContent(const char* pszContent,
1701 : GDALPDFDictionary* poXObjectDict,
1702 : double* pdfDPI,
1703 : int* pbDPISet,
1704 : int* pnBands,
1705 : std::vector<GDALPDFTileDesc>& asTiles,
1706 : int bAcceptRotationTerms)
1707 : {
1708 171 : CPLString osToken;
1709 : char ch;
1710 171 : PDFStreamState nState = STATE_INIT;
1711 171 : int nCurIdx = 0;
1712 : double adfVals[6];
1713 171 : CPLString osCurrentImage;
1714 :
1715 171 : double dfDPI = 72.0;
1716 171 : *pbDPISet = FALSE;
1717 :
1718 13186 : while((ch = *pszContent) != '\0')
1719 : {
1720 12859 : if (ch == '%')
1721 : {
1722 : /* Skip comments until end-of-line */
1723 0 : while((ch = *pszContent) != '\0')
1724 : {
1725 0 : if (ch == '\r' || ch == '\n')
1726 0 : break;
1727 0 : pszContent ++;
1728 : }
1729 0 : if (ch == 0)
1730 0 : break;
1731 : }
1732 16599 : else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
1733 : {
1734 3755 : if (osToken.size())
1735 : {
1736 3755 : if (nState == STATE_INIT)
1737 : {
1738 355 : if (osToken == "q")
1739 : {
1740 340 : nState = STATE_AFTER_q;
1741 340 : nCurIdx = 0;
1742 : }
1743 15 : else if (osToken != "Q")
1744 15 : return FALSE;
1745 : }
1746 3400 : else if (nState == STATE_AFTER_q)
1747 : {
1748 2380 : if (osToken == "q")
1749 : {
1750 : // ignore
1751 : }
1752 2380 : else if (nCurIdx < 6)
1753 : {
1754 2040 : adfVals[nCurIdx ++] = CPLAtof(osToken);
1755 : }
1756 340 : else if (nCurIdx == 6 && osToken == "cm")
1757 : {
1758 340 : nState = STATE_AFTER_cm;
1759 340 : nCurIdx = 0;
1760 : }
1761 : else
1762 0 : return FALSE;
1763 : }
1764 1020 : else if (nState == STATE_AFTER_cm)
1765 : {
1766 680 : if (nCurIdx == 0 && osToken[0] == '/')
1767 : {
1768 340 : osCurrentImage = osToken.substr(1);
1769 : }
1770 340 : else if (osToken == "Do")
1771 : {
1772 340 : nState = STATE_AFTER_Do;
1773 : }
1774 : else
1775 0 : return FALSE;
1776 : }
1777 340 : else if (nState == STATE_AFTER_Do)
1778 : {
1779 340 : if (osToken == "Q")
1780 : {
1781 340 : GDALPDFObject* poImage = poXObjectDict->Get(osCurrentImage);
1782 340 : if (poImage != NULL && poImage->GetType() == PDFObjectType_Dictionary)
1783 : {
1784 : GDALPDFTileDesc sTile;
1785 340 : GDALPDFDictionary* poImageDict = poImage->GetDictionary();
1786 340 : GDALPDFObject* poWidth = poImageDict->Get("Width");
1787 340 : GDALPDFObject* poHeight = poImageDict->Get("Height");
1788 340 : GDALPDFObject* poColorSpace = poImageDict->Get("ColorSpace");
1789 340 : GDALPDFObject* poSMask = poImageDict->Get("SMask");
1790 340 : if (poColorSpace && poColorSpace->GetType() == PDFObjectType_Name)
1791 : {
1792 337 : if (poColorSpace->GetName() == "DeviceRGB")
1793 : {
1794 140 : sTile.nBands = 3;
1795 140 : if ( *pnBands < 3)
1796 24 : *pnBands = 3;
1797 : }
1798 197 : else if (poColorSpace->GetName() == "DeviceGray")
1799 : {
1800 197 : sTile.nBands = 1;
1801 197 : if ( *pnBands < 1)
1802 137 : *pnBands = 1;
1803 : }
1804 : else
1805 0 : sTile.nBands = 0;
1806 : }
1807 340 : if ( poSMask != NULL )
1808 126 : *pnBands = 4;
1809 :
1810 1020 : if (poWidth && poHeight && ((bAcceptRotationTerms && adfVals[1] == -adfVals[2]) ||
1811 680 : (!bAcceptRotationTerms && adfVals[1] == 0.0 && adfVals[2] == 0.0)))
1812 : {
1813 340 : double dfWidth = Get(poWidth);
1814 340 : double dfHeight = Get(poHeight);
1815 340 : double dfScaleX = adfVals[0];
1816 340 : double dfScaleY = adfVals[3];
1817 340 : double dfDPI_X = ROUND_TO_INT_IF_CLOSE(dfWidth / dfScaleX * 72, 1e-3);
1818 340 : double dfDPI_Y = ROUND_TO_INT_IF_CLOSE(dfHeight / dfScaleY * 72, 1e-3);
1819 : //CPLDebug("PDF", "Image %s, width = %.16g, height = %.16g, scaleX = %.16g, scaleY = %.16g --> DPI_X = %.16g, DPI_Y = %.16g",
1820 : // osCurrentImage.c_str(), dfWidth, dfHeight, dfScaleX, dfScaleY, dfDPI_X, dfDPI_Y);
1821 340 : if (dfDPI_X > dfDPI) dfDPI = dfDPI_X;
1822 340 : if (dfDPI_Y > dfDPI) dfDPI = dfDPI_Y;
1823 :
1824 340 : memcpy(&(sTile.adfCM), adfVals, 6 * sizeof(double));
1825 340 : sTile.poImage = poImage;
1826 340 : sTile.dfWidth = dfWidth;
1827 340 : sTile.dfHeight = dfHeight;
1828 340 : asTiles.push_back(sTile);
1829 :
1830 340 : *pbDPISet = TRUE;
1831 340 : *pdfDPI = dfDPI;
1832 : }
1833 : }
1834 340 : nState = STATE_INIT;
1835 : }
1836 : else
1837 0 : return FALSE;
1838 : }
1839 : }
1840 3740 : osToken = "";
1841 : }
1842 : else
1843 9104 : osToken += ch;
1844 12844 : pszContent ++;
1845 : }
1846 :
1847 156 : return TRUE;
1848 : }
1849 :
1850 : /************************************************************************/
1851 : /* CheckTiledRaster() */
1852 : /************************************************************************/
1853 :
1854 164 : int PDFDataset::CheckTiledRaster()
1855 : {
1856 : size_t i;
1857 164 : int nBlockXSize = 0, nBlockYSize = 0;
1858 :
1859 : /* First pass : check that all tiles have same DPI, */
1860 : /* are contained entirely in the raster size, */
1861 : /* and determine the block size */
1862 501 : for(i=0; i<asTiles.size(); i++)
1863 : {
1864 340 : double dfDrawWidth = asTiles[i].adfCM[0] * dfDPI / 72.0;
1865 340 : double dfDrawHeight = asTiles[i].adfCM[3] * dfDPI / 72.0;
1866 340 : double dfX = asTiles[i].adfCM[4] * dfDPI / 72.0;
1867 340 : double dfY = asTiles[i].adfCM[5] * dfDPI / 72.0;
1868 340 : int nX = (int)(dfX+0.1);
1869 340 : int nY = (int)(dfY+0.1);
1870 340 : int nWidth = (int)(asTiles[i].dfWidth + 1e-8);
1871 340 : int nHeight = (int)(asTiles[i].dfHeight + 1e-8);
1872 :
1873 340 : GDALPDFDictionary* poImageDict = asTiles[i].poImage->GetDictionary();
1874 340 : GDALPDFObject* poBitsPerComponent = poImageDict->Get("BitsPerComponent");
1875 340 : GDALPDFObject* poColorSpace = poImageDict->Get("ColorSpace");
1876 340 : GDALPDFObject* poFilter = poImageDict->Get("Filter");
1877 :
1878 : /* Podofo cannot uncompress JPEG2000 streams */
1879 520 : if( !bUsePoppler && poFilter != NULL &&
1880 90 : poFilter->GetType() == PDFObjectType_Name &&
1881 90 : poFilter->GetName() == "JPXDecode" )
1882 : {
1883 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
1884 0 : (int)i);
1885 0 : return FALSE;
1886 : }
1887 :
1888 1214 : if( poBitsPerComponent == NULL ||
1889 : Get(poBitsPerComponent) != 8 ||
1890 : poColorSpace == NULL ||
1891 340 : poColorSpace->GetType() != PDFObjectType_Name ||
1892 337 : (poColorSpace->GetName() != "DeviceRGB" &&
1893 197 : poColorSpace->GetName() != "DeviceGray") )
1894 : {
1895 : CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading",
1896 3 : (int)i);
1897 3 : return FALSE;
1898 : }
1899 :
1900 337 : if( fabs(dfDrawWidth - asTiles[i].dfWidth) > 1e-2 ||
1901 : fabs(dfDrawHeight - asTiles[i].dfHeight) > 1e-2 ||
1902 : fabs(nWidth - asTiles[i].dfWidth) > 1e-8 ||
1903 : fabs(nHeight - asTiles[i].dfHeight) > 1e-8 ||
1904 : fabs(nX - dfX) > 1e-1 ||
1905 : fabs(nY - dfY) > 1e-1 ||
1906 : nX < 0 || nY < 0 || nX + nWidth > nRasterXSize ||
1907 : nY >= nRasterYSize )
1908 : {
1909 : CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f",
1910 : (int)i, dfX, dfY, dfDrawWidth, dfDrawHeight,
1911 0 : asTiles[i].dfWidth, asTiles[i].dfHeight);
1912 0 : return FALSE;
1913 : }
1914 337 : if( nBlockXSize == 0 && nBlockYSize == 0 &&
1915 : nX == 0 && nY != 0 )
1916 : {
1917 12 : nBlockXSize = nWidth;
1918 12 : nBlockYSize = nHeight;
1919 : }
1920 : }
1921 161 : if( nBlockXSize <= 0 || nBlockYSize <= 0 || nBlockXSize > 2048 || nBlockYSize > 2048 )
1922 149 : return FALSE;
1923 :
1924 12 : int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
1925 12 : int nYBlocks = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
1926 :
1927 : /* Second pass to determine that all tiles are properly aligned on block size */
1928 200 : for(i=0; i<asTiles.size(); i++)
1929 : {
1930 188 : double dfX = asTiles[i].adfCM[4] * dfDPI / 72.0;
1931 188 : double dfY = asTiles[i].adfCM[5] * dfDPI / 72.0;
1932 188 : int nX = (int)(dfX+0.1);
1933 188 : int nY = (int)(dfY+0.1);
1934 188 : int nWidth = (int)(asTiles[i].dfWidth + 1e-8);
1935 188 : int nHeight = (int)(asTiles[i].dfHeight + 1e-8);
1936 188 : int bOK = TRUE;
1937 188 : int nBlockXOff = nX / nBlockXSize;
1938 188 : if( (nX % nBlockXSize) != 0 )
1939 0 : bOK = FALSE;
1940 188 : if( nBlockXOff < nXBlocks - 1 && nWidth != nBlockXSize )
1941 0 : bOK = FALSE;
1942 188 : if( nBlockXOff == nXBlocks - 1 && nX + nWidth != nRasterXSize )
1943 0 : bOK = FALSE;
1944 :
1945 188 : if( nY > 0 && nHeight != nBlockYSize )
1946 0 : bOK = FALSE;
1947 188 : if( nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * nBlockYSize)
1948 0 : bOK = FALSE;
1949 :
1950 188 : if( !bOK )
1951 : {
1952 : CPLDebug("PDF", "Tile %d : %d %d %d %d",
1953 0 : (int)i, nX, nY, nWidth, nHeight);
1954 0 : return FALSE;
1955 : }
1956 : }
1957 :
1958 : /* Third pass to set the aiTiles array */
1959 12 : aiTiles.resize(nXBlocks * nYBlocks, -1);
1960 200 : for(i=0; i<asTiles.size(); i++)
1961 : {
1962 188 : double dfX = asTiles[i].adfCM[4] * dfDPI / 72.0;
1963 188 : double dfY = asTiles[i].adfCM[5] * dfDPI / 72.0;
1964 188 : int nHeight = (int)(asTiles[i].dfHeight + 1e-8);
1965 188 : int nX = (int)(dfX+0.1);
1966 188 : int nY = nRasterYSize - ((int)(dfY+0.1) + nHeight);
1967 188 : int nBlockXOff = nX / nBlockXSize;
1968 188 : int nBlockYOff = nY / nBlockYSize;
1969 188 : aiTiles[ nBlockYOff * nXBlocks + nBlockXOff ] = (int) i;
1970 : }
1971 :
1972 12 : this->nBlockXSize = nBlockXSize;
1973 12 : this->nBlockYSize = nBlockYSize;
1974 :
1975 12 : return TRUE;
1976 : }
1977 :
1978 : /************************************************************************/
1979 : /* GuessDPI() */
1980 : /************************************************************************/
1981 :
1982 180 : void PDFDataset::GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands)
1983 : {
1984 180 : const char* pszDPI = CPLGetConfigOption("GDAL_PDF_DPI", NULL);
1985 180 : if (pszDPI != NULL)
1986 : {
1987 1 : dfDPI = atof(pszDPI);
1988 : }
1989 : else
1990 : {
1991 : /* Try to get a better value from the images that are drawn */
1992 : /* Very simplistic logic. Will only work for raster only PDF */
1993 :
1994 179 : GDALPDFObject* poContents = poPageDict->Get("Contents");
1995 179 : if (poContents != NULL && poContents->GetType() == PDFObjectType_Array)
1996 : {
1997 3 : GDALPDFArray* poContentsArray = poContents->GetArray();
1998 3 : if (poContentsArray->GetLength() == 1)
1999 : {
2000 2 : poContents = poContentsArray->Get(0);
2001 : }
2002 : }
2003 :
2004 179 : GDALPDFObject* poXObject = poPageDict->LookupObject("Resources.XObject");
2005 529 : if (poContents != NULL &&
2006 179 : poContents->GetType() == PDFObjectType_Dictionary &&
2007 : poXObject != NULL &&
2008 171 : poXObject->GetType() == PDFObjectType_Dictionary)
2009 : {
2010 171 : GDALPDFDictionary* poXObjectDict = poXObject->GetDictionary();
2011 171 : GDALPDFDictionary* poContentDict = poXObjectDict;
2012 171 : GDALPDFStream* poPageStream = poContents->GetStream();
2013 171 : if (poPageStream != NULL)
2014 : {
2015 171 : char* pszContent = NULL;
2016 171 : int nLength = poPageStream->GetLength();
2017 171 : int bResetTiles = FALSE;
2018 171 : double dfScaleDPI = 1.0;
2019 :
2020 171 : if( nLength < 100000 )
2021 : {
2022 171 : CPLString osForm;
2023 171 : pszContent = poPageStream->GetBytes();
2024 171 : if( pszContent != NULL )
2025 : {
2026 : #ifdef DEBUG
2027 171 : const char* pszDumpStream = CPLGetConfigOption("PDF_DUMP_STREAM", NULL);
2028 171 : if( pszDumpStream != NULL )
2029 : {
2030 0 : VSILFILE* fpDump = VSIFOpenL(pszDumpStream, "wb");
2031 0 : if( fpDump )
2032 : {
2033 0 : VSIFWriteL(pszContent, 1, nLength, fpDump);
2034 0 : VSIFCloseL(fpDump);
2035 : }
2036 : }
2037 : #endif
2038 171 : osForm = GDALPDFParseStreamContentOnlyDrawForm(pszContent);
2039 171 : if (osForm.size() == 0)
2040 : {
2041 : /* Special case for USGS Topo PDF, like CA_Hollywood_20090811_OM_geo.pdf */
2042 169 : const char* pszOGCDo = strstr(pszContent, " /XO1 Do");
2043 169 : if( pszOGCDo )
2044 : {
2045 0 : const char* pszcm = strstr(pszContent, " cm ");
2046 0 : if( pszcm != NULL && pszcm < pszOGCDo )
2047 : {
2048 0 : const char* pszNextcm = strstr(pszcm + 2, "cm");
2049 0 : if( pszNextcm == NULL || pszNextcm > pszOGCDo )
2050 : {
2051 0 : const char* pszIter = pszcm;
2052 0 : while( pszIter > pszContent )
2053 : {
2054 0 : if( (*pszIter >= '0' && *pszIter <= '9') ||
2055 : *pszIter == '-' ||
2056 : *pszIter == '.' ||
2057 : *pszIter == ' ' )
2058 0 : pszIter --;
2059 : else
2060 : {
2061 0 : pszIter ++;
2062 0 : break;
2063 : }
2064 : }
2065 0 : CPLString oscm(pszIter);
2066 0 : oscm.resize(pszcm - pszIter);
2067 0 : char** papszTokens = CSLTokenizeString(oscm);
2068 0 : double dfScaleX = -1, dfScaleY = -2;
2069 0 : if( CSLCount(papszTokens) == 6 )
2070 : {
2071 0 : dfScaleX = CPLAtof(papszTokens[0]);
2072 0 : dfScaleY = CPLAtof(papszTokens[3]);
2073 : }
2074 0 : CSLDestroy(papszTokens);
2075 0 : if( dfScaleX == dfScaleY && dfScaleX > 0.0 )
2076 : {
2077 0 : osForm = "XO1";
2078 0 : bResetTiles = TRUE;
2079 0 : dfScaleDPI = 1.0 / dfScaleX;
2080 0 : }
2081 : }
2082 : }
2083 : else
2084 : {
2085 0 : osForm = "XO1";
2086 0 : bResetTiles = TRUE;
2087 : }
2088 : }
2089 : /* Special case for USGS Topo PDF, like CA_Sacramento_East_20120308_TM_geo.pdf */
2090 : else
2091 : {
2092 169 : CPLString osOCG = FindLayerOCG(poPageDict, "Orthoimage");
2093 169 : if( osOCG.size() )
2094 : {
2095 0 : const char* pszBDCLookup = CPLSPrintf("/OC /%s BDC", osOCG.c_str());
2096 0 : const char* pszBDC = strstr(pszContent, pszBDCLookup);
2097 0 : if( pszBDC != NULL )
2098 : {
2099 0 : const char* pszIter = pszBDC + strlen(pszBDCLookup);
2100 0 : while( *pszIter != '\0' )
2101 : {
2102 0 : if( *pszIter == 13 || *pszIter == 10 ||
2103 : *pszIter == ' '|| *pszIter == 'q' )
2104 0 : pszIter ++;
2105 : else
2106 0 : break;
2107 : }
2108 0 : if( strncmp(pszIter, "1 0 0 1 0 0 cm\n",
2109 : strlen( "1 0 0 1 0 0 cm\n" )) == 0 )
2110 0 : pszIter += strlen("1 0 0 1 0 0 cm\n");
2111 0 : if( *pszIter == '/' )
2112 : {
2113 0 : pszIter ++;
2114 0 : const char* pszDo = strstr(pszIter, " Do");
2115 0 : if( pszDo != NULL )
2116 : {
2117 0 : osForm = pszIter;
2118 0 : osForm.resize(pszDo - pszIter);
2119 0 : bResetTiles = TRUE;
2120 : }
2121 : }
2122 : }
2123 169 : }
2124 : }
2125 : }
2126 : }
2127 :
2128 171 : if (osForm.size())
2129 : {
2130 2 : CPLFree(pszContent);
2131 2 : pszContent = NULL;
2132 :
2133 2 : GDALPDFObject* poObjForm = poXObjectDict->Get(osForm);
2134 6 : if (poObjForm != NULL &&
2135 2 : poObjForm->GetType() == PDFObjectType_Dictionary &&
2136 2 : (poPageStream = poObjForm->GetStream()) != NULL)
2137 : {
2138 2 : GDALPDFDictionary* poObjFormDict = poObjForm->GetDictionary();
2139 : GDALPDFObject* poSubtype;
2140 6 : if ((poSubtype = poObjFormDict->Get("Subtype")) != NULL &&
2141 2 : poSubtype->GetType() == PDFObjectType_Name &&
2142 2 : poSubtype->GetName() == "Form")
2143 : {
2144 2 : int nLength = poPageStream->GetLength();
2145 2 : if( nLength < 100000 )
2146 : {
2147 2 : pszContent = poPageStream->GetBytes();
2148 :
2149 2 : GDALPDFObject* poXObject2 = poObjFormDict->LookupObject("Resources.XObject");
2150 2 : if( poXObject2 != NULL &&
2151 0 : poXObject2->GetType() == PDFObjectType_Dictionary )
2152 0 : poContentDict = poXObject2->GetDictionary();
2153 : }
2154 : }
2155 : }
2156 171 : }
2157 : }
2158 :
2159 171 : if (pszContent != NULL)
2160 : {
2161 171 : int bDPISet = FALSE;
2162 :
2163 171 : const char* pszContentToParse = pszContent;
2164 171 : if( bResetTiles )
2165 : {
2166 0 : while( *pszContentToParse != '\0' )
2167 : {
2168 0 : if( *pszContentToParse == 13 || *pszContentToParse == 10 ||
2169 : *pszContentToParse == ' ' ||
2170 : (*pszContentToParse >= '0' && *pszContentToParse <= '9') ||
2171 : *pszContentToParse == '.' ||
2172 : *pszContentToParse == '-' ||
2173 : *pszContentToParse == 'l' ||
2174 : *pszContentToParse == 'm' ||
2175 : *pszContentToParse == 'n' ||
2176 : *pszContentToParse == 'W' )
2177 0 : pszContentToParse ++;
2178 : else
2179 0 : break;
2180 : }
2181 : }
2182 :
2183 : GDALPDFParseStreamContent(pszContentToParse,
2184 : poContentDict,
2185 : &(dfDPI),
2186 : &bDPISet,
2187 : pnBands,
2188 : asTiles,
2189 171 : bResetTiles);
2190 171 : CPLFree(pszContent);
2191 171 : if (bDPISet)
2192 : {
2193 164 : dfDPI *= dfScaleDPI;
2194 :
2195 164 : CPLDebug("PDF", "DPI guessed from contents stream = %.16g", dfDPI);
2196 164 : SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI));
2197 164 : if( bResetTiles )
2198 0 : asTiles.resize(0);
2199 : }
2200 : else
2201 7 : asTiles.resize(0);
2202 : }
2203 : }
2204 : }
2205 :
2206 179 : GDALPDFObject* poUserUnit = NULL;
2207 379 : if ( (poUserUnit = poPageDict->Get("UserUnit")) != NULL &&
2208 169 : (poUserUnit->GetType() == PDFObjectType_Int ||
2209 31 : poUserUnit->GetType() == PDFObjectType_Real) )
2210 : {
2211 169 : dfDPI = ROUND_TO_INT_IF_CLOSE(Get(poUserUnit) * 72.0);
2212 169 : CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", dfDPI);
2213 169 : SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI));
2214 : }
2215 : }
2216 :
2217 180 : if (dfDPI < 1 || dfDPI > 7200)
2218 : {
2219 : CPLError(CE_Warning, CPLE_AppDefined,
2220 0 : "Invalid value for GDAL_PDF_DPI. Using default value instead");
2221 0 : dfDPI = DEFAULT_DPI;
2222 : }
2223 180 : }
2224 :
2225 : /************************************************************************/
2226 : /* FindXMP() */
2227 : /************************************************************************/
2228 :
2229 719 : void PDFDataset::FindXMP(GDALPDFObject* poObj)
2230 : {
2231 719 : if (poObj->GetType() != PDFObjectType_Dictionary)
2232 233 : return;
2233 :
2234 486 : GDALPDFDictionary* poDict = poObj->GetDictionary();
2235 486 : GDALPDFObject* poType = poDict->Get("Type");
2236 486 : GDALPDFObject* poSubtype = poDict->Get("Subtype");
2237 1264 : if (poType == NULL ||
2238 385 : poType->GetType() != PDFObjectType_Name ||
2239 385 : poType->GetName() != "Metadata" ||
2240 : poSubtype == NULL ||
2241 4 : poSubtype->GetType() != PDFObjectType_Name ||
2242 4 : poSubtype->GetName() != "XML")
2243 : {
2244 482 : return;
2245 : }
2246 :
2247 4 : GDALPDFStream* poStream = poObj->GetStream();
2248 4 : if (poStream == NULL)
2249 0 : return;
2250 :
2251 4 : char* pszContent = poStream->GetBytes();
2252 4 : int nLength = (int)poStream->GetLength();
2253 4 : if (pszContent != NULL && nLength > 15 &&
2254 : strncmp(pszContent, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
2255 : {
2256 : char *apszMDList[2];
2257 4 : apszMDList[0] = pszContent;
2258 4 : apszMDList[1] = NULL;
2259 4 : SetMetadata(apszMDList, "xml:XMP");
2260 : }
2261 4 : CPLFree(pszContent);
2262 : }
2263 :
2264 : /************************************************************************/
2265 : /* ParseInfo() */
2266 : /************************************************************************/
2267 :
2268 182 : void PDFDataset::ParseInfo(GDALPDFObject* poInfoObj)
2269 : {
2270 182 : if (poInfoObj->GetType() != PDFObjectType_Dictionary)
2271 130 : return;
2272 :
2273 52 : GDALPDFDictionary* poInfoObjDict = poInfoObj->GetDictionary();
2274 52 : GDALPDFObject* poItem = NULL;
2275 52 : int bOneMDISet = FALSE;
2276 62 : if ((poItem = poInfoObjDict->Get("Author")) != NULL &&
2277 10 : poItem->GetType() == PDFObjectType_String)
2278 : {
2279 10 : SetMetadataItem("AUTHOR", poItem->GetString().c_str());
2280 10 : bOneMDISet = TRUE;
2281 : }
2282 65 : if ((poItem = poInfoObjDict->Get("Creator")) != NULL &&
2283 13 : poItem->GetType() == PDFObjectType_String)
2284 : {
2285 13 : SetMetadataItem("CREATOR", poItem->GetString().c_str());
2286 13 : bOneMDISet = TRUE;
2287 : }
2288 54 : if ((poItem = poInfoObjDict->Get("Keywords")) != NULL &&
2289 2 : poItem->GetType() == PDFObjectType_String)
2290 : {
2291 2 : SetMetadataItem("KEYWORDS", poItem->GetString().c_str());
2292 2 : bOneMDISet = TRUE;
2293 : }
2294 56 : if ((poItem = poInfoObjDict->Get("Subject")) != NULL &&
2295 4 : poItem->GetType() == PDFObjectType_String)
2296 : {
2297 4 : SetMetadataItem("SUBJECT", poItem->GetString().c_str());
2298 4 : bOneMDISet = TRUE;
2299 : }
2300 56 : if ((poItem = poInfoObjDict->Get("Title")) != NULL &&
2301 4 : poItem->GetType() == PDFObjectType_String)
2302 : {
2303 4 : SetMetadataItem("TITLE", poItem->GetString().c_str());
2304 4 : bOneMDISet = TRUE;
2305 : }
2306 83 : if ((poItem = poInfoObjDict->Get("Producer")) != NULL &&
2307 31 : poItem->GetType() == PDFObjectType_String)
2308 : {
2309 31 : if (bOneMDISet || poItem->GetString() != "PoDoFo - http://podofo.sf.net")
2310 : {
2311 4 : SetMetadataItem("PRODUCER", poItem->GetString().c_str());
2312 4 : bOneMDISet = TRUE;
2313 : }
2314 : }
2315 90 : if ((poItem = poInfoObjDict->Get("CreationDate")) != NULL &&
2316 38 : poItem->GetType() == PDFObjectType_String)
2317 : {
2318 38 : if (bOneMDISet)
2319 11 : SetMetadataItem("CREATION_DATE", poItem->GetString().c_str());
2320 : }
2321 : }
2322 :
2323 : /************************************************************************/
2324 : /* PDFSanitizeLayerName() */
2325 : /************************************************************************/
2326 :
2327 : static
2328 81 : CPLString PDFSanitizeLayerName(const char* pszName)
2329 : {
2330 81 : CPLString osName;
2331 882 : for(int i=0; pszName[i] != '\0'; i++)
2332 : {
2333 842 : if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',')
2334 41 : osName += "_";
2335 760 : else if (pszName[i] != '"')
2336 760 : osName += pszName[i];
2337 : }
2338 0 : return osName;
2339 : }
2340 :
2341 : #ifdef HAVE_POPPLER
2342 :
2343 : /************************************************************************/
2344 : /* AddLayer() */
2345 : /************************************************************************/
2346 :
2347 79 : void PDFDataset::AddLayer(const char* pszLayerName, OptionalContentGroup* ocg)
2348 : {
2349 79 : int nNewIndex = osLayerList.size() /*/ 2*/;
2350 :
2351 79 : if (nNewIndex == 100)
2352 : {
2353 0 : CPLStringList osNewLayerList;
2354 0 : for(int i=0;i<100;i++)
2355 : {
2356 : osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_NAME", i),
2357 0 : osLayerList[/*2 * */ i] + strlen("LAYER_00_NAME="));
2358 : /*osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_INIT_STATE", i),
2359 : osLayerList[i * 2 + 1] + strlen("LAYER_00_INIT_STATE="));*/
2360 : }
2361 0 : osLayerList = osNewLayerList;
2362 : }
2363 :
2364 : char szFormatName[64];
2365 : /* char szFormatInitState[64]; */
2366 79 : sprintf(szFormatName, "LAYER_%%0%dd_NAME", nNewIndex >= 100 ? 3 : 2);
2367 : /* sprintf(szFormatInitState, "LAYER_%%0%dd_INIT_STATE", nNewIndex >= 100 ? 3 : 2); */
2368 :
2369 : osLayerList.AddNameValue(CPLSPrintf(szFormatName, nNewIndex),
2370 79 : pszLayerName);
2371 : /*osLayerList.AddNameValue(CPLSPrintf(szFormatInitState, nNewIndex),
2372 : (ocg == NULL || ocg->getState() == OptionalContentGroup::On) ? "ON" : "OFF");*/
2373 79 : oLayerOCGMap[pszLayerName] = ocg;
2374 :
2375 : //if (ocg != NULL && ocg->getState() == OptionalContentGroup::Off)
2376 : // bUseOCG = TRUE;
2377 79 : }
2378 :
2379 : /************************************************************************/
2380 : /* ExploreLayers() */
2381 : /************************************************************************/
2382 :
2383 47 : void PDFDataset::ExploreLayers(GDALPDFArray* poArray,
2384 : int nRecLevel,
2385 : CPLString osTopLayer)
2386 : {
2387 47 : if( nRecLevel == 16 )
2388 0 : return;
2389 :
2390 47 : int nLength = poArray->GetLength();
2391 47 : CPLString osCurLayer;
2392 150 : for(int i=0;i<nLength;i++)
2393 : {
2394 103 : GDALPDFObject* poObj = poArray->Get(i);
2395 103 : if (i == 0 && poObj->GetType() == PDFObjectType_String)
2396 : {
2397 1 : CPLString osName = PDFSanitizeLayerName(poObj->GetString().c_str());
2398 1 : if (osTopLayer.size())
2399 0 : osTopLayer = osTopLayer + "." + osName;
2400 : else
2401 1 : osTopLayer = osName;
2402 1 : AddLayer(osTopLayer.c_str(), NULL);
2403 : }
2404 102 : else if (poObj->GetType() == PDFObjectType_Array)
2405 : {
2406 24 : ExploreLayers(poObj->GetArray(), nRecLevel + 1, osCurLayer);
2407 24 : osCurLayer = "";
2408 : }
2409 78 : else if (poObj->GetType() == PDFObjectType_Dictionary)
2410 : {
2411 78 : GDALPDFDictionary* poDict = poObj->GetDictionary();
2412 78 : GDALPDFObject* poName = poDict->Get("Name");
2413 78 : if (poName != NULL && poName->GetType() == PDFObjectType_String)
2414 : {
2415 78 : CPLString osName = PDFSanitizeLayerName(poName->GetString().c_str());
2416 78 : if (osTopLayer.size())
2417 44 : osCurLayer = osTopLayer + "." + osName;
2418 : else
2419 34 : osCurLayer = osName;
2420 : //CPLDebug("PDF", "Layer %s", osCurLayer.c_str());
2421 :
2422 78 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
2423 : struct Ref r;
2424 78 : r.num = poObj->GetRefNum();
2425 78 : r.gen = poObj->GetRefGen();
2426 78 : OptionalContentGroup* ocg = optContentConfig->findOcgByRef(r);
2427 78 : if (ocg)
2428 : {
2429 78 : AddLayer(osCurLayer.c_str(), ocg);
2430 : osLayerWithRefList.AddString(
2431 78 : CPLSPrintf("%s %d %d", osCurLayer.c_str(), r.num, r.gen));
2432 78 : }
2433 : }
2434 : }
2435 47 : }
2436 : }
2437 :
2438 : /************************************************************************/
2439 : /* FindLayers() */
2440 : /************************************************************************/
2441 :
2442 149 : void PDFDataset::FindLayers()
2443 : {
2444 149 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
2445 149 : if (optContentConfig == NULL || !optContentConfig->isOk() )
2446 126 : return;
2447 :
2448 23 : Array* array = optContentConfig->getOrderArray();
2449 23 : if (array)
2450 : {
2451 23 : GDALPDFArray* poArray = GDALPDFCreateArray(array);
2452 23 : ExploreLayers(poArray, 0);
2453 46 : delete poArray;
2454 : }
2455 : else
2456 : {
2457 0 : GooList* ocgList = optContentConfig->getOCGs();
2458 0 : for(int i=0;i<ocgList->getLength();i++)
2459 : {
2460 0 : OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
2461 0 : if( ocg != NULL && ocg->getName() != NULL )
2462 0 : AddLayer((const char*)ocg->getName()->getCString(), ocg);
2463 : }
2464 : }
2465 :
2466 23 : oMDMD.SetMetadata(osLayerList.List(), "LAYERS");
2467 : }
2468 :
2469 : /************************************************************************/
2470 : /* TurnLayersOnOff() */
2471 : /************************************************************************/
2472 :
2473 149 : void PDFDataset::TurnLayersOnOff()
2474 : {
2475 149 : OCGs* optContentConfig = poDocPoppler->getOptContentConfig();
2476 149 : if (optContentConfig == NULL || !optContentConfig->isOk() )
2477 126 : return;
2478 :
2479 : // Which layers to turn ON ?
2480 23 : const char* pszLayers = CPLGetConfigOption("GDAL_PDF_LAYERS", NULL);
2481 23 : if (pszLayers)
2482 : {
2483 : int i;
2484 2 : int bAll = EQUAL(pszLayers, "ALL");
2485 2 : GooList* ocgList = optContentConfig->getOCGs();
2486 12 : for(i=0;i<ocgList->getLength();i++)
2487 : {
2488 10 : OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i);
2489 10 : ocg->setState( (bAll) ? OptionalContentGroup::On : OptionalContentGroup::Off );
2490 : }
2491 :
2492 2 : char** papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
2493 4 : for(i=0;!bAll && papszLayers[i] != NULL;i++)
2494 : {
2495 : std::map<CPLString, OptionalContentGroup*>::iterator oIter =
2496 2 : oLayerOCGMap.find(papszLayers[i]);
2497 2 : if (oIter != oLayerOCGMap.end())
2498 : {
2499 2 : if (oIter->second)
2500 : {
2501 : //CPLDebug("PDF", "Turn '%s' on", papszLayers[i]);
2502 2 : oIter->second->setState(OptionalContentGroup::On);
2503 : }
2504 :
2505 : // Turn child layers on, unless there's one of them explicitely listed
2506 : // in the list.
2507 2 : size_t nLen = strlen(papszLayers[i]);
2508 2 : int bFoundChildLayer = FALSE;
2509 2 : oIter = oLayerOCGMap.begin();
2510 12 : for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++)
2511 : {
2512 15 : if (oIter->first.size() > nLen &&
2513 5 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
2514 : oIter->first[nLen] == '.')
2515 : {
2516 4 : for(int j=0;papszLayers[j] != NULL;j++)
2517 : {
2518 2 : if (strcmp(papszLayers[j], oIter->first.c_str()) == 0)
2519 0 : bFoundChildLayer = TRUE;
2520 : }
2521 : }
2522 : }
2523 :
2524 2 : if( !bFoundChildLayer )
2525 : {
2526 2 : oIter = oLayerOCGMap.begin();
2527 12 : for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++)
2528 : {
2529 15 : if (oIter->first.size() > nLen &&
2530 5 : strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 &&
2531 : oIter->first[nLen] == '.')
2532 : {
2533 2 : if (oIter->second)
2534 : {
2535 : //CPLDebug("PDF", "Turn '%s' on too", oIter->first.c_str());
2536 2 : oIter->second->setState(OptionalContentGroup::On);
2537 : }
2538 : }
2539 : }
2540 : }
2541 :
2542 : // Turn parent layers on too
2543 : char* pszLastDot;
2544 5 : while( (pszLastDot = strrchr(papszLayers[i], '.')) != NULL)
2545 : {
2546 1 : *pszLastDot = '\0';
2547 1 : oIter = oLayerOCGMap.find(papszLayers[i]);
2548 1 : if (oIter != oLayerOCGMap.end())
2549 : {
2550 1 : if (oIter->second)
2551 : {
2552 : //CPLDebug("PDF", "Turn '%s' on too", papszLayers[i]);
2553 1 : oIter->second->setState(OptionalContentGroup::On);
2554 : }
2555 : }
2556 : }
2557 : }
2558 : else
2559 : {
2560 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
2561 0 : papszLayers[i]);
2562 : }
2563 : }
2564 2 : CSLDestroy(papszLayers);
2565 :
2566 2 : bUseOCG = TRUE;
2567 : }
2568 :
2569 : // Which layers to turn OFF ?
2570 23 : const char* pszLayersOFF = CPLGetConfigOption("GDAL_PDF_LAYERS_OFF", NULL);
2571 23 : if (pszLayersOFF)
2572 : {
2573 1 : char** papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0);
2574 2 : for(int i=0;papszLayersOFF[i] != NULL;i++)
2575 : {
2576 : std::map<CPLString, OptionalContentGroup*>::iterator oIter =
2577 1 : oLayerOCGMap.find(papszLayersOFF[i]);
2578 1 : if (oIter != oLayerOCGMap.end())
2579 : {
2580 1 : if (oIter->second)
2581 : {
2582 : //CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]);
2583 1 : oIter->second->setState(OptionalContentGroup::Off);
2584 : }
2585 :
2586 : // Turn child layers off too
2587 1 : size_t nLen = strlen(papszLayersOFF[i]);
2588 1 : oIter = oLayerOCGMap.begin();
2589 6 : for( ; oIter != oLayerOCGMap.end(); oIter ++)
2590 : {
2591 8 : if (oIter->first.size() > nLen &&
2592 3 : strncmp(oIter->first.c_str(), papszLayersOFF[i], nLen) == 0 &&
2593 : oIter->first[nLen] == '.')
2594 : {
2595 1 : if (oIter->second)
2596 : {
2597 : //CPLDebug("PDF", "Turn '%s' off too", oIter->first.c_str());
2598 1 : oIter->second->setState(OptionalContentGroup::Off);
2599 : }
2600 : }
2601 : }
2602 : }
2603 : else
2604 : {
2605 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'",
2606 0 : papszLayersOFF[i]);
2607 : }
2608 : }
2609 1 : CSLDestroy(papszLayersOFF);
2610 :
2611 1 : bUseOCG = TRUE;
2612 : }
2613 : }
2614 :
2615 : #endif
2616 :
2617 : /************************************************************************/
2618 : /* FindLayerOCG() */
2619 : /************************************************************************/
2620 :
2621 169 : CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary* poPageDict,
2622 : const char* pszLayerName)
2623 : {
2624 : GDALPDFObject* poProperties =
2625 169 : poPageDict->LookupObject("Resources.Properties");
2626 184 : if (poProperties != NULL &&
2627 15 : poProperties->GetType() == PDFObjectType_Dictionary)
2628 : {
2629 : std::map<CPLString, GDALPDFObject*>& oMap =
2630 15 : poProperties->GetDictionary()->GetValues();
2631 15 : std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
2632 15 : std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
2633 :
2634 43 : for(; oIter != oEnd; ++oIter)
2635 : {
2636 28 : GDALPDFObject* poObj = oIter->second;
2637 28 : if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary )
2638 : {
2639 28 : GDALPDFObject* poType = poObj->GetDictionary()->Get("Type");
2640 28 : GDALPDFObject* poName = poObj->GetDictionary()->Get("Name");
2641 112 : if( poType != NULL &&
2642 28 : poType->GetType() == PDFObjectType_Name &&
2643 28 : poType->GetName() == "OCG" &&
2644 : poName != NULL &&
2645 28 : poName->GetType() == PDFObjectType_String )
2646 : {
2647 28 : if( strcmp(poName->GetString().c_str(), pszLayerName) == 0)
2648 0 : return oIter->first;
2649 : }
2650 : }
2651 : }
2652 : }
2653 169 : return "";
2654 : }
2655 :
2656 : /************************************************************************/
2657 : /* FindLayersGeneric() */
2658 : /************************************************************************/
2659 :
2660 33 : void PDFDataset::FindLayersGeneric(GDALPDFDictionary* poPageDict)
2661 : {
2662 : GDALPDFObject* poProperties =
2663 33 : poPageDict->LookupObject("Resources.Properties");
2664 34 : if (poProperties != NULL &&
2665 1 : poProperties->GetType() == PDFObjectType_Dictionary)
2666 : {
2667 : std::map<CPLString, GDALPDFObject*>& oMap =
2668 1 : poProperties->GetDictionary()->GetValues();
2669 1 : std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
2670 1 : std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
2671 :
2672 3 : for(; oIter != oEnd; ++oIter)
2673 : {
2674 2 : GDALPDFObject* poObj = oIter->second;
2675 2 : if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary )
2676 : {
2677 2 : GDALPDFObject* poType = poObj->GetDictionary()->Get("Type");
2678 2 : GDALPDFObject* poName = poObj->GetDictionary()->Get("Name");
2679 8 : if( poType != NULL &&
2680 2 : poType->GetType() == PDFObjectType_Name &&
2681 2 : poType->GetName() == "OCG" &&
2682 : poName != NULL &&
2683 2 : poName->GetType() == PDFObjectType_String )
2684 : {
2685 : osLayerWithRefList.AddString(
2686 : CPLSPrintf("%s %d %d",
2687 2 : PDFSanitizeLayerName(poName->GetString()).c_str(),
2688 2 : poObj->GetRefNum(),
2689 6 : poObj->GetRefGen()));
2690 : }
2691 : }
2692 : }
2693 : }
2694 33 : }
2695 :
2696 : /************************************************************************/
2697 : /* Open() */
2698 : /************************************************************************/
2699 :
2700 1526 : GDALDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo )
2701 :
2702 : {
2703 1526 : if (!Identify(poOpenInfo))
2704 1344 : return NULL;
2705 :
2706 182 : const char* pszUserPwd = CPLGetConfigOption("PDF_USER_PWD", NULL);
2707 :
2708 182 : int bOpenSubdataset = strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0;
2709 182 : int bOpenSubdatasetImage = strncmp(poOpenInfo->pszFilename, "PDF_IMAGE:", 10) == 0;
2710 182 : int iPage = -1;
2711 182 : int nImageNum = -1;
2712 182 : const char* pszFilename = poOpenInfo->pszFilename;
2713 : char szPassword[81];
2714 :
2715 182 : if (bOpenSubdataset)
2716 : {
2717 1 : iPage = atoi(pszFilename + 4);
2718 1 : if (iPage <= 0)
2719 0 : return NULL;
2720 1 : pszFilename = strchr(pszFilename + 4, ':');
2721 1 : if (pszFilename == NULL)
2722 0 : return NULL;
2723 1 : pszFilename ++;
2724 : }
2725 181 : else if (bOpenSubdatasetImage)
2726 : {
2727 2 : iPage = atoi(pszFilename + 10);
2728 2 : if (iPage <= 0)
2729 0 : return NULL;
2730 2 : const char* pszNext = strchr(pszFilename + 10, ':');
2731 2 : if (pszNext == NULL)
2732 0 : return NULL;
2733 2 : nImageNum = atoi(pszNext + 1);
2734 2 : if (nImageNum <= 0)
2735 0 : return NULL;
2736 2 : pszFilename = strchr(pszNext + 1, ':');
2737 2 : if (pszFilename == NULL)
2738 0 : return NULL;
2739 2 : pszFilename ++;
2740 : }
2741 : else
2742 179 : iPage = 1;
2743 :
2744 : int bUsePoppler;
2745 : #if defined(HAVE_POPPLER) && !defined(HAVE_PODOFO)
2746 : bUsePoppler = TRUE;
2747 : #elif !defined(HAVE_POPPLER) && defined(HAVE_PODOFO)
2748 : bUsePoppler = FALSE;
2749 : #elif defined(HAVE_POPPLER) && defined(HAVE_PODOFO)
2750 182 : const char* pszPDFLib = CPLGetConfigOption("GDAL_PDF_LIB", "POPPLER");
2751 182 : if (EQUAL(pszPDFLib, "POPPLER"))
2752 149 : bUsePoppler = TRUE;
2753 33 : else if (EQUAL(pszPDFLib, "PODOFO"))
2754 33 : bUsePoppler = FALSE;
2755 : else
2756 : {
2757 0 : CPLDebug("PDF", "Invalid value for GDAL_PDF_LIB config option");
2758 0 : bUsePoppler = TRUE;
2759 : }
2760 : #else
2761 : return NULL;
2762 : #endif
2763 :
2764 182 : GDALPDFObject* poPageObj = NULL;
2765 : #ifdef HAVE_POPPLER
2766 182 : PDFDoc* poDocPoppler = NULL;
2767 182 : ObjectAutoFree oObj;
2768 182 : Page* poPagePoppler = NULL;
2769 182 : Catalog* poCatalogPoppler = NULL;
2770 : #endif
2771 : #ifdef HAVE_PODOFO
2772 182 : PoDoFo::PdfMemDocument* poDocPodofo = NULL;
2773 182 : PoDoFo::PdfPage* poPagePodofo = NULL;
2774 : #endif
2775 182 : int nPages = 0;
2776 :
2777 : #ifdef HAVE_POPPLER
2778 182 : if(bUsePoppler)
2779 : {
2780 149 : GooString* poUserPwd = NULL;
2781 :
2782 : /* Set custom error handler for poppler errors */
2783 : #ifdef POPPLER_0_20_OR_LATER
2784 149 : setErrorCallback(PDFDatasetErrorFunction, NULL);
2785 : #else
2786 : setErrorFunction(PDFDatasetErrorFunction);
2787 : #endif
2788 :
2789 : {
2790 149 : CPLMutexHolderD(&hGlobalParamsMutex);
2791 : /* poppler global variable */
2792 149 : if (globalParams == NULL)
2793 1 : globalParams = new GlobalParams();
2794 :
2795 : globalParams->setPrintCommands(CSLTestBoolean(
2796 149 : CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE")));
2797 : }
2798 :
2799 0 : while(TRUE)
2800 : {
2801 149 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
2802 149 : if (fp == NULL)
2803 0 : return NULL;
2804 :
2805 149 : fp = (VSILFILE*)VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp);
2806 :
2807 149 : if (pszUserPwd)
2808 0 : poUserPwd = new GooString(pszUserPwd);
2809 :
2810 149 : oObj.initNull();
2811 149 : poDocPoppler = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, &oObj), NULL, poUserPwd);
2812 149 : delete poUserPwd;
2813 :
2814 149 : if ( !poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0 )
2815 : {
2816 0 : if (poDocPoppler->getErrorCode() == errEncrypted)
2817 : {
2818 0 : if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
2819 : {
2820 0 : printf( "Enter password (will be echo'ed in the console): " );
2821 0 : fgets( szPassword, sizeof(szPassword), stdin );
2822 0 : szPassword[sizeof(szPassword)-1] = 0;
2823 0 : char* sz10 = strchr(szPassword, '\n');
2824 0 : if (sz10)
2825 0 : *sz10 = 0;
2826 0 : pszUserPwd = szPassword;
2827 0 : PDFFreeDoc(poDocPoppler);
2828 0 : continue;
2829 : }
2830 0 : else if (pszUserPwd == NULL)
2831 : {
2832 : CPLError(CE_Failure, CPLE_AppDefined,
2833 : "A password is needed. You can specify it through the PDF_USER_PWD "
2834 0 : "configuration option (that can be set to ASK_INTERACTIVE)");
2835 : }
2836 : else
2837 : {
2838 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
2839 : }
2840 : }
2841 : else
2842 : {
2843 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
2844 : }
2845 :
2846 0 : PDFFreeDoc(poDocPoppler);
2847 :
2848 0 : return NULL;
2849 : }
2850 : else
2851 : break;
2852 : }
2853 :
2854 149 : poCatalogPoppler = poDocPoppler->getCatalog();
2855 149 : if ( poCatalogPoppler == NULL || !poCatalogPoppler->isOk() )
2856 : {
2857 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid catalog");
2858 0 : PDFFreeDoc(poDocPoppler);
2859 0 : return NULL;
2860 : }
2861 :
2862 : #ifdef dump_catalog
2863 : {
2864 : ObjectAutoFree oCatalog;
2865 : poDocPoppler->getXRef()->getCatalog(&oCatalog);
2866 : GDALPDFObjectPoppler oCatalogGDAL(&oCatalog, FALSE);
2867 : GDALPDFDumper oDumper(stderr);
2868 : oDumper.Dump(&oCatalogGDAL);
2869 : }
2870 : #endif
2871 :
2872 149 : nPages = poDocPoppler->getNumPages();
2873 149 : if (iPage < 1 || iPage > nPages)
2874 : {
2875 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
2876 0 : iPage, nPages);
2877 0 : PDFFreeDoc(poDocPoppler);
2878 0 : return NULL;
2879 : }
2880 :
2881 149 : poPagePoppler = poCatalogPoppler->getPage(iPage);
2882 149 : if ( poPagePoppler == NULL || !poPagePoppler->isOk() )
2883 : {
2884 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
2885 0 : PDFFreeDoc(poDocPoppler);
2886 0 : return NULL;
2887 : }
2888 :
2889 : /* Here's the dirty part: this is a private member */
2890 : /* so we had to #define private public to get it ! */
2891 149 : Object& oPageObj = poPagePoppler->pageObj;
2892 149 : if ( !oPageObj.isDict() )
2893 : {
2894 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : !oPageObj.isDict()");
2895 0 : PDFFreeDoc(poDocPoppler);
2896 0 : return NULL;
2897 : }
2898 :
2899 149 : poPageObj = new GDALPDFObjectPoppler(&oPageObj, FALSE);
2900 298 : Ref* poPageRef = poCatalogPoppler->getPageRef(iPage);
2901 149 : if (poPageRef != NULL)
2902 : {
2903 149 : ((GDALPDFObjectPoppler*)poPageObj)->SetRefNumAndGen(poPageRef->num, poPageRef->gen);
2904 : }
2905 : }
2906 : #endif
2907 :
2908 : #ifdef HAVE_PODOFO
2909 182 : if (!bUsePoppler)
2910 : {
2911 33 : PoDoFo::PdfError::EnableDebug( false );
2912 33 : PoDoFo::PdfError::EnableLogging( false );
2913 :
2914 33 : poDocPodofo = new PoDoFo::PdfMemDocument();
2915 : try
2916 : {
2917 33 : poDocPodofo->Load(pszFilename);
2918 : }
2919 0 : catch(PoDoFo::PdfError& oError)
2920 : {
2921 0 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
2922 : {
2923 0 : if (pszUserPwd)
2924 : {
2925 0 : if (EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
2926 : {
2927 0 : printf( "Enter password (will be echo'ed in the console): " );
2928 0 : fgets( szPassword, sizeof(szPassword), stdin );
2929 0 : szPassword[sizeof(szPassword)-1] = 0;
2930 0 : char* sz10 = strchr(szPassword, '\n');
2931 0 : if (sz10)
2932 0 : *sz10 = 0;
2933 0 : pszUserPwd = szPassword;
2934 : }
2935 :
2936 : try
2937 : {
2938 0 : poDocPodofo->SetPassword(pszUserPwd);
2939 : }
2940 0 : catch(PoDoFo::PdfError& oError)
2941 : {
2942 0 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
2943 : {
2944 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
2945 : }
2946 : else
2947 : {
2948 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
2949 : }
2950 0 : delete poDocPodofo;
2951 0 : return NULL;
2952 : }
2953 0 : catch(...)
2954 : {
2955 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
2956 0 : delete poDocPodofo;
2957 0 : return NULL;
2958 : }
2959 : }
2960 : else
2961 : {
2962 : CPLError(CE_Failure, CPLE_AppDefined,
2963 : "A password is needed. You can specify it through the PDF_USER_PWD "
2964 0 : "configuration option (that can be set to ASK_INTERACTIVE)");
2965 0 : delete poDocPodofo;
2966 0 : return NULL;
2967 : }
2968 : }
2969 : else
2970 : {
2971 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
2972 0 : delete poDocPodofo;
2973 0 : return NULL;
2974 : }
2975 : }
2976 0 : catch(...)
2977 : {
2978 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
2979 0 : delete poDocPodofo;
2980 0 : return NULL;
2981 : }
2982 :
2983 33 : nPages = poDocPodofo->GetPageCount();
2984 33 : if (iPage < 1 || iPage > nPages)
2985 : {
2986 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
2987 0 : iPage, nPages);
2988 0 : delete poDocPodofo;
2989 0 : return NULL;
2990 : }
2991 :
2992 : try
2993 : {
2994 33 : poPagePodofo = poDocPodofo->GetPage(iPage - 1);
2995 : }
2996 0 : catch(PoDoFo::PdfError& oError)
2997 : {
2998 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
2999 0 : delete poDocPodofo;
3000 0 : return NULL;
3001 : }
3002 0 : catch(...)
3003 : {
3004 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
3005 0 : delete poDocPodofo;
3006 0 : return NULL;
3007 : }
3008 :
3009 33 : if ( poPagePodofo == NULL )
3010 : {
3011 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
3012 0 : delete poDocPodofo;
3013 0 : return NULL;
3014 : }
3015 :
3016 33 : PoDoFo::PdfObject* pObj = poPagePodofo->GetObject();
3017 33 : poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects());
3018 : }
3019 : #endif
3020 :
3021 182 : GDALPDFDictionary* poPageDict = poPageObj->GetDictionary();
3022 182 : if ( poPageDict == NULL )
3023 : {
3024 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : poPageDict == NULL");
3025 : #ifdef HAVE_POPPLER
3026 0 : PDFFreeDoc(poDocPoppler);
3027 : #endif
3028 : #ifdef HAVE_PODOFO
3029 0 : delete poDocPodofo;
3030 : #endif
3031 0 : return NULL;
3032 : }
3033 :
3034 182 : const char* pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", NULL);
3035 182 : if (pszDumpObject != NULL)
3036 : {
3037 : FILE* f;
3038 0 : if (strcmp(pszDumpObject, "stderr") == 0)
3039 0 : f = stderr;
3040 0 : else if (EQUAL(pszDumpObject, "YES"))
3041 0 : f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), "wt");
3042 : else
3043 0 : f = fopen(pszDumpObject, "wt");
3044 0 : if (f == NULL)
3045 0 : f = stderr;
3046 :
3047 0 : GDALPDFDumper oDumper(f);
3048 0 : oDumper.Dump(poPageObj);
3049 0 : if (f != stderr)
3050 0 : fclose(f);
3051 : }
3052 :
3053 182 : PDFDataset* poDS = new PDFDataset();
3054 182 : poDS->bUsePoppler = bUsePoppler;
3055 364 : poDS->osFilename = pszFilename;
3056 182 : poDS->eAccess = poOpenInfo->eAccess;
3057 :
3058 182 : if ( nPages > 1 && !bOpenSubdataset )
3059 : {
3060 : int i;
3061 1 : CPLStringList aosList;
3062 5 : for(i=0;i<nPages;i++)
3063 : {
3064 : char szKey[32];
3065 4 : sprintf( szKey, "SUBDATASET_%d_NAME", i+1 );
3066 4 : aosList.AddNameValue(szKey, CPLSPrintf("PDF:%d:%s", i+1, poOpenInfo->pszFilename));
3067 4 : sprintf( szKey, "SUBDATASET_%d_DESC", i+1 );
3068 4 : aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i+1, poOpenInfo->pszFilename));
3069 : }
3070 1 : poDS->SetMetadata( aosList.List(), "SUBDATASETS" );
3071 : }
3072 :
3073 : #ifdef HAVE_POPPLER
3074 182 : poDS->poDocPoppler = poDocPoppler;
3075 : #endif
3076 : #ifdef HAVE_PODOFO
3077 182 : poDS->poDocPodofo = poDocPodofo;
3078 : #endif
3079 182 : poDS->poPageObj = poPageObj;
3080 182 : poDS->osUserPwd = pszUserPwd ? pszUserPwd : "";
3081 182 : poDS->iPage = iPage;
3082 :
3083 182 : int nBandsGuessed = 0;
3084 182 : if (nImageNum < 0)
3085 : {
3086 180 : poDS->GuessDPI(poPageDict, &nBandsGuessed);
3087 180 : if( nBandsGuessed < 4 )
3088 170 : nBandsGuessed = 0;
3089 : }
3090 : else
3091 : {
3092 2 : const char* pszDPI = CPLGetConfigOption("GDAL_PDF_DPI", NULL);
3093 2 : if (pszDPI != NULL)
3094 : {
3095 0 : poDS->dfDPI = atof(pszDPI);
3096 : }
3097 : }
3098 :
3099 182 : double dfX1 = 0.0, dfY1 = 0.0, dfX2 = 0.0, dfY2 = 0.0;
3100 :
3101 : #ifdef HAVE_POPPLER
3102 182 : if (bUsePoppler)
3103 : {
3104 149 : PDFRectangle* psMediaBox = poPagePoppler->getMediaBox();
3105 149 : dfX1 = psMediaBox->x1;
3106 149 : dfY1 = psMediaBox->y1;
3107 149 : dfX2 = psMediaBox->x2;
3108 149 : dfY2 = psMediaBox->y2;
3109 : }
3110 : #endif
3111 :
3112 : #ifdef HAVE_PODOFO
3113 182 : if (!bUsePoppler)
3114 : {
3115 33 : PoDoFo::PdfRect oMediaBox = poPagePodofo->GetMediaBox();
3116 33 : dfX1 = oMediaBox.GetLeft();
3117 33 : dfY1 = oMediaBox.GetBottom();
3118 33 : dfX2 = dfX1 + oMediaBox.GetWidth();
3119 33 : dfY2 = dfY1 + oMediaBox.GetHeight();
3120 : }
3121 : #endif
3122 :
3123 182 : double dfUserUnit = poDS->dfDPI / 72.0;
3124 182 : poDS->nRasterXSize = (int) floor((dfX2 - dfX1) * dfUserUnit+0.5);
3125 182 : poDS->nRasterYSize = (int) floor((dfY2 - dfY1) * dfUserUnit+0.5);
3126 :
3127 182 : if( !GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) )
3128 : {
3129 0 : delete poDS;
3130 0 : return NULL;
3131 : }
3132 :
3133 182 : double dfRotation = 0;
3134 : #ifdef HAVE_POPPLER
3135 182 : if (bUsePoppler)
3136 149 : dfRotation = poDocPoppler->getPageRotate(iPage);
3137 : #endif
3138 :
3139 : #ifdef HAVE_PODOFO
3140 182 : if (!bUsePoppler)
3141 33 : dfRotation = poPagePodofo->GetRotation();
3142 : #endif
3143 182 : if ( dfRotation == 90 ||
3144 : dfRotation == 270 )
3145 : {
3146 : /* FIXME: the non poppler case should be implemented. This needs to rotate */
3147 : /* the output of pdftoppm */
3148 : #ifdef HAVE_POPPLER
3149 0 : if (bUsePoppler)
3150 : {
3151 : /* Wondering how it would work with a georeferenced image */
3152 : /* Has only been tested with ungeoreferenced image */
3153 0 : int nTmp = poDS->nRasterXSize;
3154 0 : poDS->nRasterXSize = poDS->nRasterYSize;
3155 0 : poDS->nRasterYSize = nTmp;
3156 : }
3157 : #endif
3158 : }
3159 :
3160 : /* Check if the PDF is only made of regularly tiled images */
3161 : /* (like some USGS GeoPDF production) */
3162 182 : if( dfRotation == 0.0 && poDS->asTiles.size() &&
3163 : EQUAL(CPLGetConfigOption("GDAL_PDF_LAYERS", "ALL"), "ALL") )
3164 : {
3165 164 : poDS->CheckTiledRaster();
3166 164 : if (poDS->aiTiles.size() )
3167 12 : poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
3168 : }
3169 :
3170 182 : GDALPDFObject* poLGIDict = NULL;
3171 182 : GDALPDFObject* poVP = NULL;
3172 182 : int bIsOGCBP = FALSE;
3173 182 : if ( (poLGIDict = poPageDict->Get("LGIDict")) != NULL && nImageNum < 0 )
3174 : {
3175 : /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */
3176 22 : CPLDebug("PDF", "OGC Encoding Best Practice style detected");
3177 22 : if (poDS->ParseLGIDictObject(poLGIDict))
3178 : {
3179 22 : if (poDS->bHasCTM)
3180 : {
3181 19 : poDS->adfGeoTransform[0] = poDS->adfCTM[4] + poDS->adfCTM[0] * dfX1 + poDS->adfCTM[2] * dfY2;
3182 19 : poDS->adfGeoTransform[1] = poDS->adfCTM[0] / dfUserUnit;
3183 19 : poDS->adfGeoTransform[2] = poDS->adfCTM[1] / dfUserUnit;
3184 19 : poDS->adfGeoTransform[3] = poDS->adfCTM[5] + poDS->adfCTM[1] * dfX1 + poDS->adfCTM[3] * dfY2;
3185 19 : poDS->adfGeoTransform[4] = - poDS->adfCTM[2] / dfUserUnit;
3186 19 : poDS->adfGeoTransform[5] = - poDS->adfCTM[3] / dfUserUnit;
3187 19 : poDS->bGeoTransformValid = TRUE;
3188 : }
3189 :
3190 22 : bIsOGCBP = TRUE;
3191 :
3192 : int i;
3193 37 : for(i=0;i<poDS->nGCPCount;i++)
3194 : {
3195 15 : poDS->pasGCPList[i].dfGCPPixel = poDS->pasGCPList[i].dfGCPPixel * dfUserUnit;
3196 15 : poDS->pasGCPList[i].dfGCPLine = poDS->nRasterYSize - poDS->pasGCPList[i].dfGCPLine * dfUserUnit;
3197 : }
3198 : }
3199 : }
3200 160 : else if ( (poVP = poPageDict->Get("VP")) != NULL && nImageNum < 0 )
3201 : {
3202 : /* Cf adobe_supplement_iso32000.pdf */
3203 132 : CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?");
3204 132 : if (dfX1 != 0 || dfY1 != 0)
3205 : {
3206 0 : CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case...");
3207 : }
3208 132 : poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1);
3209 : }
3210 : else
3211 : {
3212 28 : GDALPDFObject* poXObject = poPageDict->LookupObject("Resources.XObject");
3213 :
3214 56 : if (poXObject != NULL &&
3215 28 : poXObject->GetType() == PDFObjectType_Dictionary)
3216 : {
3217 28 : GDALPDFDictionary* poXObjectDict = poXObject->GetDictionary();
3218 28 : std::map<CPLString, GDALPDFObject*>& oMap = poXObjectDict->GetValues();
3219 28 : std::map<CPLString, GDALPDFObject*>::iterator oMapIter = oMap.begin();
3220 28 : int nSubDataset = 0;
3221 198 : while(oMapIter != oMap.end())
3222 : {
3223 144 : GDALPDFObject* poObj = oMapIter->second;
3224 144 : if (poObj->GetType() == PDFObjectType_Dictionary)
3225 : {
3226 144 : GDALPDFDictionary* poDict = poObj->GetDictionary();
3227 144 : GDALPDFObject* poSubtype = NULL;
3228 144 : GDALPDFObject* poMeasure = NULL;
3229 144 : GDALPDFObject* poWidth = NULL;
3230 144 : GDALPDFObject* poHeight = NULL;
3231 144 : int nW = 0;
3232 144 : int nH = 0;
3233 618 : if ((poSubtype = poDict->Get("Subtype")) != NULL &&
3234 144 : poSubtype->GetType() == PDFObjectType_Name &&
3235 144 : poSubtype->GetName() == "Image" &&
3236 144 : (poMeasure = poDict->Get("Measure")) != NULL &&
3237 6 : poMeasure->GetType() == PDFObjectType_Dictionary &&
3238 6 : (poWidth = poDict->Get("Width")) != NULL &&
3239 6 : poWidth->GetType() == PDFObjectType_Int &&
3240 6 : (nW = poWidth->GetInt()) > 0 &&
3241 6 : (poHeight = poDict->Get("Height")) != NULL &&
3242 6 : poHeight->GetType() == PDFObjectType_Int &&
3243 6 : (nH = poHeight->GetInt()) > 0 )
3244 : {
3245 6 : if (nImageNum < 0)
3246 : CPLDebug("PDF", "Measure found on Image object (%d)",
3247 4 : poObj->GetRefNum());
3248 :
3249 6 : GDALPDFObject* poColorSpace = poDict->Get("ColorSpace");
3250 6 : GDALPDFObject* poBitsPerComponent = poDict->Get("BitsPerComponent");
3251 39 : if (poObj->GetRefNum() != 0 &&
3252 6 : poObj->GetRefGen() == 0 &&
3253 : poColorSpace != NULL &&
3254 6 : poColorSpace->GetType() == PDFObjectType_Name &&
3255 6 : (poColorSpace->GetName() == "DeviceGray" ||
3256 3 : poColorSpace->GetName() == "DeviceRGB") &&
3257 : (poBitsPerComponent == NULL ||
3258 6 : (poBitsPerComponent->GetType() == PDFObjectType_Int &&
3259 6 : poBitsPerComponent->GetInt() == 8)))
3260 : {
3261 6 : if (nImageNum < 0)
3262 : {
3263 4 : nSubDataset ++;
3264 : poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME",
3265 : nSubDataset),
3266 : CPLSPrintf("PDF_IMAGE:%d:%d:%s",
3267 4 : iPage, poObj->GetRefNum(), pszFilename),
3268 8 : "SUBDATASETS");
3269 : poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC",
3270 : nSubDataset),
3271 : CPLSPrintf("Georeferenced image of size %dx%d of page %d of %s",
3272 : nW, nH, iPage, pszFilename),
3273 4 : "SUBDATASETS");
3274 : }
3275 2 : else if (poObj->GetRefNum() == nImageNum)
3276 : {
3277 2 : poDS->nRasterXSize = nW;
3278 2 : poDS->nRasterYSize = nH;
3279 2 : poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW, 0);
3280 2 : poDS->poImageObj = poObj;
3281 2 : if (poColorSpace->GetName() == "DeviceGray")
3282 1 : nBandsGuessed = 1;
3283 2 : break;
3284 : }
3285 : }
3286 : }
3287 : }
3288 142 : ++ oMapIter;
3289 : }
3290 : }
3291 :
3292 28 : if (nImageNum >= 0 && poDS->poImageObj == NULL)
3293 : {
3294 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d", nImageNum);
3295 0 : delete poDS;
3296 0 : return NULL;
3297 : }
3298 :
3299 : /* Not a geospatial PDF doc */
3300 : }
3301 :
3302 : /* If pixel size or top left coordinates are very close to an int, round them to the int */
3303 182 : double dfEps = ( fabs(poDS->adfGeoTransform[0]) > 1e5 &&
3304 182 : fabs(poDS->adfGeoTransform[3]) > 1e5 ) ? 1e-5 : 1e-8;
3305 182 : poDS->adfGeoTransform[0] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[0], dfEps);
3306 182 : poDS->adfGeoTransform[1] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[1]);
3307 182 : poDS->adfGeoTransform[3] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[3], dfEps);
3308 182 : poDS->adfGeoTransform[5] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[5]);
3309 :
3310 182 : if (poDS->poNeatLine)
3311 : {
3312 156 : char* pszNeatLineWkt = NULL;
3313 156 : OGRLinearRing* poRing = poDS->poNeatLine->getExteriorRing();
3314 : /* Adobe style is already in target SRS units */
3315 156 : if (bIsOGCBP)
3316 : {
3317 22 : int nPoints = poRing->getNumPoints();
3318 : int i;
3319 :
3320 116 : for(i=0;i<nPoints;i++)
3321 : {
3322 94 : double x = poRing->getX(i) * dfUserUnit;
3323 94 : double y = poDS->nRasterYSize - poRing->getY(i) * dfUserUnit;
3324 94 : double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] +
3325 94 : y * poDS->adfGeoTransform[2];
3326 94 : double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] +
3327 94 : y * poDS->adfGeoTransform[5];
3328 94 : poRing->setPoint(i, X, Y);
3329 : }
3330 : }
3331 156 : poRing->closeRings();
3332 :
3333 156 : poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
3334 156 : if (nImageNum < 0)
3335 154 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
3336 156 : CPLFree(pszNeatLineWkt);
3337 : }
3338 :
3339 :
3340 : #ifdef HAVE_POPPLER
3341 182 : if (bUsePoppler)
3342 : {
3343 149 : GooString* poMetadata = poCatalogPoppler->readMetadata();
3344 149 : if (poMetadata)
3345 : {
3346 10 : char* pszContent = poMetadata->getCString();
3347 10 : if (pszContent != NULL &&
3348 : strncmp(pszContent, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
3349 : {
3350 : char *apszMDList[2];
3351 10 : apszMDList[0] = pszContent;
3352 10 : apszMDList[1] = NULL;
3353 10 : poDS->SetMetadata(apszMDList, "xml:XMP");
3354 : }
3355 10 : delete poMetadata;
3356 : }
3357 :
3358 : /* Read Info object */
3359 : /* The test is necessary since with some corrupted PDFs poDocPoppler->getDocInfo() */
3360 : /* might abort() */
3361 149 : if( poDocPoppler->getXRef()->isOk() )
3362 : {
3363 149 : Object oInfo;
3364 149 : poDocPoppler->getDocInfo(&oInfo);
3365 149 : GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE);
3366 149 : poDS->ParseInfo(&oInfoObjPoppler);
3367 149 : oInfo.free();
3368 : }
3369 :
3370 : /* Find layers */
3371 149 : poDS->FindLayers();
3372 :
3373 : /* Turn user specified layers on or off */
3374 149 : poDS->TurnLayersOnOff();
3375 : }
3376 : #endif
3377 :
3378 : #ifdef HAVE_PODOFO
3379 182 : if (!bUsePoppler)
3380 : {
3381 33 : PoDoFo::TIVecObjects it = poDocPodofo->GetObjects().begin();
3382 752 : for( ; it != poDocPodofo->GetObjects().end(); ++it )
3383 : {
3384 719 : GDALPDFObjectPodofo oObjPodofo((*it), poDocPodofo->GetObjects());
3385 719 : poDS->FindXMP(&oObjPodofo);
3386 : }
3387 :
3388 : /* Find layers */
3389 33 : poDS->FindLayersGeneric(poPageDict);
3390 :
3391 : /* Read Info object */
3392 33 : PoDoFo::PdfInfo* poInfo = poDocPodofo->GetInfo();
3393 33 : if (poInfo != NULL)
3394 : {
3395 33 : GDALPDFObjectPodofo oInfoObjPodofo(poInfo->GetObject(), poDocPodofo->GetObjects());
3396 33 : poDS->ParseInfo(&oInfoObjPodofo);
3397 : }
3398 : }
3399 : #endif
3400 :
3401 182 : int nBands = 3;
3402 182 : if( nBandsGuessed )
3403 11 : nBands = nBandsGuessed;
3404 182 : const char* pszPDFBands = CPLGetConfigOption("GDAL_PDF_BANDS", NULL);
3405 182 : if( pszPDFBands )
3406 : {
3407 0 : nBands = atoi(pszPDFBands);
3408 0 : if (nBands != 3 && nBands != 4)
3409 : {
3410 : CPLError(CE_Warning, CPLE_NotSupported,
3411 0 : "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback");
3412 0 : nBands = 3;
3413 : }
3414 : }
3415 : #ifdef HAVE_PODOFO
3416 182 : if (!bUsePoppler && nBands == 4 && poDS->aiTiles.size() == 0)
3417 : {
3418 : CPLError(CE_Warning, CPLE_NotSupported,
3419 : "GDAL_PDF_BANDS=4 only supported when PDF driver is compiled against Poppler. "
3420 0 : "Using 3 as a fallback");
3421 0 : nBands = 3;
3422 : }
3423 : #endif
3424 :
3425 : int iBand;
3426 736 : for(iBand = 1; iBand <= nBands; iBand ++)
3427 : {
3428 554 : if (poDS->poImageObj != NULL)
3429 4 : poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand));
3430 : else
3431 550 : poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand));
3432 : }
3433 :
3434 : /* -------------------------------------------------------------------- */
3435 : /* Initialize any PAM information. */
3436 : /* -------------------------------------------------------------------- */
3437 182 : poDS->SetDescription( poOpenInfo->pszFilename );
3438 182 : poDS->TryLoadXML();
3439 :
3440 : /* -------------------------------------------------------------------- */
3441 : /* Support overviews. */
3442 : /* -------------------------------------------------------------------- */
3443 182 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
3444 :
3445 : /* Clear dirty flag */
3446 182 : poDS->bProjDirty = FALSE;
3447 182 : poDS->bNeatLineDirty = FALSE;
3448 182 : poDS->bInfoDirty = FALSE;
3449 182 : poDS->bXMPDirty = FALSE;
3450 :
3451 182 : return( poDS );
3452 : }
3453 :
3454 : /************************************************************************/
3455 : /* ParseLGIDictObject() */
3456 : /************************************************************************/
3457 :
3458 22 : int PDFDataset::ParseLGIDictObject(GDALPDFObject* poLGIDict)
3459 : {
3460 : int i;
3461 22 : int bOK = FALSE;
3462 22 : if (poLGIDict->GetType() == PDFObjectType_Array)
3463 : {
3464 2 : GDALPDFArray* poArray = poLGIDict->GetArray();
3465 2 : int nArrayLength = poArray->GetLength();
3466 2 : int iMax = -1;
3467 : GDALPDFObject* poArrayElt;
3468 4 : for (i=0; i<nArrayLength; i++)
3469 : {
3470 4 : if ( (poArrayElt = poArray->Get(i)) == NULL ||
3471 2 : poArrayElt->GetType() != PDFObjectType_Dictionary )
3472 : {
3473 : CPLError(CE_Failure, CPLE_AppDefined,
3474 0 : "LGIDict[%d] is not a dictionary", i);
3475 0 : return FALSE;
3476 : }
3477 :
3478 2 : int bIsBestCandidate = FALSE;
3479 2 : if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), &bIsBestCandidate))
3480 : {
3481 2 : if (bIsBestCandidate || iMax < 0)
3482 2 : iMax = i;
3483 : }
3484 : }
3485 :
3486 2 : if (iMax < 0)
3487 0 : return FALSE;
3488 :
3489 2 : poArrayElt = poArray->Get(iMax);
3490 2 : bOK = ParseLGIDictDictSecondPass(poArrayElt->GetDictionary());
3491 : }
3492 20 : else if (poLGIDict->GetType() == PDFObjectType_Dictionary)
3493 : {
3494 20 : bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) &&
3495 20 : ParseLGIDictDictSecondPass(poLGIDict->GetDictionary());
3496 : }
3497 : else
3498 : {
3499 : CPLError(CE_Failure, CPLE_AppDefined,
3500 0 : "LGIDict is of type %s", poLGIDict->GetTypeName());
3501 : }
3502 :
3503 22 : return bOK;
3504 : }
3505 :
3506 : /************************************************************************/
3507 : /* Get() */
3508 : /************************************************************************/
3509 :
3510 11091 : static double Get(GDALPDFObject* poObj, int nIndice)
3511 : {
3512 11091 : if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0)
3513 : {
3514 4854 : poObj = poObj->GetArray()->Get(nIndice);
3515 4854 : if (poObj == NULL)
3516 0 : return 0;
3517 4854 : return Get(poObj);
3518 : }
3519 6237 : else if (poObj->GetType() == PDFObjectType_Int)
3520 4931 : return poObj->GetInt();
3521 1306 : else if (poObj->GetType() == PDFObjectType_Real)
3522 1055 : return poObj->GetReal();
3523 251 : else if (poObj->GetType() == PDFObjectType_String)
3524 : {
3525 251 : const char* pszStr = poObj->GetString().c_str();
3526 251 : size_t nLen = strlen(pszStr);
3527 : /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" */
3528 251 : char chLast = pszStr[nLen-1];
3529 251 : if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
3530 : {
3531 0 : double dfDeg = atof(pszStr);
3532 0 : double dfMin = 0, dfSec = 0;
3533 0 : const char* pszNext = strchr(pszStr, ' ');
3534 0 : if (pszNext)
3535 0 : pszNext ++;
3536 0 : if (pszNext)
3537 0 : dfMin = atof(pszNext);
3538 0 : if (pszNext)
3539 0 : pszNext = strchr(pszNext, ' ');
3540 0 : if (pszNext)
3541 0 : pszNext ++;
3542 0 : if (pszNext)
3543 0 : dfSec = atof(pszNext);
3544 0 : double dfVal = dfDeg + dfMin / 60 + dfSec / 3600;
3545 0 : if (chLast == 'W' || chLast == 'S')
3546 0 : return -dfVal;
3547 : else
3548 0 : return dfVal;
3549 : }
3550 251 : return atof(pszStr);
3551 : }
3552 : else
3553 : {
3554 : CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s",
3555 0 : poObj->GetTypeName());
3556 0 : return 0;
3557 : }
3558 : }
3559 :
3560 : /************************************************************************/
3561 : /* Get() */
3562 : /************************************************************************/
3563 :
3564 14 : static double Get(GDALPDFDictionary* poDict, const char* pszName)
3565 : {
3566 : GDALPDFObject* poObj;
3567 14 : if ( (poObj = poDict->Get(pszName)) != NULL )
3568 14 : return Get(poObj);
3569 : CPLError(CE_Failure, CPLE_AppDefined,
3570 0 : "Cannot find parameter %s", pszName);
3571 0 : return 0;
3572 : }
3573 :
3574 : /************************************************************************/
3575 : /* ParseLGIDictDictFirstPass() */
3576 : /************************************************************************/
3577 :
3578 22 : int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict,
3579 : int* pbIsBestCandidate)
3580 : {
3581 : int i;
3582 :
3583 22 : if (pbIsBestCandidate)
3584 2 : *pbIsBestCandidate = FALSE;
3585 :
3586 22 : if (poLGIDict == NULL)
3587 0 : return FALSE;
3588 :
3589 : /* -------------------------------------------------------------------- */
3590 : /* Extract Type attribute */
3591 : /* -------------------------------------------------------------------- */
3592 : GDALPDFObject* poType;
3593 22 : if ((poType = poLGIDict->Get("Type")) == NULL)
3594 : {
3595 : CPLError(CE_Failure, CPLE_AppDefined,
3596 0 : "Cannot find Type of LGIDict object");
3597 0 : return FALSE;
3598 : }
3599 :
3600 22 : if ( poType->GetType() != PDFObjectType_Name )
3601 : {
3602 : CPLError(CE_Failure, CPLE_AppDefined,
3603 0 : "Invalid type for Type of LGIDict object");
3604 0 : return FALSE;
3605 : }
3606 :
3607 22 : if ( strcmp(poType->GetName().c_str(), "LGIDict") != 0 )
3608 : {
3609 : CPLError(CE_Failure, CPLE_AppDefined,
3610 : "Invalid value for Type of LGIDict object : %s",
3611 0 : poType->GetName().c_str());
3612 0 : return FALSE;
3613 : }
3614 :
3615 : /* -------------------------------------------------------------------- */
3616 : /* Extract Version attribute */
3617 : /* -------------------------------------------------------------------- */
3618 : GDALPDFObject* poVersion;
3619 22 : if ((poVersion = poLGIDict->Get("Version")) == NULL)
3620 : {
3621 : CPLError(CE_Failure, CPLE_AppDefined,
3622 0 : "Cannot find Version of LGIDict object");
3623 0 : return FALSE;
3624 : }
3625 :
3626 22 : if ( poVersion->GetType() == PDFObjectType_String )
3627 : {
3628 : /* OGC best practice is 2.1 */
3629 : CPLDebug("PDF", "LGIDict Version : %s",
3630 20 : poVersion->GetString().c_str());
3631 : }
3632 2 : else if (poVersion->GetType() == PDFObjectType_Int)
3633 : {
3634 : /* Old TerraGo is 2 */
3635 : CPLDebug("PDF", "LGIDict Version : %d",
3636 2 : poVersion->GetInt());
3637 : }
3638 :
3639 : /* USGS PDF maps have several LGIDict. Keep the one whose description */
3640 : /* is "Map Layers" by default */
3641 : const char* pszNeatlineToSelect =
3642 22 : CPLGetConfigOption("GDAL_PDF_NEATLINE", "Map Layers");
3643 :
3644 : /* -------------------------------------------------------------------- */
3645 : /* Extract Neatline attribute */
3646 : /* -------------------------------------------------------------------- */
3647 : GDALPDFObject* poNeatline;
3648 44 : if ((poNeatline = poLGIDict->Get("Neatline")) != NULL &&
3649 22 : poNeatline->GetType() == PDFObjectType_Array)
3650 : {
3651 22 : int nLength = poNeatline->GetArray()->GetLength();
3652 22 : if ( (nLength % 2) != 0 || nLength < 4 )
3653 : {
3654 : CPLError(CE_Failure, CPLE_AppDefined,
3655 0 : "Invalid length for Neatline");
3656 0 : return FALSE;
3657 : }
3658 :
3659 : GDALPDFObject* poDescription;
3660 22 : int bIsAskedNeatline = FALSE;
3661 44 : if ( (poDescription = poLGIDict->Get("Description")) != NULL &&
3662 22 : poDescription->GetType() == PDFObjectType_String )
3663 : {
3664 22 : CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str());
3665 :
3666 22 : if( EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect) )
3667 : {
3668 0 : dfMaxArea = 1e300;
3669 0 : bIsAskedNeatline = TRUE;
3670 : }
3671 : }
3672 :
3673 22 : if( !bIsAskedNeatline )
3674 : {
3675 22 : double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0;
3676 116 : for(i=0;i<nLength;i+=2)
3677 : {
3678 94 : double dfX = Get(poNeatline, i);
3679 94 : double dfY = Get(poNeatline, i + 1);
3680 94 : if (i == 0 || dfX < dfMinX) dfMinX = dfX;
3681 94 : if (i == 0 || dfY < dfMinY) dfMinY = dfY;
3682 94 : if (i == 0 || dfX > dfMaxX) dfMaxX = dfX;
3683 94 : if (i == 0 || dfY > dfMaxY) dfMaxY = dfY;
3684 : }
3685 22 : double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY);
3686 22 : if (dfArea < dfMaxArea)
3687 : {
3688 0 : CPLDebug("PDF", "Not the largest neatline. Skipping it");
3689 0 : return TRUE;
3690 : }
3691 :
3692 22 : CPLDebug("PDF", "This is the largest neatline for now");
3693 22 : dfMaxArea = dfArea;
3694 : }
3695 : else
3696 : CPLDebug("PDF", "The \"%s\" registration will be selected",
3697 0 : pszNeatlineToSelect);
3698 :
3699 22 : if (pbIsBestCandidate)
3700 2 : *pbIsBestCandidate = TRUE;
3701 :
3702 22 : delete poNeatLine;
3703 22 : poNeatLine = new OGRPolygon();
3704 44 : OGRLinearRing* poRing = new OGRLinearRing();
3705 22 : if (nLength == 4)
3706 : {
3707 : /* 2 points only ? They are the bounding box */
3708 0 : double dfX1 = Get(poNeatline, 0);
3709 0 : double dfY1 = Get(poNeatline, 1);
3710 0 : double dfX2 = Get(poNeatline, 2);
3711 0 : double dfY2 = Get(poNeatline, 3);
3712 0 : poRing->addPoint(dfX1, dfY1);
3713 0 : poRing->addPoint(dfX2, dfY1);
3714 0 : poRing->addPoint(dfX2, dfY2);
3715 0 : poRing->addPoint(dfX1, dfY2);
3716 : }
3717 : else
3718 : {
3719 116 : for(i=0;i<nLength;i+=2)
3720 : {
3721 94 : double dfX = Get(poNeatline, i);
3722 94 : double dfY = Get(poNeatline, i + 1);
3723 94 : poRing->addPoint(dfX, dfY);
3724 : }
3725 : }
3726 22 : poNeatLine->addRingDirectly(poRing);
3727 : }
3728 :
3729 22 : return TRUE;
3730 : }
3731 :
3732 : /************************************************************************/
3733 : /* ParseLGIDictDictSecondPass() */
3734 : /************************************************************************/
3735 :
3736 22 : int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict)
3737 : {
3738 : int i;
3739 :
3740 : /* -------------------------------------------------------------------- */
3741 : /* Extract Description attribute */
3742 : /* -------------------------------------------------------------------- */
3743 : GDALPDFObject* poDescription;
3744 44 : if ( (poDescription = poLGIDict->Get("Description")) != NULL &&
3745 22 : poDescription->GetType() == PDFObjectType_String )
3746 : {
3747 22 : CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str());
3748 : }
3749 :
3750 : /* -------------------------------------------------------------------- */
3751 : /* Extract CTM attribute */
3752 : /* -------------------------------------------------------------------- */
3753 : GDALPDFObject* poCTM;
3754 22 : bHasCTM = FALSE;
3755 41 : if ((poCTM = poLGIDict->Get("CTM")) != NULL &&
3756 19 : poCTM->GetType() == PDFObjectType_Array)
3757 : {
3758 19 : int nLength = poCTM->GetArray()->GetLength();
3759 19 : if ( nLength != 6 )
3760 : {
3761 : CPLError(CE_Failure, CPLE_AppDefined,
3762 0 : "Invalid length for CTM");
3763 0 : return FALSE;
3764 : }
3765 :
3766 19 : bHasCTM = TRUE;
3767 133 : for(i=0;i<nLength;i++)
3768 : {
3769 114 : adfCTM[i] = Get(poCTM, i);
3770 : /* Nullify rotation terms that are significantly smaller than */
3771 : /* scaling termes */
3772 114 : if ((i == 1 || i == 2) && fabs(adfCTM[i]) < fabs(adfCTM[0]) * 1e-10)
3773 38 : adfCTM[i] = 0;
3774 114 : CPLDebug("PDF", "CTM[%d] = %.16g", i, adfCTM[i]);
3775 : }
3776 : }
3777 :
3778 : /* -------------------------------------------------------------------- */
3779 : /* Extract Registration attribute */
3780 : /* -------------------------------------------------------------------- */
3781 : GDALPDFObject* poRegistration;
3782 26 : if ((poRegistration = poLGIDict->Get("Registration")) != NULL &&
3783 4 : poRegistration->GetType() == PDFObjectType_Array)
3784 : {
3785 4 : GDALPDFArray* poRegistrationArray = poRegistration->GetArray();
3786 4 : int nLength = poRegistrationArray->GetLength();
3787 4 : if (nLength > 4 || (!bHasCTM && nLength >= 2) )
3788 : {
3789 3 : nGCPCount = 0;
3790 3 : pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nLength);
3791 :
3792 18 : for(i=0;i<nLength;i++)
3793 : {
3794 15 : GDALPDFObject* poGCP = poRegistrationArray->Get(i);
3795 60 : if ( poGCP != NULL &&
3796 15 : poGCP->GetType() == PDFObjectType_Array &&
3797 30 : poGCP->GetArray()->GetLength() == 4 )
3798 : {
3799 15 : double dfUserX = Get(poGCP, 0);
3800 15 : double dfUserY = Get(poGCP, 1);
3801 15 : double dfX = Get(poGCP, 2);
3802 15 : double dfY = Get(poGCP, 3);
3803 15 : CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX);
3804 15 : CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY);
3805 15 : CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX);
3806 15 : CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY);
3807 :
3808 : char szID[32];
3809 15 : sprintf( szID, "%d", nGCPCount+1 );
3810 15 : pasGCPList[nGCPCount].pszId = CPLStrdup( szID );
3811 15 : pasGCPList[nGCPCount].pszInfo = CPLStrdup("");
3812 15 : pasGCPList[nGCPCount].dfGCPPixel = dfUserX;
3813 15 : pasGCPList[nGCPCount].dfGCPLine = dfUserY;
3814 15 : pasGCPList[nGCPCount].dfGCPX = dfX;
3815 15 : pasGCPList[nGCPCount].dfGCPY = dfY;
3816 15 : nGCPCount ++;
3817 : }
3818 : }
3819 : }
3820 : }
3821 :
3822 22 : if (!bHasCTM && nGCPCount == 0)
3823 : {
3824 0 : CPLDebug("PDF", "Neither CTM nor Registration found");
3825 0 : return FALSE;
3826 : }
3827 :
3828 : /* -------------------------------------------------------------------- */
3829 : /* Extract Projection attribute */
3830 : /* -------------------------------------------------------------------- */
3831 : GDALPDFObject* poProjection;
3832 44 : if ((poProjection = poLGIDict->Get("Projection")) == NULL ||
3833 22 : poProjection->GetType() != PDFObjectType_Dictionary)
3834 : {
3835 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection");
3836 0 : return FALSE;
3837 : }
3838 :
3839 22 : return ParseProjDict(poProjection->GetDictionary());
3840 : }
3841 :
3842 : /************************************************************************/
3843 : /* ParseProjDict() */
3844 : /************************************************************************/
3845 :
3846 22 : int PDFDataset::ParseProjDict(GDALPDFDictionary* poProjDict)
3847 : {
3848 22 : if (poProjDict == NULL)
3849 0 : return FALSE;
3850 22 : OGRSpatialReference oSRS;
3851 :
3852 : /* -------------------------------------------------------------------- */
3853 : /* Extract WKT attribute (GDAL extension) */
3854 : /* -------------------------------------------------------------------- */
3855 : GDALPDFObject* poWKT;
3856 30 : if ( (poWKT = poProjDict->Get("WKT")) != NULL &&
3857 8 : poWKT->GetType() == PDFObjectType_String &&
3858 : CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE") ) )
3859 : {
3860 8 : CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it");
3861 8 : const char* pszWKTRead = poWKT->GetString().c_str();
3862 8 : CPLFree(pszWKT);
3863 8 : pszWKT = CPLStrdup(pszWKTRead);
3864 8 : return TRUE;
3865 : }
3866 :
3867 : /* -------------------------------------------------------------------- */
3868 : /* Extract Type attribute */
3869 : /* -------------------------------------------------------------------- */
3870 : GDALPDFObject* poType;
3871 14 : if ((poType = poProjDict->Get("Type")) == NULL)
3872 : {
3873 : CPLError(CE_Failure, CPLE_AppDefined,
3874 0 : "Cannot find Type of Projection object");
3875 0 : return FALSE;
3876 : }
3877 :
3878 14 : if ( poType->GetType() != PDFObjectType_Name )
3879 : {
3880 : CPLError(CE_Failure, CPLE_AppDefined,
3881 0 : "Invalid type for Type of Projection object");
3882 0 : return FALSE;
3883 : }
3884 :
3885 14 : if ( strcmp(poType->GetName().c_str(), "Projection") != 0 )
3886 : {
3887 : CPLError(CE_Failure, CPLE_AppDefined,
3888 : "Invalid value for Type of Projection object : %s",
3889 0 : poType->GetName().c_str());
3890 0 : return FALSE;
3891 : }
3892 :
3893 : /* -------------------------------------------------------------------- */
3894 : /* Extract Datum attribute */
3895 : /* -------------------------------------------------------------------- */
3896 14 : int bIsWGS84 = FALSE;
3897 14 : int bIsNAD83 = FALSE;
3898 14 : int bIsNAD27 = FALSE;
3899 :
3900 : GDALPDFObject* poDatum;
3901 14 : if ((poDatum = poProjDict->Get("Datum")) != NULL)
3902 : {
3903 14 : if (poDatum->GetType() == PDFObjectType_String)
3904 : {
3905 14 : const char* pszDatum = poDatum->GetString().c_str();
3906 14 : CPLDebug("PDF", "Datum = %s", pszDatum);
3907 18 : if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE"))
3908 : {
3909 4 : bIsWGS84 = TRUE;
3910 4 : oSRS.SetWellKnownGeogCS("WGS84");
3911 : }
3912 10 : else if (EQUAL(pszDatum, "NAR") || EQUALN(pszDatum, "NAR-", 4))
3913 : {
3914 0 : bIsNAD83 = TRUE;
3915 0 : oSRS.SetWellKnownGeogCS("NAD83");
3916 : }
3917 19 : else if (EQUAL(pszDatum, "NAS") || EQUALN(pszDatum, "NAS-", 4))
3918 : {
3919 9 : bIsNAD27 = TRUE;
3920 9 : oSRS.SetWellKnownGeogCS("NAD27");
3921 : }
3922 1 : else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */
3923 : {
3924 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
3925 : "unknown" /*const char * pszDatumName */,
3926 : "International 1924",
3927 1 : 6378388,297);
3928 1 : oSRS.SetTOWGS84(-333,-222,114);
3929 : }
3930 0 : else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */
3931 : {
3932 0 : oSRS.importFromEPSG(4131);
3933 : }
3934 0 : else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */
3935 : {
3936 0 : oSRS.importFromEPSG(4283);
3937 : }
3938 : else
3939 : {
3940 : CPLError(CE_Warning, CPLE_AppDefined,
3941 : "Unhandled (yet) value for Datum : %s. Defaulting to WGS84...",
3942 0 : pszDatum);
3943 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
3944 : "unknown" /*const char * pszDatumName */,
3945 : "unknown",
3946 0 : 6378137,298.257223563);
3947 : }
3948 : }
3949 0 : else if (poDatum->GetType() == PDFObjectType_Dictionary)
3950 : {
3951 0 : GDALPDFDictionary* poDatumDict = poDatum->GetDictionary();
3952 :
3953 0 : GDALPDFObject* poDatumDescription = poDatumDict->Get("Description");
3954 0 : const char* pszDatumDescription = "unknown";
3955 0 : if (poDatumDescription != NULL &&
3956 0 : poDatumDescription->GetType() == PDFObjectType_String)
3957 0 : pszDatumDescription = poDatumDescription->GetString().c_str();
3958 0 : CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription);
3959 :
3960 0 : GDALPDFObject* poEllipsoid = poDatumDict->Get("Ellipsoid");
3961 0 : if (poEllipsoid == NULL ||
3962 0 : !(poEllipsoid->GetType() == PDFObjectType_String ||
3963 0 : poEllipsoid->GetType() == PDFObjectType_Dictionary))
3964 : {
3965 : CPLError(CE_Warning, CPLE_AppDefined,
3966 0 : "Cannot find Ellipsoid in Datum. Defaulting to WGS84...");
3967 : oSRS.SetGeogCS( "unknown",
3968 : pszDatumDescription,
3969 : "unknown",
3970 0 : 6378137,298.257223563);
3971 : }
3972 0 : else if (poEllipsoid->GetType() == PDFObjectType_String)
3973 : {
3974 0 : const char* pszEllipsoid = poEllipsoid->GetString().c_str();
3975 0 : CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid);
3976 0 : if( EQUAL(pszEllipsoid, "WE") )
3977 : {
3978 : oSRS.SetGeogCS( "unknown",
3979 : pszDatumDescription,
3980 : "WGS 84",
3981 0 : 6378137,298.257223563);
3982 : }
3983 : else
3984 : {
3985 : CPLError(CE_Warning, CPLE_AppDefined,
3986 : "Unhandled (yet) value for Ellipsoid : %s. Defaulting to WGS84...",
3987 0 : pszEllipsoid);
3988 : oSRS.SetGeogCS( "unknown",
3989 : pszDatumDescription,
3990 : pszEllipsoid,
3991 0 : 6378137,298.257223563);
3992 : }
3993 : }
3994 : else// if (poEllipsoid->GetType() == PDFObjectType_Dictionary)
3995 : {
3996 0 : GDALPDFDictionary* poEllipsoidDict = poEllipsoid->GetDictionary();
3997 :
3998 0 : GDALPDFObject* poEllipsoidDescription = poEllipsoidDict->Get("Description");
3999 0 : const char* pszEllipsoidDescription = "unknown";
4000 0 : if (poEllipsoidDescription != NULL &&
4001 0 : poEllipsoidDescription->GetType() == PDFObjectType_String)
4002 0 : pszEllipsoidDescription = poEllipsoidDescription->GetString().c_str();
4003 0 : CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", pszEllipsoidDescription);
4004 :
4005 0 : double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis");
4006 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", dfSemiMajor);
4007 0 : double dfInvFlattening = -1.0;
4008 :
4009 0 : if( poEllipsoidDict->Get("InvFlattening") )
4010 : {
4011 0 : dfInvFlattening = Get(poEllipsoidDict, "InvFlattening");
4012 0 : CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", dfInvFlattening);
4013 : }
4014 0 : else if( poEllipsoidDict->Get("SemiMinorAxis") )
4015 : {
4016 0 : double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis");
4017 0 : CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", dfSemiMinor);
4018 0 : if( ABS(dfSemiMajor/dfSemiMinor) - 1.0 < 0.0000000000001 )
4019 0 : dfInvFlattening = 0.0;
4020 : else
4021 0 : dfInvFlattening = -1.0 / (dfSemiMinor/dfSemiMajor - 1.0);
4022 : }
4023 :
4024 0 : if( dfSemiMajor != 0.0 && dfInvFlattening != -1.0 )
4025 : {
4026 : oSRS.SetGeogCS( "unknown",
4027 : pszDatumDescription,
4028 : pszEllipsoidDescription,
4029 0 : dfSemiMajor, dfInvFlattening);
4030 : }
4031 : else
4032 : {
4033 : CPLError(CE_Warning, CPLE_AppDefined,
4034 0 : "Invalid Ellipsoid object. Defaulting to WGS84...");
4035 : oSRS.SetGeogCS( "unknown",
4036 : pszDatumDescription,
4037 : pszEllipsoidDescription,
4038 0 : 6378137,298.257223563);
4039 : }
4040 :
4041 :
4042 : }
4043 :
4044 0 : GDALPDFObject* poTOWGS84 = poDatumDict->Get("ToWGS84");
4045 0 : if( poTOWGS84 != NULL && poTOWGS84->GetType() == PDFObjectType_Dictionary )
4046 : {
4047 0 : GDALPDFDictionary* poTOWGS84Dict = poTOWGS84->GetDictionary();
4048 0 : double dx = Get(poTOWGS84Dict, "dx");
4049 0 : double dy = Get(poTOWGS84Dict, "dy");
4050 0 : double dz = Get(poTOWGS84Dict, "dz");
4051 0 : if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") &&
4052 0 : poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf"))
4053 : {
4054 0 : double rx = Get(poTOWGS84Dict, "rx");
4055 0 : double ry = Get(poTOWGS84Dict, "ry");
4056 0 : double rz = Get(poTOWGS84Dict, "rz");
4057 0 : double sf = Get(poTOWGS84Dict, "sf");
4058 0 : oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf);
4059 : }
4060 : else
4061 : {
4062 0 : oSRS.SetTOWGS84(dx, dy, dz);
4063 : }
4064 : }
4065 : }
4066 : }
4067 :
4068 : /* -------------------------------------------------------------------- */
4069 : /* Extract Hemisphere attribute */
4070 : /* -------------------------------------------------------------------- */
4071 14 : CPLString osHemisphere;
4072 : GDALPDFObject* poHemisphere;
4073 23 : if ((poHemisphere = poProjDict->Get("Hemisphere")) != NULL &&
4074 9 : poHemisphere->GetType() == PDFObjectType_String)
4075 : {
4076 9 : osHemisphere = poHemisphere->GetString();
4077 : }
4078 :
4079 : /* -------------------------------------------------------------------- */
4080 : /* Extract ProjectionType attribute */
4081 : /* -------------------------------------------------------------------- */
4082 : GDALPDFObject* poProjectionType;
4083 28 : if ((poProjectionType = poProjDict->Get("ProjectionType")) == NULL ||
4084 14 : poProjectionType->GetType() != PDFObjectType_String)
4085 : {
4086 : CPLError(CE_Failure, CPLE_AppDefined,
4087 0 : "Cannot find ProjectionType of Projection object");
4088 0 : return FALSE;
4089 : }
4090 14 : CPLString osProjectionType(poProjectionType->GetString());
4091 14 : CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str());
4092 :
4093 : /* Unhandled: NONE, GEODETIC */
4094 :
4095 14 : if (EQUAL(osProjectionType, "GEOGRAPHIC"))
4096 : {
4097 : /* Nothing to do */
4098 : }
4099 :
4100 : /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */
4101 :
4102 10 : else if (EQUAL(osProjectionType, "UT")) /* UTM */
4103 : {
4104 9 : int nZone = (int)Get(poProjDict, "Zone");
4105 9 : int bNorth = EQUAL(osHemisphere, "N");
4106 9 : if (bIsWGS84)
4107 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
4108 : else
4109 9 : oSRS.SetUTM( nZone, bNorth );
4110 : }
4111 :
4112 1 : else if (EQUAL(osProjectionType, "UP")) /* Universal Polar Stereographic (UPS) */
4113 : {
4114 0 : int bNorth = EQUAL(osHemisphere, "N");
4115 0 : if (bIsWGS84)
4116 0 : oSRS.importFromEPSG( (bNorth) ? 32661 : 32761 );
4117 : else
4118 : oSRS.SetPS( (bNorth) ? 90 : -90, 0,
4119 0 : 0.994, 200000, 200000 );
4120 : }
4121 :
4122 1 : else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */
4123 : {
4124 0 : int nZone = (int)Get(poProjDict, "Zone");
4125 0 : oSRS.SetStatePlane( nZone, bIsNAD83 );
4126 : }
4127 :
4128 1 : else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */
4129 : {
4130 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
4131 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
4132 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4133 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4134 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4135 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4136 : oSRS.SetACEA( dfStdP1, dfStdP2,
4137 : dfCenterLat, dfCenterLong,
4138 0 : dfFalseEasting, dfFalseNorthing );
4139 : }
4140 :
4141 1 : else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */
4142 : {
4143 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4144 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4145 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4146 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4147 : oSRS.SetAE( dfCenterLat, dfCenterLong,
4148 0 : dfFalseEasting, dfFalseNorthing );
4149 : }
4150 :
4151 1 : else if (EQUAL(osProjectionType, "BF")) /* Bonne */
4152 : {
4153 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
4154 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
4155 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4156 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4157 : oSRS.SetBonne( dfStdP1, dfCentralMeridian,
4158 0 : dfFalseEasting, dfFalseNorthing );
4159 : }
4160 :
4161 1 : else if (EQUAL(osProjectionType, "CS")) /* Cassini */
4162 : {
4163 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4164 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4165 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4166 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4167 : oSRS.SetCS( dfCenterLat, dfCenterLong,
4168 0 : dfFalseEasting, dfFalseNorthing );
4169 : }
4170 :
4171 1 : else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */
4172 : {
4173 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
4174 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
4175 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4176 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4177 : oSRS.SetCEA( dfStdP1, dfCentralMeridian,
4178 0 : dfFalseEasting, dfFalseNorthing );
4179 : }
4180 :
4181 1 : else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */
4182 : {
4183 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
4184 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4185 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4186 : oSRS.SetEckertIV( dfCentralMeridian,
4187 0 : dfFalseEasting, dfFalseNorthing );
4188 : }
4189 :
4190 1 : else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */
4191 : {
4192 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
4193 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4194 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4195 : oSRS.SetEckertVI( dfCentralMeridian,
4196 0 : dfFalseEasting, dfFalseNorthing );
4197 : }
4198 :
4199 1 : else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */
4200 : {
4201 0 : double dfCenterLat = Get(poProjDict, "StandardParallel");
4202 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4203 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4204 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4205 : oSRS.SetEquirectangular( dfCenterLat, dfCenterLong,
4206 0 : dfFalseEasting, dfFalseNorthing );
4207 : }
4208 :
4209 1 : else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */
4210 : {
4211 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4212 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4213 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4214 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4215 : oSRS.SetGnomonic(dfCenterLat, dfCenterLong,
4216 0 : dfFalseEasting, dfFalseNorthing );
4217 : }
4218 :
4219 1 : else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */
4220 : {
4221 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
4222 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
4223 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4224 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4225 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4226 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4227 : oSRS.SetLCC( dfStdP1, dfStdP2,
4228 : dfCenterLat, dfCenterLong,
4229 0 : dfFalseEasting, dfFalseNorthing );
4230 : }
4231 :
4232 1 : else if (EQUAL(osProjectionType, "MC")) /* Mercator */
4233 : {
4234 : #ifdef not_supported
4235 : if (poProjDict->Get("StandardParallelOne") == NULL)
4236 : {
4237 : #endif
4238 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4239 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4240 0 : double dfScale = Get(poProjDict, "ScaleFactor");
4241 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4242 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4243 : oSRS.SetMercator( dfCenterLat, dfCenterLong,
4244 : dfScale,
4245 0 : dfFalseEasting, dfFalseNorthing );
4246 : #ifdef not_supported
4247 : }
4248 : else
4249 : {
4250 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
4251 : double dfCenterLat = poProjDict->Get("OriginLatitude") ? Get(poProjDict, "OriginLatitude") : 0;
4252 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4253 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4254 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4255 : oSRS.SetMercator2SP( dfStdP1,
4256 : dfCenterLat, dfCenterLong,
4257 : dfFalseEasting, dfFalseNorthing );
4258 : }
4259 : #endif
4260 : }
4261 :
4262 1 : else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */
4263 : {
4264 0 : double dfCenterLat = 0 /* ? */;
4265 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4266 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4267 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4268 : oSRS.SetMC( dfCenterLat, dfCenterLong,
4269 0 : dfFalseEasting, dfFalseNorthing );
4270 : }
4271 :
4272 1 : else if (EQUAL(osProjectionType, "MP")) /* Mollweide */
4273 : {
4274 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
4275 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4276 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4277 : oSRS.SetMollweide( dfCentralMeridian,
4278 0 : dfFalseEasting, dfFalseNorthing );
4279 : }
4280 :
4281 : /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */
4282 :
4283 1 : else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */
4284 : {
4285 : /* No parameter specified in the PDF, so let's take the ones of EPSG:27200 */
4286 0 : double dfCenterLat = -41;
4287 0 : double dfCenterLong = 173;
4288 0 : double dfFalseEasting = 2510000;
4289 0 : double dfFalseNorthing = 6023150;
4290 : oSRS.SetNZMG( dfCenterLat, dfCenterLong,
4291 0 : dfFalseEasting, dfFalseNorthing );
4292 : }
4293 :
4294 1 : else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */
4295 : {
4296 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4297 0 : double dfLat1 = Get(poProjDict, "LatitudeOne");
4298 0 : double dfLong1 = Get(poProjDict, "LongitudeOne");
4299 0 : double dfLat2 = Get(poProjDict, "LatitudeTwo");
4300 0 : double dfLong2 = Get(poProjDict, "LongitudeTwo");
4301 0 : double dfScale = Get(poProjDict, "ScaleFactor");
4302 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4303 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4304 : oSRS.SetHOM2PNO( dfCenterLat,
4305 : dfLat1, dfLong1,
4306 : dfLat2, dfLong2,
4307 : dfScale,
4308 : dfFalseEasting,
4309 0 : dfFalseNorthing );
4310 : }
4311 :
4312 1 : else if (EQUAL(osProjectionType, "OD")) /* Orthographic */
4313 : {
4314 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4315 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4316 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4317 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4318 : oSRS.SetOrthographic( dfCenterLat, dfCenterLong,
4319 0 : dfFalseEasting, dfFalseNorthing );
4320 : }
4321 :
4322 1 : else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */
4323 : {
4324 0 : double dfCenterLat = Get(poProjDict, "LatitudeTrueScale");
4325 0 : double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole");
4326 0 : double dfScale = 1.0;
4327 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4328 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4329 : oSRS.SetPS( dfCenterLat, dfCenterLong,
4330 : dfScale,
4331 0 : dfFalseEasting, dfFalseNorthing);
4332 : }
4333 :
4334 1 : else if (EQUAL(osProjectionType, "PH")) /* Polyconic */
4335 : {
4336 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4337 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4338 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4339 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4340 : oSRS.SetPolyconic( dfCenterLat, dfCenterLong,
4341 0 : dfFalseEasting, dfFalseNorthing );
4342 : }
4343 :
4344 1 : else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */
4345 : {
4346 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4347 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4348 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4349 : oSRS.SetSinusoidal( dfCenterLong,
4350 0 : dfFalseEasting, dfFalseNorthing );
4351 : }
4352 :
4353 1 : else if (EQUAL(osProjectionType, "SD")) /* Stereographic */
4354 : {
4355 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4356 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4357 0 : double dfScale = 1.0;
4358 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4359 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4360 : oSRS.SetStereographic( dfCenterLat, dfCenterLong,
4361 : dfScale,
4362 0 : dfFalseEasting, dfFalseNorthing);
4363 : }
4364 :
4365 1 : else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */
4366 : {
4367 1 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
4368 1 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4369 1 : double dfScale = Get(poProjDict, "ScaleFactor");
4370 1 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4371 1 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4372 2 : if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfFalseEasting == 500000 &&
4373 : (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0))
4374 : {
4375 1 : int nZone = (int) floor( (dfCenterLong + 180.0) / 6.0 ) + 1;
4376 1 : int bNorth = dfFalseNorthing == 0;
4377 1 : if (bIsWGS84)
4378 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
4379 1 : else if (bIsNAD83 && bNorth)
4380 0 : oSRS.importFromEPSG( 26900 + nZone );
4381 : else
4382 1 : oSRS.SetUTM( nZone, bNorth );
4383 : }
4384 : else
4385 : {
4386 : oSRS.SetTM( dfCenterLat, dfCenterLong,
4387 : dfScale,
4388 0 : dfFalseEasting, dfFalseNorthing );
4389 : }
4390 : }
4391 :
4392 : /* Unhandled TX : Transverse Cylindrical Equal Area */
4393 :
4394 0 : else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */
4395 : {
4396 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
4397 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
4398 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
4399 : oSRS.SetVDG( dfCenterLong,
4400 0 : dfFalseEasting, dfFalseNorthing );
4401 : }
4402 :
4403 : else
4404 : {
4405 : CPLError(CE_Failure, CPLE_AppDefined,
4406 : "Unhandled (yet) value for ProjectionType : %s",
4407 0 : osProjectionType.c_str());
4408 0 : return FALSE;
4409 : }
4410 :
4411 : /* -------------------------------------------------------------------- */
4412 : /* Extract Units attribute */
4413 : /* -------------------------------------------------------------------- */
4414 14 : CPLString osUnits;
4415 : GDALPDFObject* poUnits;
4416 23 : if ((poUnits = poProjDict->Get("Units")) != NULL &&
4417 9 : poUnits->GetType() == PDFObjectType_String)
4418 : {
4419 9 : osUnits = poUnits->GetString();
4420 9 : CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str());
4421 :
4422 9 : if (EQUAL(osUnits, "M"))
4423 9 : oSRS.SetLinearUnits( "Meter", 1.0 );
4424 0 : else if (EQUAL(osUnits, "FT"))
4425 0 : oSRS.SetLinearUnits( "foot", 0.3048 );
4426 : }
4427 :
4428 : /* -------------------------------------------------------------------- */
4429 : /* Export SpatialRef */
4430 : /* -------------------------------------------------------------------- */
4431 14 : CPLFree(pszWKT);
4432 14 : pszWKT = NULL;
4433 14 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
4434 : {
4435 0 : CPLFree(pszWKT);
4436 0 : pszWKT = NULL;
4437 : }
4438 :
4439 14 : return TRUE;
4440 : }
4441 :
4442 : /************************************************************************/
4443 : /* ParseVP() */
4444 : /************************************************************************/
4445 :
4446 132 : int PDFDataset::ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight)
4447 : {
4448 : int i;
4449 :
4450 132 : if (poVP->GetType() != PDFObjectType_Array)
4451 0 : return FALSE;
4452 :
4453 132 : GDALPDFArray* poVPArray = poVP->GetArray();
4454 :
4455 132 : int nLength = poVPArray->GetLength();
4456 132 : CPLDebug("PDF", "VP length = %d", nLength);
4457 132 : if (nLength < 1)
4458 0 : return FALSE;
4459 :
4460 : /* -------------------------------------------------------------------- */
4461 : /* Find the largest BBox */
4462 : /* -------------------------------------------------------------------- */
4463 132 : int iLargest = 0;
4464 132 : double dfLargestArea = 0;
4465 :
4466 272 : for(i=0;i<nLength;i++)
4467 : {
4468 140 : GDALPDFObject* poVPElt = poVPArray->Get(i);
4469 140 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
4470 : {
4471 0 : return FALSE;
4472 : }
4473 :
4474 140 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
4475 :
4476 : GDALPDFObject* poBBox;
4477 280 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
4478 140 : poBBox->GetType() != PDFObjectType_Array )
4479 : {
4480 : CPLError(CE_Failure, CPLE_AppDefined,
4481 0 : "Cannot find Bbox object");
4482 0 : return FALSE;
4483 : }
4484 :
4485 140 : int nBboxLength = poBBox->GetArray()->GetLength();
4486 140 : if (nBboxLength != 4)
4487 : {
4488 : CPLError(CE_Failure, CPLE_AppDefined,
4489 0 : "Invalid length for Bbox object");
4490 0 : return FALSE;
4491 : }
4492 :
4493 : double adfBBox[4];
4494 140 : adfBBox[0] = Get(poBBox, 0);
4495 140 : adfBBox[1] = Get(poBBox, 1);
4496 140 : adfBBox[2] = Get(poBBox, 2);
4497 140 : adfBBox[3] = Get(poBBox, 3);
4498 140 : double dfArea = fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]);
4499 140 : if (dfArea > dfLargestArea)
4500 : {
4501 132 : iLargest = i;
4502 132 : dfLargestArea = dfArea;
4503 : }
4504 : }
4505 :
4506 132 : if (nLength > 1)
4507 : {
4508 8 : CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest);
4509 : }
4510 :
4511 :
4512 132 : GDALPDFObject* poVPElt = poVPArray->Get(iLargest);
4513 132 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
4514 : {
4515 0 : return FALSE;
4516 : }
4517 :
4518 132 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
4519 :
4520 : GDALPDFObject* poBBox;
4521 264 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
4522 132 : poBBox->GetType() != PDFObjectType_Array )
4523 : {
4524 : CPLError(CE_Failure, CPLE_AppDefined,
4525 0 : "Cannot find Bbox object");
4526 0 : return FALSE;
4527 : }
4528 :
4529 132 : int nBboxLength = poBBox->GetArray()->GetLength();
4530 132 : if (nBboxLength != 4)
4531 : {
4532 : CPLError(CE_Failure, CPLE_AppDefined,
4533 0 : "Invalid length for Bbox object");
4534 0 : return FALSE;
4535 : }
4536 :
4537 132 : double dfULX = Get(poBBox, 0);
4538 132 : double dfULY = dfMediaBoxHeight - Get(poBBox, 1);
4539 132 : double dfLRX = Get(poBBox, 2);
4540 132 : double dfLRY = dfMediaBoxHeight - Get(poBBox, 3);
4541 :
4542 : /* -------------------------------------------------------------------- */
4543 : /* Extract Measure attribute */
4544 : /* -------------------------------------------------------------------- */
4545 : GDALPDFObject* poMeasure;
4546 264 : if( (poMeasure = poVPEltDict->Get("Measure")) == NULL ||
4547 132 : poMeasure->GetType() != PDFObjectType_Dictionary )
4548 : {
4549 : CPLError(CE_Failure, CPLE_AppDefined,
4550 0 : "Cannot find Measure object");
4551 0 : return FALSE;
4552 : }
4553 :
4554 : int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight,
4555 132 : dfULX, dfULY, dfLRX, dfLRY);
4556 :
4557 : /* -------------------------------------------------------------------- */
4558 : /* Extract PointData attribute */
4559 : /* -------------------------------------------------------------------- */
4560 : GDALPDFObject* poPointData;
4561 132 : if( (poPointData = poVPEltDict->Get("PtData")) != NULL &&
4562 0 : poPointData->GetType() == PDFObjectType_Dictionary )
4563 : {
4564 0 : CPLDebug("PDF", "Found PointData");
4565 : }
4566 :
4567 132 : return bRet;
4568 : }
4569 :
4570 : /************************************************************************/
4571 : /* ParseMeasure() */
4572 : /************************************************************************/
4573 :
4574 134 : int PDFDataset::ParseMeasure(GDALPDFObject* poMeasure,
4575 : double dfMediaBoxWidth, double dfMediaBoxHeight,
4576 : double dfULX, double dfULY, double dfLRX, double dfLRY)
4577 : {
4578 : int i;
4579 134 : GDALPDFDictionary* poMeasureDict = poMeasure->GetDictionary();
4580 :
4581 : /* -------------------------------------------------------------------- */
4582 : /* Extract Subtype attribute */
4583 : /* -------------------------------------------------------------------- */
4584 : GDALPDFObject* poSubtype;
4585 268 : if( (poSubtype = poMeasureDict->Get("Subtype")) == NULL ||
4586 134 : poSubtype->GetType() != PDFObjectType_Name )
4587 : {
4588 : CPLError(CE_Failure, CPLE_AppDefined,
4589 0 : "Cannot find Subtype object");
4590 0 : return FALSE;
4591 : }
4592 :
4593 134 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
4594 :
4595 : /* -------------------------------------------------------------------- */
4596 : /* Extract Bounds attribute (optional) */
4597 : /* -------------------------------------------------------------------- */
4598 :
4599 : /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf */
4600 : /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */
4601 : /* LPTS, GPTS and Bounds. Use those ones */
4602 :
4603 : GDALPDFObject* poBounds;
4604 134 : if( (poBounds = poMeasureDict->Get("lgit:Bounds")) != NULL &&
4605 0 : poBounds->GetType() == PDFObjectType_Array )
4606 : {
4607 0 : CPLDebug("PDF", "Using lgit:Bounds");
4608 : }
4609 268 : else if( (poBounds = poMeasureDict->Get("Bounds")) == NULL ||
4610 134 : poBounds->GetType() != PDFObjectType_Array )
4611 : {
4612 0 : poBounds = NULL;
4613 : }
4614 :
4615 134 : if (poBounds != NULL)
4616 : {
4617 134 : int nBoundsLength = poBounds->GetArray()->GetLength();
4618 134 : if (nBoundsLength == 8)
4619 : {
4620 : double adfBounds[8];
4621 1206 : for(i=0;i<8;i++)
4622 : {
4623 1072 : adfBounds[i] = Get(poBounds, i);
4624 1072 : CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
4625 : }
4626 :
4627 : // TODO we should use it to restrict the neatline but
4628 : // I have yet to set a sample where bounds are not the four
4629 : // corners of the unit square.
4630 : }
4631 : }
4632 :
4633 : /* -------------------------------------------------------------------- */
4634 : /* Extract GPTS attribute */
4635 : /* -------------------------------------------------------------------- */
4636 : GDALPDFObject* poGPTS;
4637 134 : if( (poGPTS = poMeasureDict->Get("lgit:GPTS")) != NULL &&
4638 0 : poGPTS->GetType() == PDFObjectType_Array )
4639 : {
4640 0 : CPLDebug("PDF", "Using lgit:GPTS");
4641 : }
4642 268 : else if( (poGPTS = poMeasureDict->Get("GPTS")) == NULL ||
4643 134 : poGPTS->GetType() != PDFObjectType_Array )
4644 : {
4645 : CPLError(CE_Failure, CPLE_AppDefined,
4646 0 : "Cannot find GPTS object");
4647 0 : return FALSE;
4648 : }
4649 :
4650 134 : int nGPTSLength = poGPTS->GetArray()->GetLength();
4651 134 : if (nGPTSLength != 8)
4652 : {
4653 : CPLError(CE_Failure, CPLE_AppDefined,
4654 0 : "Invalid length for GPTS object");
4655 0 : return FALSE;
4656 : }
4657 :
4658 : double adfGPTS[8];
4659 1206 : for(i=0;i<8;i++)
4660 : {
4661 1072 : adfGPTS[i] = Get(poGPTS, i);
4662 1072 : CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
4663 : }
4664 :
4665 : /* -------------------------------------------------------------------- */
4666 : /* Extract LPTS attribute */
4667 : /* -------------------------------------------------------------------- */
4668 : GDALPDFObject* poLPTS;
4669 134 : if( (poLPTS = poMeasureDict->Get("lgit:LPTS")) != NULL &&
4670 0 : poLPTS->GetType() == PDFObjectType_Array )
4671 : {
4672 0 : CPLDebug("PDF", "Using lgit:LPTS");
4673 : }
4674 268 : else if( (poLPTS = poMeasureDict->Get("LPTS")) == NULL ||
4675 134 : poLPTS->GetType() != PDFObjectType_Array )
4676 : {
4677 : CPLError(CE_Failure, CPLE_AppDefined,
4678 0 : "Cannot find LPTS object");
4679 0 : return FALSE;
4680 : }
4681 :
4682 134 : int nLPTSLength = poLPTS->GetArray()->GetLength();
4683 134 : if (nLPTSLength != 8)
4684 : {
4685 : CPLError(CE_Failure, CPLE_AppDefined,
4686 0 : "Invalid length for LPTS object");
4687 0 : return FALSE;
4688 : }
4689 :
4690 : double adfLPTS[8];
4691 1206 : for(i=0;i<8;i++)
4692 : {
4693 1072 : adfLPTS[i] = Get(poLPTS, i);
4694 1072 : CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
4695 : }
4696 :
4697 : /* -------------------------------------------------------------------- */
4698 : /* Extract GCS attribute */
4699 : /* -------------------------------------------------------------------- */
4700 : GDALPDFObject* poGCS;
4701 268 : if( (poGCS = poMeasureDict->Get("GCS")) == NULL ||
4702 134 : poGCS->GetType() != PDFObjectType_Dictionary )
4703 : {
4704 : CPLError(CE_Failure, CPLE_AppDefined,
4705 0 : "Cannot find GCS object");
4706 0 : return FALSE;
4707 : }
4708 :
4709 134 : GDALPDFDictionary* poGCSDict = poGCS->GetDictionary();
4710 :
4711 : /* -------------------------------------------------------------------- */
4712 : /* Extract GCS.Type attribute */
4713 : /* -------------------------------------------------------------------- */
4714 : GDALPDFObject* poGCSType;
4715 268 : if( (poGCSType = poGCSDict->Get("Type")) == NULL ||
4716 134 : poGCSType->GetType() != PDFObjectType_Name )
4717 : {
4718 : CPLError(CE_Failure, CPLE_AppDefined,
4719 0 : "Cannot find GCS.Type object");
4720 0 : return FALSE;
4721 : }
4722 :
4723 134 : CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str());
4724 :
4725 : /* -------------------------------------------------------------------- */
4726 : /* Extract EPSG attribute */
4727 : /* -------------------------------------------------------------------- */
4728 : GDALPDFObject* poEPSG;
4729 134 : int nEPSGCode = 0;
4730 258 : if( (poEPSG = poGCSDict->Get("EPSG")) != NULL &&
4731 124 : poEPSG->GetType() == PDFObjectType_Int )
4732 : {
4733 124 : nEPSGCode = poEPSG->GetInt();
4734 124 : CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode);
4735 : }
4736 :
4737 : /* -------------------------------------------------------------------- */
4738 : /* Extract GCS.WKT attribute */
4739 : /* -------------------------------------------------------------------- */
4740 134 : GDALPDFObject* poGCSWKT = poGCSDict->Get("WKT");
4741 268 : if( poGCSWKT != NULL &&
4742 134 : poGCSWKT->GetType() != PDFObjectType_String )
4743 : {
4744 0 : poGCSWKT = NULL;
4745 : }
4746 :
4747 134 : if (poGCSWKT != NULL)
4748 134 : CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str());
4749 :
4750 134 : if (nEPSGCode <= 0 && poGCSWKT == NULL)
4751 : {
4752 : CPLError(CE_Failure, CPLE_AppDefined,
4753 0 : "Cannot find GCS.WKT or GCS.EPSG objects");
4754 0 : return FALSE;
4755 : }
4756 :
4757 134 : OGRSpatialReference oSRS;
4758 134 : int bSRSOK = FALSE;
4759 134 : if (nEPSGCode != 0 &&
4760 : oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
4761 : {
4762 124 : bSRSOK = TRUE;
4763 124 : CPLFree(pszWKT);
4764 124 : pszWKT = NULL;
4765 124 : oSRS.exportToWkt(&pszWKT);
4766 : }
4767 : else
4768 : {
4769 10 : if (poGCSWKT == NULL)
4770 : {
4771 : CPLError(CE_Failure, CPLE_AppDefined,
4772 0 : "Cannot resolve EPSG object, and GCS.WKT not found");
4773 0 : return FALSE;
4774 : }
4775 :
4776 10 : CPLFree(pszWKT);
4777 10 : pszWKT = CPLStrdup(poGCSWKT->GetString().c_str());
4778 : }
4779 :
4780 134 : if (!bSRSOK)
4781 : {
4782 10 : char* pszWktTemp = pszWKT;
4783 10 : if (oSRS.importFromWkt(&pszWktTemp) != OGRERR_NONE)
4784 : {
4785 0 : CPLFree(pszWKT);
4786 0 : pszWKT = NULL;
4787 0 : return FALSE;
4788 : }
4789 : }
4790 :
4791 : /* For http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */
4792 : /* or http://www.agmkt.state.ny.us/soilwater/aem/gis_mapping_tools/HUC12_Albany.pdf */
4793 134 : const char* pszDatum = oSRS.GetAttrValue("Datum");
4794 134 : if (pszDatum && strncmp(pszDatum, "D_", 2) == 0)
4795 : {
4796 10 : oSRS.morphFromESRI();
4797 :
4798 10 : CPLFree(pszWKT);
4799 10 : pszWKT = NULL;
4800 10 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
4801 : {
4802 0 : CPLFree(pszWKT);
4803 0 : pszWKT = NULL;
4804 : }
4805 : else
4806 : {
4807 10 : CPLDebug("PDF", "WKT after morphFromESRI() = %s", pszWKT);
4808 : }
4809 : }
4810 :
4811 : /* -------------------------------------------------------------------- */
4812 : /* Compute geotransform */
4813 : /* -------------------------------------------------------------------- */
4814 134 : OGRSpatialReference* poSRSGeog = oSRS.CloneGeogCS();
4815 :
4816 : /* Files found at http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ */
4817 : /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, long) as required by the */
4818 : /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is able to understand that, */
4819 : /* so let's also try to do it with a heuristics. */
4820 :
4821 134 : int bReproject = TRUE;
4822 998 : if (oSRS.IsProjected() &&
4823 432 : (fabs(adfGPTS[0]) > 91 || fabs(adfGPTS[2]) > 91 || fabs(adfGPTS[4]) > 91 || fabs(adfGPTS[6]) > 91 ||
4824 432 : fabs(adfGPTS[1]) > 361 || fabs(adfGPTS[3]) > 361 || fabs(adfGPTS[5]) > 361 || fabs(adfGPTS[7]) > 361))
4825 : {
4826 0 : CPLDebug("PDF", "GPTS coordinates seems to be in (northing, easting), which is non-standard");
4827 0 : bReproject = FALSE;
4828 : }
4829 :
4830 134 : OGRCoordinateTransformation* poCT = NULL;
4831 134 : if (bReproject)
4832 : {
4833 134 : poCT = OGRCreateCoordinateTransformation( poSRSGeog, &oSRS);
4834 134 : if (poCT == NULL)
4835 : {
4836 0 : delete poSRSGeog;
4837 0 : CPLFree(pszWKT);
4838 0 : pszWKT = NULL;
4839 0 : return FALSE;
4840 : }
4841 : }
4842 :
4843 : GDAL_GCP asGCPS[4];
4844 :
4845 : /* Create NEATLINE */
4846 134 : poNeatLine = new OGRPolygon();
4847 268 : OGRLinearRing* poRing = new OGRLinearRing();
4848 134 : poNeatLine->addRingDirectly(poRing);
4849 :
4850 670 : for(i=0;i<4;i++)
4851 : {
4852 : /* We probably assume LPTS is 0 or 1 */
4853 536 : asGCPS[i].dfGCPPixel = (dfULX * (1 - adfLPTS[2*i+0]) + dfLRX * adfLPTS[2*i+0]) / dfMediaBoxWidth * nRasterXSize;
4854 536 : asGCPS[i].dfGCPLine = (dfULY * (1 - adfLPTS[2*i+1]) + dfLRY * adfLPTS[2*i+1]) / dfMediaBoxHeight * nRasterYSize;
4855 :
4856 536 : double lat = adfGPTS[2*i], lon = adfGPTS[2*i+1];
4857 536 : double x = lon, y = lat;
4858 536 : if (bReproject)
4859 : {
4860 536 : if (!poCT->Transform(1, &x, &y, NULL))
4861 : {
4862 : CPLError(CE_Failure, CPLE_AppDefined,
4863 0 : "Cannot reproject (%f, %f)", lon, lat);
4864 0 : delete poSRSGeog;
4865 0 : delete poCT;
4866 0 : CPLFree(pszWKT);
4867 0 : pszWKT = NULL;
4868 0 : return FALSE;
4869 : }
4870 : }
4871 :
4872 536 : x = ROUND_TO_INT_IF_CLOSE(x);
4873 536 : y = ROUND_TO_INT_IF_CLOSE(y);
4874 :
4875 536 : asGCPS[i].dfGCPX = x;
4876 536 : asGCPS[i].dfGCPY = y;
4877 :
4878 536 : poRing->addPoint(x, y);
4879 : }
4880 :
4881 134 : delete poSRSGeog;
4882 134 : delete poCT;
4883 :
4884 134 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
4885 : adfGeoTransform, FALSE))
4886 : {
4887 0 : CPLDebug("PDF", "Could not compute GT with exact match. Try with approximate");
4888 0 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
4889 : adfGeoTransform, TRUE))
4890 : {
4891 : CPLError(CE_Failure, CPLE_AppDefined,
4892 0 : "Could not compute GT with approximate match.");
4893 0 : return FALSE;
4894 : }
4895 : }
4896 134 : bGeoTransformValid = TRUE;
4897 :
4898 : /* If the non scaling terms of the geotransform are significantly smaller than */
4899 : /* the pixel size, then nullify them as being just artifacts of reprojection and */
4900 : /* GDALGCPsToGeoTransform() numerical imprecisions */
4901 134 : double dfPixelSize = MIN(fabs(adfGeoTransform[1]), fabs(adfGeoTransform[5]));
4902 134 : double dfRotationShearTerm = MAX(fabs(adfGeoTransform[2]), fabs(adfGeoTransform[4]));
4903 134 : if (dfRotationShearTerm < 1e-5 * dfPixelSize)
4904 : {
4905 134 : double dfLRX = adfGeoTransform[0] + nRasterXSize * adfGeoTransform[1] + nRasterYSize * adfGeoTransform[2];
4906 134 : double dfLRY = adfGeoTransform[3] + nRasterXSize * adfGeoTransform[4] + nRasterYSize * adfGeoTransform[5];
4907 134 : adfGeoTransform[1] = (dfLRX - adfGeoTransform[0]) / nRasterXSize;
4908 134 : adfGeoTransform[5] = (dfLRY - adfGeoTransform[3]) / nRasterYSize;
4909 134 : adfGeoTransform[2] = adfGeoTransform[4] = 0;
4910 : }
4911 :
4912 134 : return TRUE;
4913 : }
4914 :
4915 : /************************************************************************/
4916 : /* GetProjectionRef() */
4917 : /************************************************************************/
4918 :
4919 47 : const char* PDFDataset::GetProjectionRef()
4920 : {
4921 47 : if (pszWKT && bGeoTransformValid)
4922 44 : return pszWKT;
4923 3 : return "";
4924 : }
4925 :
4926 : /************************************************************************/
4927 : /* GetGeoTransform() */
4928 : /************************************************************************/
4929 :
4930 40 : CPLErr PDFDataset::GetGeoTransform( double * padfTransform )
4931 :
4932 : {
4933 40 : memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
4934 :
4935 40 : return( (bGeoTransformValid) ? CE_None : CE_Failure );
4936 : }
4937 :
4938 : /************************************************************************/
4939 : /* SetProjection() */
4940 : /************************************************************************/
4941 :
4942 6 : CPLErr PDFDataset::SetProjection(const char* pszWKTIn)
4943 : {
4944 6 : CPLFree(pszWKT);
4945 6 : pszWKT = pszWKTIn ? CPLStrdup(pszWKTIn) : CPLStrdup("");
4946 6 : bProjDirty = TRUE;
4947 6 : return CE_None;
4948 : }
4949 :
4950 : /************************************************************************/
4951 : /* SetGeoTransform() */
4952 : /************************************************************************/
4953 :
4954 4 : CPLErr PDFDataset::SetGeoTransform(double* padfGeoTransform)
4955 : {
4956 4 : memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double));
4957 4 : bGeoTransformValid = TRUE;
4958 4 : bProjDirty = TRUE;
4959 :
4960 : /* Reset NEATLINE if not explicitely set by the user */
4961 4 : if (!bNeatLineDirty)
4962 4 : SetMetadataItem("NEATLINE", NULL);
4963 4 : return CE_None;
4964 : }
4965 :
4966 : /************************************************************************/
4967 : /* GetMetadata() */
4968 : /************************************************************************/
4969 :
4970 28 : char **PDFDataset::GetMetadata( const char * pszDomain )
4971 : {
4972 28 : if( pszDomain != NULL && EQUAL(pszDomain, "LAYERS_WITH_REF") )
4973 : {
4974 2 : return osLayerWithRefList.List(); /* Used by OGR driver */
4975 : }
4976 26 : else if( pszDomain != NULL && EQUAL(pszDomain, "EMBEDDED_METADATA") )
4977 : {
4978 0 : char** papszRet = oMDMD.GetMetadata(pszDomain);
4979 0 : if( papszRet )
4980 0 : return papszRet;
4981 :
4982 0 : GDALPDFObject* poCatalog = GetCatalog();
4983 0 : if( poCatalog == NULL )
4984 0 : return NULL;
4985 0 : GDALPDFObject* poFirstElt = poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]");
4986 0 : GDALPDFObject* poF = poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F");
4987 :
4988 0 : if( poFirstElt == NULL || poFirstElt->GetType() != PDFObjectType_String ||
4989 0 : poFirstElt->GetString() != "Metadata" )
4990 0 : return NULL;
4991 0 : if( poF == NULL || poF->GetType() != PDFObjectType_Dictionary )
4992 0 : return NULL;
4993 0 : GDALPDFStream* poStream = poF->GetStream();
4994 0 : if( poStream == NULL )
4995 0 : return NULL;
4996 :
4997 0 : char* apszMetadata[2] = { NULL, NULL };
4998 0 : apszMetadata[0] = poStream->GetBytes();
4999 0 : oMDMD.SetMetadata(apszMetadata, pszDomain);
5000 0 : VSIFree(apszMetadata[0]);
5001 : }
5002 :
5003 26 : return oMDMD.GetMetadata(pszDomain);
5004 : }
5005 :
5006 : /************************************************************************/
5007 : /* SetMetadata() */
5008 : /************************************************************************/
5009 :
5010 21 : CPLErr PDFDataset::SetMetadata( char ** papszMetadata,
5011 : const char * pszDomain )
5012 : {
5013 21 : if (pszDomain == NULL || EQUAL(pszDomain, ""))
5014 : {
5015 0 : if (CSLFindString(papszMetadata, "NEATLINE") != -1)
5016 : {
5017 0 : bProjDirty = TRUE;
5018 0 : bNeatLineDirty = TRUE;
5019 : }
5020 0 : bInfoDirty = TRUE;
5021 : }
5022 21 : else if (EQUAL(pszDomain, "xml:XMP"))
5023 20 : bXMPDirty = TRUE;
5024 21 : return oMDMD.SetMetadata(papszMetadata, pszDomain);
5025 : }
5026 :
5027 : /************************************************************************/
5028 : /* GetMetadataItem() */
5029 : /************************************************************************/
5030 :
5031 1790 : const char *PDFDataset::GetMetadataItem( const char * pszName,
5032 : const char * pszDomain )
5033 : {
5034 1790 : if ( (pszDomain == NULL || EQUAL(pszDomain, "")) && EQUAL(pszName, "PDF_PAGE_OBJECT") )
5035 : {
5036 6 : return CPLSPrintf("%p", poPageObj);
5037 : }
5038 1784 : if ( (pszDomain == NULL || EQUAL(pszDomain, "")) && EQUAL(pszName, "PDF_CATALOG_OBJECT") )
5039 : {
5040 6 : return CPLSPrintf("%p", GetCatalog());
5041 : }
5042 :
5043 1778 : return oMDMD.GetMetadataItem(pszName, pszDomain);
5044 : }
5045 :
5046 : /************************************************************************/
5047 : /* SetMetadataItem() */
5048 : /************************************************************************/
5049 :
5050 569 : CPLErr PDFDataset::SetMetadataItem( const char * pszName,
5051 : const char * pszValue,
5052 : const char * pszDomain )
5053 : {
5054 1118 : if (pszDomain == NULL || EQUAL(pszDomain, ""))
5055 : {
5056 549 : if (EQUAL(pszName, "NEATLINE"))
5057 : {
5058 162 : bProjDirty = TRUE;
5059 162 : bNeatLineDirty = TRUE;
5060 : }
5061 : else
5062 : {
5063 387 : if (pszValue == NULL)
5064 2 : pszValue = "";
5065 387 : bInfoDirty = TRUE;
5066 : }
5067 : }
5068 20 : else if (EQUAL(pszDomain, "xml:XMP"))
5069 0 : bXMPDirty = TRUE;
5070 569 : return oMDMD.SetMetadataItem(pszName, pszValue, pszDomain);
5071 : }
5072 :
5073 : /************************************************************************/
5074 : /* GetGCPCount() */
5075 : /************************************************************************/
5076 :
5077 18 : int PDFDataset::GetGCPCount()
5078 : {
5079 18 : return nGCPCount;
5080 : }
5081 :
5082 : /************************************************************************/
5083 : /* GetGCPProjection() */
5084 : /************************************************************************/
5085 :
5086 5 : const char * PDFDataset::GetGCPProjection()
5087 : {
5088 5 : if (pszWKT != NULL && nGCPCount != 0)
5089 3 : return pszWKT;
5090 2 : return "";
5091 : }
5092 :
5093 : /************************************************************************/
5094 : /* GetGCPs() */
5095 : /************************************************************************/
5096 :
5097 5 : const GDAL_GCP * PDFDataset::GetGCPs()
5098 : {
5099 5 : return pasGCPList;
5100 : }
5101 :
5102 : /************************************************************************/
5103 : /* SetGCPs() */
5104 : /************************************************************************/
5105 :
5106 2 : CPLErr PDFDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
5107 : const char *pszGCPProjectionIn )
5108 : {
5109 : const char* pszGEO_ENCODING =
5110 2 : CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
5111 2 : if( nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000"))
5112 : {
5113 : CPLError(CE_Failure, CPLE_NotSupported,
5114 : "PDF driver only supports writing 4 GCPs when "
5115 0 : "GDAL_PDF_GEO_ENCODING=ISO32000.");
5116 0 : return CE_Failure;
5117 : }
5118 :
5119 : /* Free previous GCPs */
5120 2 : GDALDeinitGCPs( nGCPCount, pasGCPList );
5121 2 : CPLFree( pasGCPList );
5122 :
5123 : /* Duplicate in GCPs */
5124 2 : nGCPCount = nGCPCountIn;
5125 2 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
5126 :
5127 2 : CPLFree(pszWKT);
5128 2 : pszWKT = CPLStrdup(pszGCPProjectionIn);
5129 :
5130 2 : bProjDirty = TRUE;
5131 :
5132 : /* Reset NEATLINE if not explicitely set by the user */
5133 2 : if (!bNeatLineDirty)
5134 2 : SetMetadataItem("NEATLINE", NULL);
5135 :
5136 2 : return CE_None;
5137 : }
5138 :
5139 : #endif // #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
5140 :
5141 :
5142 : /************************************************************************/
5143 : /* GDALPDFOpen() */
5144 : /************************************************************************/
5145 :
5146 6 : GDALDataset* GDALPDFOpen(const char* pszFilename, GDALAccess eAccess)
5147 : {
5148 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
5149 6 : GDALOpenInfo oOpenInfo(pszFilename, eAccess);
5150 6 : return PDFDataset::Open(&oOpenInfo);
5151 : #else
5152 : return NULL;
5153 : #endif
5154 : }
5155 :
5156 : /************************************************************************/
5157 : /* GDALPDFUnloadDriver() */
5158 : /************************************************************************/
5159 :
5160 550 : static void GDALPDFUnloadDriver(GDALDriver * poDriver)
5161 : {
5162 : #ifdef HAVE_POPPLER
5163 550 : if( hGlobalParamsMutex != NULL )
5164 1 : CPLDestroyMutex(hGlobalParamsMutex);
5165 : #endif
5166 550 : }
5167 :
5168 : /************************************************************************/
5169 : /* GDALRegister_PDF() */
5170 : /************************************************************************/
5171 :
5172 610 : void GDALRegister_PDF()
5173 :
5174 : {
5175 : GDALDriver *poDriver;
5176 :
5177 610 : if (! GDAL_CHECK_VERSION("PDF driver"))
5178 0 : return;
5179 :
5180 610 : if( GDALGetDriverByName( "PDF" ) == NULL )
5181 : {
5182 588 : poDriver = new GDALDriver();
5183 :
5184 588 : poDriver->SetDescription( "PDF" );
5185 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
5186 588 : "Geospatial PDF" );
5187 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
5188 588 : "frmt_pdf.html" );
5189 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pdf" );
5190 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
5191 588 : "Byte" );
5192 588 : poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
5193 : #ifdef HAVE_POPPLER
5194 588 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
5195 588 : poDriver->SetMetadataItem( "HAVE_POPPLER", "YES" );
5196 : #endif
5197 : #ifdef HAVE_PODOFO
5198 588 : poDriver->SetMetadataItem( "HAVE_PODOFO", "YES" );
5199 : #endif
5200 :
5201 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
5202 : "<CreationOptionList>\n"
5203 : " <Option name='COMPRESS' type='string-select' description='Compression method for raster data' default='DEFLATE'>\n"
5204 : " <Value>NONE</Value>\n"
5205 : " <Value>DEFLATE</Value>\n"
5206 : " <Value>JPEG</Value>\n"
5207 : " <Value>JPEG2000</Value>\n"
5208 : " </Option>\n"
5209 : " <Option name='STREAM_COMPRESS' type='string-select' description='Compression method for stream objects' default='DEFLATE'>\n"
5210 : " <Value>NONE</Value>\n"
5211 : " <Value>DEFLATE</Value>\n"
5212 : " </Option>\n"
5213 : " <Option name='GEO_ENCODING' type='string-select' description='Format of geo-encoding' default='ISO32000'>\n"
5214 : " <Value>NONE</Value>\n"
5215 : " <Value>ISO32000</Value>\n"
5216 : " <Value>OGC_BP</Value>\n"
5217 : " <Value>BOTH</Value>\n"
5218 : " </Option>\n"
5219 : " <Option name='NEATLINE' type='string' description='Neatline'/>\n"
5220 : " <Option name='DPI' type='float' description='DPI' default='72'/>\n"
5221 : " <Option name='PREDICTOR' type='int' description='Predictor Type (for DEFLATE compression)'/>\n"
5222 : " <Option name='JPEG_QUALITY' type='int' description='JPEG quality 1-100' default='75'/>\n"
5223 : " <Option name='JPEG2000_DRIVER' type='string'/>\n"
5224 : " <Option name='TILED' type='boolean' description='Switch to tiled format' default='NO'/>\n"
5225 : " <Option name='BLOCKXSIZE' type='int' description='Block Width'/>\n"
5226 : " <Option name='BLOCKYSIZE' type='int' description='Block Height'/>\n"
5227 : " <Option name='LAYER_NAME' type='string' description='Layer name for raster content'/>\n"
5228 : " <Option name='CLIPPING_EXTENT' type='string' description='Clipping extent for main and extra rasters. Format: xmin,ymin,xmax,ymax'/>\n"
5229 : " <Option name='EXTRA_RASTERS' type='string' description='List of extra (georeferenced) rasters.'/>\n"
5230 : " <Option name='EXTRA_RASTERS_LAYER_NAME' type='string' description='List of layer names for the extra (georeferenced) rasters.'/>\n"
5231 : " <Option name='EXTRA_STREAM' type='string' description='Extra data to insert into the page content stream'/>\n"
5232 : " <Option name='EXTRA_IMAGES' type='string' description='List of image_file_name,x,y,scale[,link=some_url] (possibly repeated)'/>\n"
5233 : " <Option name='EXTRA_LAYER_NAME' type='string' description='Layer name for extra content'/>\n"
5234 : " <Option name='MARGIN' type='int' description='Margin around image in user units'/>\n"
5235 : " <Option name='LEFT_MARGIN' type='int' description='Left margin in user units'/>\n"
5236 : " <Option name='RIGHT_MARGIN' type='int' description='Right margin in user units'/>\n"
5237 : " <Option name='TOP_MARGIN' type='int' description='Top margin in user units'/>\n"
5238 : " <Option name='BOTTOM_MARGIN' type='int' description='Bottom margin in user units'/>\n"
5239 : " <Option name='OGR_DATASOURCE' type='string' description='Name of OGR datasource to display on top of the raster layer'/>\n"
5240 : " <Option name='OGR_DISPLAY_FIELD' type='string' description='Name of field to use as the display field in the feature tree'/>\n"
5241 : " <Option name='OGR_DISPLAY_LAYER_NAMES' type='string' description='Comma separated list of OGR layer names to display in the feature tree'/>\n"
5242 : " <Option name='OGR_WRITE_ATTRIBUTES' type='boolean' description='Whether to write attributes of OGR features' default='YES'/>\n"
5243 : " <Option name='OGR_LINK_FIELD' type='string' description='Name of field to use as the URL field to make objects clickable.'/>\n"
5244 : " <Option name='XMP' type='string' description='xml:XMP metadata'/>\n"
5245 : " <Option name='WRITE_INFO' type='boolean' description='to control whether a Info block must be written' default='YES'/>\n"
5246 : " <Option name='AUTHOR' type='string'/>\n"
5247 : " <Option name='CREATOR' type='string'/>\n"
5248 : " <Option name='CREATION_DATE' type='string'/>\n"
5249 : " <Option name='KEYWORDS' type='string'/>\n"
5250 : " <Option name='PRODUCER' type='string'/>\n"
5251 : " <Option name='SUBJECT' type='string'/>\n"
5252 : " <Option name='TITLE' type='string'/>\n"
5253 : " <Option name='OFF_LAYERS' type='string' description='Comma separated list of layer names that should be initially hidden'/>\n"
5254 : " <Option name='EXCLUSIVE_LAYERS' type='string' description='Comma separated list of layer names, such that only one of those layers can be ON at a time.'/>\n"
5255 : " <Option name='JAVASCRIPT' type='string' description='Javascript script to embed and run at file opening'/>\n"
5256 : " <Option name='JAVASCRIPT_FILE' type='string' description='Filename of the Javascript script to embed and run at file opening'/>\n"
5257 588 : "</CreationOptionList>\n" );
5258 :
5259 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
5260 588 : poDriver->pfnOpen = PDFDataset::Open;
5261 588 : poDriver->pfnIdentify = PDFDataset::Identify;
5262 : #endif
5263 :
5264 588 : poDriver->pfnCreateCopy = GDALPDFCreateCopy;
5265 588 : poDriver->pfnUnloadDriver = GDALPDFUnloadDriver;
5266 :
5267 588 : GetGDALDriverManager()->RegisterDriver( poDriver );
5268 : }
5269 2274 : }
5270 :
|