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