LCOV - code coverage report
Current view: directory - frmts/pdf - pdfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2180 1476 67.7 %
Date: 2012-12-26 Functions: 90 63 70.0 %

       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                 : 

Generated by: LCOV version 1.7