LCOV - code coverage report
Current view: directory - frmts/pdf - pdfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 1798 1150 64.0 %
Date: 2012-04-28 Functions: 76 52 68.4 %

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

Generated by: LCOV version 1.7