LCOV - code coverage report
Current view: directory - frmts/pdf - pdfcreatecopy.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 1714 1510 88.1 %
Date: 2012-04-28 Functions: 39 37 94.9 %

       1                 : /******************************************************************************
       2                 :  * $Id: pdfcreatecopy.cpp 24189 2012-04-01 14:20:02Z 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) 2012, 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 "pdfcreatecopy.h"
      35                 : 
      36                 : #include "cpl_vsi_virtual.h"
      37                 : #include "cpl_conv.h"
      38                 : #include "cpl_error.h"
      39                 : #include "ogr_spatialref.h"
      40                 : #include "ogr_geometry.h"
      41                 : #include "vrt/vrtdataset.h"
      42                 : 
      43                 : #include "pdfobject.h"
      44                 : 
      45                 : #ifndef M_PI
      46                 : #define M_PI       3.14159265358979323846
      47                 : #endif
      48                 : 
      49                 : CPL_CVSID("$Id: pdfcreatecopy.cpp 24189 2012-04-01 14:20:02Z rouault $");
      50                 : 
      51                 : #define PIXEL_TO_GEO_X(x,y) adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2]
      52                 : #define PIXEL_TO_GEO_Y(x,y) adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5]
      53                 : 
      54                 : class GDALFakePDFDataset : public GDALDataset
      55                 : {
      56                 :     public:
      57                 :         GDALFakePDFDataset() {}
      58                 : };
      59                 : 
      60                 : /************************************************************************/
      61                 : /*                             Init()                                   */
      62                 : /************************************************************************/
      63                 : 
      64             142 : void GDALPDFWriter::Init()
      65                 : {
      66             142 :     nPageResourceId = 0;
      67             142 :     nStructTreeRootId = 0;
      68             142 :     nCatalogId = nCatalogGen = 0;
      69             142 :     bInWriteObj = FALSE;
      70             142 :     nInfoId = nInfoGen = 0;
      71             142 :     nXMPId = nXMPGen = 0;
      72                 : 
      73             142 :     nLastStartXRef = 0;
      74             142 :     nLastXRefSize = 0;
      75             142 :     bCanUpdate = FALSE;
      76             142 : }
      77                 : 
      78                 : /************************************************************************/
      79                 : /*                         GDALPDFWriter()                              */
      80                 : /************************************************************************/
      81                 : 
      82             142 : GDALPDFWriter::GDALPDFWriter(VSILFILE* fpIn, int bAppend) : fp(fpIn)
      83                 : {
      84             142 :     Init();
      85                 : 
      86             142 :     if (!bAppend)
      87                 :     {
      88              92 :         VSIFPrintfL(fp, "%%PDF-1.6\n");
      89                 : 
      90                 :         /* See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate that the content will be binary */
      91              92 :         VSIFPrintfL(fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF);
      92                 : 
      93              92 :         nPageResourceId = AllocNewObject();
      94              92 :         nCatalogId = AllocNewObject();
      95                 :     }
      96             142 : }
      97                 : 
      98                 : /************************************************************************/
      99                 : /*                         ~GDALPDFWriter()                             */
     100                 : /************************************************************************/
     101                 : 
     102             142 : GDALPDFWriter::~GDALPDFWriter()
     103                 : {
     104             142 :     Close();
     105             142 : }
     106                 : 
     107                 : /************************************************************************/
     108                 : /*                          ParseIndirectRef()                          */
     109                 : /************************************************************************/
     110                 : 
     111              58 : static int ParseIndirectRef(const char* pszStr, int& nNum, int &nGen)
     112                 : {
     113             116 :     while(*pszStr == ' ')
     114               0 :         pszStr ++;
     115                 : 
     116              58 :     nNum = atoi(pszStr);
     117             182 :     while(*pszStr >= '0' && *pszStr <= '9')
     118              66 :         pszStr ++;
     119              58 :     if (*pszStr != ' ')
     120               0 :         return FALSE;
     121                 : 
     122             174 :     while(*pszStr == ' ')
     123              58 :         pszStr ++;
     124                 : 
     125              58 :     nGen = atoi(pszStr);
     126             174 :     while(*pszStr >= '0' && *pszStr <= '9')
     127              58 :         pszStr ++;
     128              58 :     if (*pszStr != ' ')
     129               0 :         return FALSE;
     130                 : 
     131             174 :     while(*pszStr == ' ')
     132              58 :         pszStr ++;
     133                 : 
     134              58 :     return *pszStr == 'R';
     135                 : }
     136                 : 
     137                 : /************************************************************************/
     138                 : /*                       ParseTrailerAndXRef()                          */
     139                 : /************************************************************************/
     140                 : 
     141              50 : int GDALPDFWriter::ParseTrailerAndXRef()
     142                 : {
     143              50 :     VSIFSeekL(fp, 0, SEEK_END);
     144                 :     char szBuf[1024+1];
     145              50 :     vsi_l_offset nOffset = VSIFTellL(fp);
     146                 : 
     147              50 :     if (nOffset > 128)
     148              50 :         nOffset -= 128;
     149                 :     else
     150               0 :         nOffset = 0;
     151                 : 
     152                 :     /* Find startxref section */
     153              50 :     VSIFSeekL(fp, nOffset, SEEK_SET);
     154              50 :     int nRead = VSIFReadL(szBuf, 1, 128, fp);
     155              50 :     szBuf[nRead] = 0;
     156              50 :     if (nRead < 9)
     157               0 :         return FALSE;
     158                 : 
     159              50 :     const char* pszStartXRef = NULL;
     160                 :     int i;
     161             646 :     for(i = nRead - 9; i>= 0; i --)
     162                 :     {
     163             646 :         if (strncmp(szBuf + i, "startxref", 9) == 0)
     164                 :         {
     165              50 :             pszStartXRef = szBuf + i;
     166              50 :             break;
     167                 :         }
     168                 :     }
     169              50 :     if (pszStartXRef == NULL)
     170                 :     {
     171               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref");
     172               0 :         return FALSE;
     173                 :     }
     174              50 :     pszStartXRef += 9;
     175             150 :     while(*pszStartXRef == '\r' || *pszStartXRef == '\n')
     176              50 :         pszStartXRef ++;
     177              50 :     if (*pszStartXRef == '\0')
     178                 :     {
     179               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref");
     180               0 :         return FALSE;
     181                 :     }
     182                 : 
     183              50 :     nLastStartXRef = atoi(pszStartXRef);
     184                 : 
     185                 :     /* Skip to beginning of xref section */
     186              50 :     VSIFSeekL(fp, nLastStartXRef, SEEK_SET);
     187                 : 
     188                 :     /* And skip to trailer */
     189                 :     const char* pszLine;
     190              50 :     while( (pszLine = CPLReadLineL(fp)) != NULL)
     191                 :     {
     192             522 :         if (strncmp(pszLine, "trailer", 7) == 0)
     193              50 :             break;
     194                 :     }
     195                 : 
     196              50 :     if( pszLine == NULL )
     197                 :     {
     198               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer");
     199               0 :         return FALSE;
     200                 :     }
     201                 : 
     202                 :     /* Read trailer content */
     203              50 :     nRead = VSIFReadL(szBuf, 1, 1024, fp);
     204              50 :     szBuf[nRead] = 0;
     205                 : 
     206                 :     /* Find XRef size */
     207              50 :     const char* pszSize = strstr(szBuf, "/Size");
     208              50 :     if (pszSize == NULL)
     209                 :     {
     210               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Size");
     211               0 :         return FALSE;
     212                 :     }
     213              50 :     pszSize += 5;
     214             150 :     while(*pszSize == ' ')
     215              50 :         pszSize ++;
     216              50 :     nLastXRefSize = atoi(pszSize);
     217                 : 
     218                 :     /* Find Root object */
     219              50 :     const char* pszRoot = strstr(szBuf, "/Root");
     220              50 :     if (pszRoot == NULL)
     221                 :     {
     222               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Root");
     223               0 :         return FALSE;
     224                 :     }
     225              50 :     pszRoot += 5;
     226             150 :     while(*pszRoot == ' ')
     227              50 :         pszRoot ++;
     228                 : 
     229              50 :     if (!ParseIndirectRef(pszRoot, nCatalogId, nCatalogGen))
     230                 :     {
     231               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Root");
     232               0 :         return FALSE;
     233                 :     }
     234                 : 
     235                 :     /* Find Info object */
     236              50 :     const char* pszInfo = strstr(szBuf, "/Info");
     237              50 :     if (pszInfo != NULL)
     238                 :     {
     239               8 :         pszInfo += 5;
     240              24 :         while(*pszInfo == ' ')
     241               8 :             pszInfo ++;
     242                 : 
     243               8 :         if (!ParseIndirectRef(pszInfo, nInfoId, nInfoGen))
     244                 :         {
     245               0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Info");
     246               0 :             nInfoId = nInfoGen = 0;
     247                 :         }
     248                 :     }
     249                 : 
     250              50 :     VSIFSeekL(fp, 0, SEEK_END);
     251                 : 
     252              50 :     return TRUE;
     253                 : }
     254                 : 
     255                 : /************************************************************************/
     256                 : /*                              Close()                                 */
     257                 : /************************************************************************/
     258                 : 
     259             284 : void GDALPDFWriter::Close()
     260                 : {
     261             284 :     if (fp)
     262                 :     {
     263             142 :         CPLAssert(!bInWriteObj);
     264             142 :         if (nPageResourceId)
     265                 :         {
     266              92 :             WritePages();
     267              92 :             WriteXRefTableAndTrailer();
     268                 :         }
     269              50 :         else if (bCanUpdate)
     270                 :         {
     271              44 :             WriteXRefTableAndTrailer();
     272                 :         }
     273             142 :         VSIFCloseL(fp);
     274                 :     }
     275             284 :     fp = NULL;
     276             284 : }
     277                 : 
     278                 : /************************************************************************/
     279                 : /*                           UpdateProj()                               */
     280                 : /************************************************************************/
     281                 : 
     282              20 : void GDALPDFWriter::UpdateProj(GDALDataset* poSrcDS,
     283                 :                                double dfDPI,
     284                 :                                GDALPDFDictionaryRW* poPageDict,
     285                 :                                int nPageNum, int nPageGen)
     286                 : {
     287              20 :     bCanUpdate = TRUE;
     288              20 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     289              20 :         asXRefEntries.resize(nLastXRefSize - 1);
     290                 : 
     291              20 :     int nViewportId = 0;
     292              20 :     int nLGIDictId = 0;
     293                 : 
     294              20 :     CPLAssert(nPageNum != 0);
     295              20 :     CPLAssert(poPageDict != NULL);
     296                 : 
     297              20 :     PDFMargins sMargins = {0, 0, 0, 0};
     298                 : 
     299              20 :     const char* pszGEO_ENCODING = CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
     300              20 :     if (EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH"))
     301              18 :         nViewportId = WriteSRS_ISO32000(poSrcDS, dfDPI / 72.0, NULL, &sMargins);
     302              20 :     if (EQUAL(pszGEO_ENCODING, "OGC_BP") || EQUAL(pszGEO_ENCODING, "BOTH"))
     303               2 :         nLGIDictId = WriteSRS_OGC_BP(poSrcDS, dfDPI / 72.0, NULL, &sMargins);
     304                 : 
     305                 : #ifdef invalidate_xref_entry
     306                 :     GDALPDFObject* poVP = poPageDict->Get("VP");
     307                 :     if (poVP)
     308                 :     {
     309                 :         if (poVP->GetType() == PDFObjectType_Array &&
     310                 :             poVP->GetArray()->GetLength() == 1)
     311                 :             poVP = poVP->GetArray()->Get(0);
     312                 : 
     313                 :         int nVPId = poVP->GetRefNum();
     314                 :         if (nVPId)
     315                 :         {
     316                 :             asXRefEntries[nVPId - 1].bFree = TRUE;
     317                 :             asXRefEntries[nVPId - 1].nGen ++;
     318                 :         }
     319                 :     }
     320                 : #endif
     321                 : 
     322              20 :     poPageDict->Remove("VP");
     323              20 :     poPageDict->Remove("LGIDict");
     324                 : 
     325              20 :     if (nViewportId)
     326                 :     {
     327                 :         poPageDict->Add("VP", &((new GDALPDFArrayRW())->
     328              14 :                 Add(nViewportId, 0)));
     329                 :     }
     330                 : 
     331              20 :     if (nLGIDictId)
     332                 :     {
     333               2 :         poPageDict->Add("LGIDict", nLGIDictId, 0);
     334                 :     }
     335                 : 
     336              20 :     StartObj(nPageNum, nPageGen);
     337              20 :     VSIFPrintfL(fp, "%s\n", poPageDict->Serialize().c_str());
     338              20 :     EndObj();
     339              20 : }
     340                 : 
     341                 : /************************************************************************/
     342                 : /*                           UpdateInfo()                               */
     343                 : /************************************************************************/
     344                 : 
     345              12 : void GDALPDFWriter::UpdateInfo(GDALDataset* poSrcDS)
     346                 : {
     347              12 :     bCanUpdate = TRUE;
     348              12 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     349              12 :         asXRefEntries.resize(nLastXRefSize - 1);
     350                 : 
     351              12 :     int nNewInfoId = SetInfo(poSrcDS, NULL);
     352                 :     /* Write empty info, because podofo driver will find the dangling info instead */
     353              12 :     if (nNewInfoId == 0 && nInfoId != 0)
     354                 :     {
     355                 : #ifdef invalidate_xref_entry
     356                 :         asXRefEntries[nInfoId - 1].bFree = TRUE;
     357                 :         asXRefEntries[nInfoId - 1].nGen ++;
     358                 : #else
     359               4 :         StartObj(nInfoId, nInfoGen);
     360               4 :         VSIFPrintfL(fp, "<< >>\n");
     361               4 :         EndObj();
     362                 : #endif
     363                 :     }
     364              12 : }
     365                 : 
     366                 : /************************************************************************/
     367                 : /*                           UpdateXMP()                                */
     368                 : /************************************************************************/
     369                 : 
     370              12 : void GDALPDFWriter::UpdateXMP(GDALDataset* poSrcDS,
     371                 :                               GDALPDFDictionaryRW* poCatalogDict)
     372                 : {
     373              12 :     bCanUpdate = TRUE;
     374              12 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     375              12 :         asXRefEntries.resize(nLastXRefSize - 1);
     376                 : 
     377              12 :     CPLAssert(nCatalogId != 0);
     378              12 :     CPLAssert(poCatalogDict != NULL);
     379                 : 
     380              12 :     GDALPDFObject* poMetadata = poCatalogDict->Get("Metadata");
     381              12 :     if (poMetadata)
     382                 :     {
     383               8 :         nXMPId = poMetadata->GetRefNum();
     384               8 :         nXMPGen = poMetadata->GetRefGen();
     385                 :     }
     386                 : 
     387              12 :     poCatalogDict->Remove("Metadata");
     388              12 :     int nNewXMPId = SetXMP(poSrcDS, NULL);
     389                 : 
     390                 :     /* Write empty metadata, because podofo driver will find the dangling info instead */
     391              12 :     if (nNewXMPId == 0 && nXMPId != 0)
     392                 :     {
     393               4 :         StartObj(nXMPId, nXMPGen);
     394               4 :         VSIFPrintfL(fp, "<< >>\n");
     395               4 :         EndObj();
     396                 :     }
     397                 : 
     398              12 :     if (nXMPId)
     399              12 :         poCatalogDict->Add("Metadata", nXMPId, 0);
     400                 : 
     401              12 :     StartObj(nCatalogId, nCatalogGen);
     402              12 :     VSIFPrintfL(fp, "%s\n", poCatalogDict->Serialize().c_str());
     403              12 :     EndObj();
     404              12 : }
     405                 : 
     406                 : /************************************************************************/
     407                 : /*                           AllocNewObject()                           */
     408                 : /************************************************************************/
     409                 : 
     410            1162 : int GDALPDFWriter::AllocNewObject()
     411                 : {
     412            1162 :     asXRefEntries.push_back(GDALXRefEntry());
     413            1162 :     return (int)asXRefEntries.size();
     414                 : }
     415                 : 
     416                 : /************************************************************************/
     417                 : /*                        WriteXRefTableAndTrailer()                    */
     418                 : /************************************************************************/
     419                 : 
     420             136 : void GDALPDFWriter::WriteXRefTableAndTrailer()
     421                 : {
     422             136 :     vsi_l_offset nOffsetXREF = VSIFTellL(fp);
     423             136 :     VSIFPrintfL(fp, "xref\n");
     424                 : 
     425                 :     char buffer[16];
     426             136 :     if (bCanUpdate)
     427                 :     {
     428              44 :         VSIFPrintfL(fp, "0 1\n");
     429              44 :         VSIFPrintfL(fp, "0000000000 65535 f \n");
     430             620 :         for(size_t i=0;i<asXRefEntries.size();)
     431                 :         {
     432             532 :             if (asXRefEntries[i].nOffset != 0 || asXRefEntries[i].bFree)
     433                 :             {
     434                 :                 /* Find number of consecutive objects */
     435              72 :                 size_t nCount = 1;
     436             172 :                 while(i + nCount <asXRefEntries.size() &&
     437                 :                     (asXRefEntries[i + nCount].nOffset != 0 || asXRefEntries[i + nCount].bFree))
     438              28 :                     nCount ++;
     439                 : 
     440              72 :                 VSIFPrintfL(fp, "%d %d\n", (int)i + 1, (int)nCount);
     441              72 :                 size_t iEnd = i + nCount;
     442             172 :                 for(; i < iEnd; i++)
     443                 :                 {
     444             100 :                     snprintf (buffer, sizeof(buffer), "%010ld", (long)asXRefEntries[i].nOffset);
     445                 :                     VSIFPrintfL(fp, "%s %05d %c \n",
     446                 :                                 buffer, asXRefEntries[i].nGen,
     447             100 :                                 asXRefEntries[i].bFree ? 'f' : 'n');
     448                 :                 }
     449                 :             }
     450                 :             else
     451                 :             {
     452             460 :                 i++;
     453                 :             }
     454                 :         }
     455                 :     }
     456                 :     else
     457                 :     {
     458                 :         VSIFPrintfL(fp, "%d %d\n",
     459              92 :                     0, (int)asXRefEntries.size() + 1);
     460              92 :         VSIFPrintfL(fp, "0000000000 65535 f \n");
     461            1202 :         for(size_t i=0;i<asXRefEntries.size();i++)
     462                 :         {
     463            1110 :             snprintf (buffer, sizeof(buffer), "%010ld", (long)asXRefEntries[i].nOffset);
     464            1110 :             VSIFPrintfL(fp, "%s %05d n \n", buffer, asXRefEntries[i].nGen);
     465                 :         }
     466                 :     }
     467                 : 
     468             136 :     VSIFPrintfL(fp, "trailer\n");
     469             136 :     GDALPDFDictionaryRW oDict;
     470                 :     oDict.Add("Size", (int)asXRefEntries.size() + 1)
     471             136 :          .Add("Root", nCatalogId, nCatalogGen);
     472             136 :     if (nInfoId)
     473              16 :         oDict.Add("Info", nInfoId, nInfoGen);
     474             136 :     if (nLastStartXRef)
     475              44 :         oDict.Add("Prev", nLastStartXRef);
     476             136 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
     477                 : 
     478                 :     VSIFPrintfL(fp,
     479                 :                 "startxref\n"
     480                 :                 "%ld\n"
     481                 :                 "%%%%EOF\n",
     482             136 :                 (long)nOffsetXREF);
     483             136 : }
     484                 : 
     485                 : /************************************************************************/
     486                 : /*                              StartObj()                              */
     487                 : /************************************************************************/
     488                 : 
     489            1210 : void GDALPDFWriter::StartObj(int nObjectId, int nGen)
     490                 : {
     491            1210 :     CPLAssert(!bInWriteObj);
     492            1210 :     CPLAssert(nObjectId - 1 < (int)asXRefEntries.size());
     493            1210 :     CPLAssert(asXRefEntries[nObjectId - 1].nOffset == 0);
     494            1210 :     asXRefEntries[nObjectId - 1].nOffset = VSIFTellL(fp);
     495            1210 :     asXRefEntries[nObjectId - 1].nGen = nGen;
     496            1210 :     VSIFPrintfL(fp, "%d %d obj\n", nObjectId, nGen);
     497            1210 :     bInWriteObj = TRUE;
     498            1210 : }
     499                 : 
     500                 : /************************************************************************/
     501                 : /*                               EndObj()                               */
     502                 : /************************************************************************/
     503                 : 
     504            1210 : void GDALPDFWriter::EndObj()
     505                 : {
     506            1210 :     CPLAssert(bInWriteObj);
     507            1210 :     VSIFPrintfL(fp, "endobj\n");
     508            1210 :     bInWriteObj = FALSE;
     509            1210 : }
     510                 : 
     511                 : 
     512                 : /************************************************************************/
     513                 : /*                         GDALPDFFind4Corners()                        */
     514                 : /************************************************************************/
     515                 : 
     516                 : static
     517              24 : void GDALPDFFind4Corners(const GDAL_GCP* pasGCPList,
     518                 :                          int& iUL, int& iUR, int& iLR, int& iLL)
     519                 : {
     520              24 :     double dfMeanX = 0, dfMeanY = 0;
     521                 :     int i;
     522                 : 
     523              24 :     iUL = 0;
     524              24 :     iUR = 0;
     525              24 :     iLR = 0;
     526              24 :     iLL = 0;
     527                 : 
     528             120 :     for(i = 0; i < 4; i++ )
     529                 :     {
     530              96 :         dfMeanX += pasGCPList[i].dfGCPPixel;
     531              96 :         dfMeanY += pasGCPList[i].dfGCPLine;
     532                 :     }
     533              24 :     dfMeanX /= 4;
     534              24 :     dfMeanY /= 4;
     535                 : 
     536             120 :     for(i = 0; i < 4; i++ )
     537                 :     {
     538             168 :         if (pasGCPList[i].dfGCPPixel < dfMeanX &&
     539              48 :             pasGCPList[i].dfGCPLine  < dfMeanY )
     540              24 :             iUL = i;
     541                 : 
     542             144 :         else if (pasGCPList[i].dfGCPPixel > dfMeanX &&
     543              48 :                     pasGCPList[i].dfGCPLine  < dfMeanY )
     544              24 :             iUR = i;
     545                 : 
     546              96 :         else if (pasGCPList[i].dfGCPPixel > dfMeanX &&
     547              24 :                     pasGCPList[i].dfGCPLine  > dfMeanY )
     548              24 :             iLR = i;
     549                 : 
     550              48 :         else if (pasGCPList[i].dfGCPPixel < dfMeanX &&
     551              24 :                     pasGCPList[i].dfGCPLine  > dfMeanY )
     552              24 :             iLL = i;
     553                 :     }
     554              24 : }
     555                 : 
     556                 : /************************************************************************/
     557                 : /*                         WriteSRS_ISO32000()                          */
     558                 : /************************************************************************/
     559                 : 
     560              94 : int  GDALPDFWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS,
     561                 :                                       double dfUserUnit,
     562                 :                                       const char* pszNEATLINE,
     563                 :                                       PDFMargins* psMargins)
     564                 : {
     565              94 :     int  nWidth = poSrcDS->GetRasterXSize();
     566              94 :     int  nHeight = poSrcDS->GetRasterYSize();
     567              94 :     const char* pszWKT = poSrcDS->GetProjectionRef();
     568                 :     double adfGeoTransform[6];
     569                 : 
     570              94 :     int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None);
     571              94 :     const GDAL_GCP* pasGCPList = (poSrcDS->GetGCPCount() == 4) ? poSrcDS->GetGCPs() : NULL;
     572              94 :     if (pasGCPList != NULL)
     573               2 :         pszWKT = poSrcDS->GetGCPProjection();
     574                 : 
     575              94 :     if( !bHasGT && pasGCPList == NULL )
     576               8 :         return 0;
     577                 : 
     578              86 :     if( pszWKT == NULL || EQUAL(pszWKT, "") )
     579               4 :         return 0;
     580                 : 
     581                 :     double adfGPTS[8];
     582                 : 
     583              82 :     double dfULPixel = 0;
     584              82 :     double dfULLine = 0;
     585              82 :     double dfLRPixel = nWidth;
     586              82 :     double dfLRLine = nHeight;
     587                 : 
     588                 :     GDAL_GCP asNeatLineGCPs[4];
     589              82 :     if (pszNEATLINE == NULL)
     590              80 :         pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE");
     591              82 :     if( bHasGT && pszNEATLINE != NULL && pszNEATLINE[0] != '\0' )
     592                 :     {
     593              10 :         OGRGeometry* poGeom = NULL;
     594              10 :         OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom );
     595              10 :         if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
     596                 :         {
     597              10 :             OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing();
     598                 :             double adfGeoTransformInv[6];
     599              10 :             if( poLS != NULL && poLS->getNumPoints() == 5 &&
     600                 :                 GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) )
     601                 :             {
     602              50 :                 for(int i=0;i<4;i++)
     603                 :                 {
     604              40 :                     double X = asNeatLineGCPs[i].dfGCPX = poLS->getX(i);
     605              40 :                     double Y = asNeatLineGCPs[i].dfGCPY = poLS->getY(i);
     606              40 :                     double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2];
     607              40 :                     double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5];
     608              40 :                     asNeatLineGCPs[i].dfGCPPixel = x;
     609              40 :                     asNeatLineGCPs[i].dfGCPLine = y;
     610                 :                 }
     611                 : 
     612              10 :                 int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
     613                 :                 GDALPDFFind4Corners(asNeatLineGCPs,
     614              10 :                                     iUL,iUR, iLR, iLL);
     615                 : 
     616              10 :                 if (fabs(asNeatLineGCPs[iUL].dfGCPPixel - asNeatLineGCPs[iLL].dfGCPPixel) > .5 ||
     617                 :                     fabs(asNeatLineGCPs[iUR].dfGCPPixel - asNeatLineGCPs[iLR].dfGCPPixel) > .5 ||
     618                 :                     fabs(asNeatLineGCPs[iUL].dfGCPLine - asNeatLineGCPs[iUR].dfGCPLine) > .5 ||
     619                 :                     fabs(asNeatLineGCPs[iLL].dfGCPLine - asNeatLineGCPs[iLR].dfGCPLine) > .5)
     620                 :                 {
     621                 :                     CPLError(CE_Warning, CPLE_NotSupported,
     622               0 :                             "Neatline coordinates should form a rectangle in pixel space. Ignoring it");
     623               0 :                     for(int i=0;i<4;i++)
     624                 :                     {
     625                 :                         CPLDebug("PDF", "pixel[%d] = %.1f, line[%d] = %.1f",
     626                 :                                 i, asNeatLineGCPs[i].dfGCPPixel,
     627               0 :                                 i, asNeatLineGCPs[i].dfGCPLine);
     628                 :                     }
     629                 :                 }
     630                 :                 else
     631                 :                 {
     632              10 :                     pasGCPList = asNeatLineGCPs;
     633                 :                 }
     634                 :             }
     635                 :         }
     636              10 :         delete poGeom;
     637                 :     }
     638                 : 
     639              82 :     if( pasGCPList )
     640                 :     {
     641              12 :         int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
     642                 :         GDALPDFFind4Corners(pasGCPList,
     643              12 :                             iUL,iUR, iLR, iLL);
     644                 : 
     645              84 :         if (fabs(pasGCPList[iUL].dfGCPPixel - pasGCPList[iLL].dfGCPPixel) > .5 ||
     646              24 :             fabs(pasGCPList[iUR].dfGCPPixel - pasGCPList[iLR].dfGCPPixel) > .5 ||
     647              24 :             fabs(pasGCPList[iUL].dfGCPLine - pasGCPList[iUR].dfGCPLine) > .5 ||
     648              24 :             fabs(pasGCPList[iLL].dfGCPLine - pasGCPList[iLR].dfGCPLine) > .5)
     649                 :         {
     650                 :             CPLError(CE_Failure, CPLE_NotSupported,
     651               0 :                      "GCPs should form a rectangle in pixel space");
     652               0 :             return 0;
     653                 :         }
     654                 :         
     655              12 :         dfULPixel = pasGCPList[iUL].dfGCPPixel;
     656              12 :         dfULLine = pasGCPList[iUL].dfGCPLine;
     657              12 :         dfLRPixel = pasGCPList[iLR].dfGCPPixel;
     658              12 :         dfLRLine = pasGCPList[iLR].dfGCPLine;
     659                 :         
     660                 :         /* Upper-left */
     661              12 :         adfGPTS[0] = pasGCPList[iUL].dfGCPX;
     662              12 :         adfGPTS[1] = pasGCPList[iUL].dfGCPY;
     663                 :         
     664                 :         /* Lower-left */
     665              12 :         adfGPTS[2] = pasGCPList[iLL].dfGCPX;
     666              12 :         adfGPTS[3] = pasGCPList[iLL].dfGCPY;
     667                 :         
     668                 :         /* Lower-right */
     669              12 :         adfGPTS[4] = pasGCPList[iLR].dfGCPX;
     670              12 :         adfGPTS[5] = pasGCPList[iLR].dfGCPY;
     671                 :         
     672                 :         /* Upper-right */
     673              12 :         adfGPTS[6] = pasGCPList[iUR].dfGCPX;
     674              12 :         adfGPTS[7] = pasGCPList[iUR].dfGCPY;
     675                 :     }
     676                 :     else
     677                 :     {
     678                 :         /* Upper-left */
     679              70 :         adfGPTS[0] = PIXEL_TO_GEO_X(0, 0);
     680              70 :         adfGPTS[1] = PIXEL_TO_GEO_Y(0, 0);
     681                 : 
     682                 :         /* Lower-left */
     683              70 :         adfGPTS[2] = PIXEL_TO_GEO_X(0, nHeight);
     684              70 :         adfGPTS[3] = PIXEL_TO_GEO_Y(0, nHeight);
     685                 : 
     686                 :         /* Lower-right */
     687              70 :         adfGPTS[4] = PIXEL_TO_GEO_X(nWidth, nHeight);
     688              70 :         adfGPTS[5] = PIXEL_TO_GEO_Y(nWidth, nHeight);
     689                 : 
     690                 :         /* Upper-right */
     691              70 :         adfGPTS[6] = PIXEL_TO_GEO_X(nWidth, 0);
     692              70 :         adfGPTS[7] = PIXEL_TO_GEO_Y(nWidth, 0);
     693                 :     }
     694                 :     
     695              82 :     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
     696              82 :     if( hSRS == NULL )
     697               0 :         return 0;
     698              82 :     OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS);
     699              82 :     if( hSRSGeog == NULL )
     700                 :     {
     701               0 :         OSRDestroySpatialReference(hSRS);
     702               0 :         return 0;
     703                 :     }
     704              82 :     OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog);
     705              82 :     if( hCT == NULL )
     706                 :     {
     707               0 :         OSRDestroySpatialReference(hSRS);
     708               0 :         OSRDestroySpatialReference(hSRSGeog);
     709               0 :         return 0;
     710                 :     }
     711                 : 
     712              82 :     int bSuccess = TRUE;
     713                 :     
     714              82 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 0, adfGPTS + 1, NULL ) == 1);
     715              82 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 2, adfGPTS + 3, NULL ) == 1);
     716              82 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 4, adfGPTS + 5, NULL ) == 1);
     717              82 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 6, adfGPTS + 7, NULL ) == 1);
     718                 : 
     719              82 :     if (!bSuccess)
     720                 :     {
     721               0 :         OSRDestroySpatialReference(hSRS);
     722               0 :         OSRDestroySpatialReference(hSRSGeog);
     723               0 :         OCTDestroyCoordinateTransformation(hCT);
     724               0 :         return 0;
     725                 :     }
     726                 : 
     727              82 :     const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, NULL );
     728              82 :     const char * pszAuthorityName = OSRGetAuthorityName( hSRS, NULL );
     729              82 :     int nEPSGCode = 0;
     730              82 :     if( pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") &&
     731                 :         pszAuthorityCode != NULL )
     732              78 :         nEPSGCode = atoi(pszAuthorityCode);
     733                 : 
     734              82 :     int bIsGeographic = OSRIsGeographic(hSRS);
     735                 : 
     736              82 :     OSRMorphToESRI(hSRS);
     737              82 :     char* pszESRIWKT = NULL;
     738              82 :     OSRExportToWkt(hSRS, &pszESRIWKT);
     739                 : 
     740              82 :     OSRDestroySpatialReference(hSRS);
     741              82 :     OSRDestroySpatialReference(hSRSGeog);
     742              82 :     OCTDestroyCoordinateTransformation(hCT);
     743              82 :     hSRS = NULL;
     744              82 :     hSRSGeog = NULL;
     745              82 :     hCT = NULL;
     746                 : 
     747              82 :     if (pszESRIWKT == NULL)
     748               0 :         return 0;
     749                 : 
     750              82 :     int nViewportId = AllocNewObject();
     751              82 :     int nMeasureId = AllocNewObject();
     752              82 :     int nGCSId = AllocNewObject();
     753                 : 
     754              82 :     StartObj(nViewportId);
     755              82 :     GDALPDFDictionaryRW oViewPortDict;
     756                 :     oViewPortDict.Add("Type", GDALPDFObjectRW::CreateName("Viewport"))
     757                 :                  .Add("Name", "Layer")
     758                 :                  .Add("BBox", &((new GDALPDFArrayRW())
     759                 :                                 ->Add(dfULPixel / dfUserUnit + psMargins->nLeft)
     760                 :                                 .Add((nHeight - dfLRLine) / dfUserUnit + psMargins->nBottom)
     761                 :                                 .Add(dfLRPixel / dfUserUnit + psMargins->nLeft)
     762                 :                                 .Add((nHeight - dfULLine) / dfUserUnit + psMargins->nBottom)))
     763              82 :                  .Add("Measure", nMeasureId, 0);
     764              82 :     VSIFPrintfL(fp, "%s\n", oViewPortDict.Serialize().c_str());
     765              82 :     EndObj();
     766                 : 
     767              82 :     StartObj(nMeasureId);
     768              82 :     GDALPDFDictionaryRW oMeasureDict;
     769                 :     oMeasureDict .Add("Type", GDALPDFObjectRW::CreateName("Measure"))
     770                 :                  .Add("Subtype", GDALPDFObjectRW::CreateName("GEO"))
     771                 :                  .Add("Bounds", &((new GDALPDFArrayRW())
     772                 :                                 ->Add(0).Add(1).
     773                 :                                   Add(0).Add(0).
     774                 :                                   Add(1).Add(0).
     775                 :                                   Add(1).Add(1)))
     776                 :                  .Add("GPTS", &((new GDALPDFArrayRW())
     777                 :                                 ->Add(adfGPTS[1]).Add(adfGPTS[0]).
     778                 :                                   Add(adfGPTS[3]).Add(adfGPTS[2]).
     779                 :                                   Add(adfGPTS[5]).Add(adfGPTS[4]).
     780                 :                                   Add(adfGPTS[7]).Add(adfGPTS[6])))
     781                 :                  .Add("LPTS", &((new GDALPDFArrayRW())
     782                 :                                 ->Add(0).Add(1).
     783                 :                                   Add(0).Add(0).
     784                 :                                   Add(1).Add(0).
     785                 :                                   Add(1).Add(1)))
     786              82 :                  .Add("GCS", nGCSId, 0);
     787              82 :     VSIFPrintfL(fp, "%s\n", oMeasureDict.Serialize().c_str());
     788              82 :     EndObj();
     789                 : 
     790                 : 
     791              82 :     StartObj(nGCSId);
     792              82 :     GDALPDFDictionaryRW oGCSDict;
     793                 :     oGCSDict.Add("Type", GDALPDFObjectRW::CreateName(bIsGeographic ? "GEOGCS" : "PROJCS"))
     794              82 :             .Add("WKT", pszESRIWKT);
     795              82 :     if (nEPSGCode)
     796              78 :         oGCSDict.Add("EPSG", nEPSGCode);
     797              82 :     VSIFPrintfL(fp, "%s\n", oGCSDict.Serialize().c_str());
     798              82 :     EndObj();
     799                 : 
     800              82 :     CPLFree(pszESRIWKT);
     801                 : 
     802              82 :     return nViewportId;
     803                 : }
     804                 : 
     805                 : /************************************************************************/
     806                 : /*                     GDALPDFBuildOGC_BP_Datum()                       */
     807                 : /************************************************************************/
     808                 : 
     809              14 : static GDALPDFObject* GDALPDFBuildOGC_BP_Datum(const OGRSpatialReference* poSRS)
     810                 : {
     811              14 :     const OGR_SRSNode* poDatumNode = poSRS->GetAttrNode("DATUM");
     812              14 :     const char* pszDatumDescription = NULL;
     813              14 :     if (poDatumNode && poDatumNode->GetChildCount() > 0)
     814              14 :         pszDatumDescription = poDatumNode->GetChild(0)->GetValue();
     815                 : 
     816              14 :     GDALPDFObjectRW* poPDFDatum = NULL;
     817                 : 
     818              14 :     if (pszDatumDescription)
     819                 :     {
     820              14 :         double dfSemiMajor = poSRS->GetSemiMajor();
     821              14 :         double dfInvFlattening = poSRS->GetInvFlattening();
     822              14 :         int nEPSGDatum = -1;
     823              14 :         const char *pszAuthority = poSRS->GetAuthorityName( "DATUM" );
     824              14 :         if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") )
     825              14 :             nEPSGDatum = atoi(poSRS->GetAuthorityCode( "DATUM" ));
     826                 : 
     827              16 :         if( EQUAL(pszDatumDescription,SRS_DN_WGS84) || nEPSGDatum == 6326 )
     828               2 :             poPDFDatum = GDALPDFObjectRW::CreateString("WGE");
     829              24 :         else if( EQUAL(pszDatumDescription, SRS_DN_NAD27) || nEPSGDatum == 6267 )
     830              12 :             poPDFDatum = GDALPDFObjectRW::CreateString("NAS");
     831               0 :         else if( EQUAL(pszDatumDescription, SRS_DN_NAD83) || nEPSGDatum == 6269 )
     832               0 :             poPDFDatum = GDALPDFObjectRW::CreateString("NAR");
     833                 :         else
     834                 :         {
     835                 :             CPLDebug("PDF",
     836                 :                      "Unhandled datum name (%s). Write datum parameters then.",
     837               0 :                      pszDatumDescription);
     838                 : 
     839               0 :             GDALPDFDictionaryRW* poPDFDatumDict = new GDALPDFDictionaryRW();
     840               0 :             poPDFDatum = GDALPDFObjectRW::CreateDictionary(poPDFDatumDict);
     841                 : 
     842               0 :             const OGR_SRSNode* poSpheroidNode = poSRS->GetAttrNode("SPHEROID");
     843               0 :             if (poSpheroidNode && poSpheroidNode->GetChildCount() >= 3)
     844                 :             {
     845               0 :                 poPDFDatumDict->Add("Description", pszDatumDescription);
     846                 : 
     847               0 :                 const char* pszEllipsoidCode = NULL;
     848                 : #ifdef disabled_because_terrago_toolbar_does_not_like_it
     849                 :                 if( ABS(dfSemiMajor-6378249.145) < 0.01
     850                 :                     && ABS(dfInvFlattening-293.465) < 0.0001 )
     851                 :                 {
     852                 :                     pszEllipsoidCode = "CD";     /* Clark 1880 */
     853                 :                 }
     854                 :                 else if( ABS(dfSemiMajor-6378245.0) < 0.01
     855                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     856                 :                 {
     857                 :                     pszEllipsoidCode = "KA";      /* Krassovsky */
     858                 :                 }
     859                 :                 else if( ABS(dfSemiMajor-6378388.0) < 0.01
     860                 :                          && ABS(dfInvFlattening-297.0) < 0.0001 )
     861                 :                 {
     862                 :                     pszEllipsoidCode = "IN";       /* International 1924 */
     863                 :                 }
     864                 :                 else if( ABS(dfSemiMajor-6378160.0) < 0.01
     865                 :                          && ABS(dfInvFlattening-298.25) < 0.0001 )
     866                 :                 {
     867                 :                     pszEllipsoidCode = "AN";    /* Australian */
     868                 :                 }
     869                 :                 else if( ABS(dfSemiMajor-6377397.155) < 0.01
     870                 :                          && ABS(dfInvFlattening-299.1528128) < 0.0001 )
     871                 :                 {
     872                 :                     pszEllipsoidCode = "BR";     /* Bessel 1841 */
     873                 :                 }
     874                 :                 else if( ABS(dfSemiMajor-6377483.865) < 0.01
     875                 :                          && ABS(dfInvFlattening-299.1528128) < 0.0001 )
     876                 :                 {
     877                 :                     pszEllipsoidCode = "BN";   /* Bessel 1841 (Namibia / Schwarzeck)*/
     878                 :                 }
     879                 : #if 0
     880                 :                 else if( ABS(dfSemiMajor-6378160.0) < 0.01
     881                 :                          && ABS(dfInvFlattening-298.247167427) < 0.0001 )
     882                 :                 {
     883                 :                     pszEllipsoidCode = "GRS67";      /* GRS 1967 */
     884                 :                 }
     885                 : #endif
     886                 :                 else if( ABS(dfSemiMajor-6378137) < 0.01
     887                 :                          && ABS(dfInvFlattening-298.257222101) < 0.000001 )
     888                 :                 {
     889                 :                     pszEllipsoidCode = "RF";      /* GRS 1980 */
     890                 :                 }
     891                 :                 else if( ABS(dfSemiMajor-6378206.4) < 0.01
     892                 :                          && ABS(dfInvFlattening-294.9786982) < 0.0001 )
     893                 :                 {
     894                 :                     pszEllipsoidCode = "CC";     /* Clarke 1866 */
     895                 :                 }
     896                 :                 else if( ABS(dfSemiMajor-6377340.189) < 0.01
     897                 :                          && ABS(dfInvFlattening-299.3249646) < 0.0001 )
     898                 :                 {
     899                 :                     pszEllipsoidCode = "AM";   /* Modified Airy */
     900                 :                 }
     901                 :                 else if( ABS(dfSemiMajor-6377563.396) < 0.01
     902                 :                          && ABS(dfInvFlattening-299.3249646) < 0.0001 )
     903                 :                 {
     904                 :                     pszEllipsoidCode = "AA";       /* Airy */
     905                 :                 }
     906                 :                 else if( ABS(dfSemiMajor-6378200) < 0.01
     907                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     908                 :                 {
     909                 :                     pszEllipsoidCode = "HE";    /* Helmert 1906 */
     910                 :                 }
     911                 :                 else if( ABS(dfSemiMajor-6378155) < 0.01
     912                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     913                 :                 {
     914                 :                     pszEllipsoidCode = "FA";   /* Modified Fischer 1960 */
     915                 :                 }
     916                 : #if 0
     917                 :                 else if( ABS(dfSemiMajor-6377298.556) < 0.01
     918                 :                          && ABS(dfInvFlattening-300.8017) < 0.0001 )
     919                 :                 {
     920                 :                     pszEllipsoidCode = "evrstSS";    /* Everest (Sabah & Sarawak) */
     921                 :                 }
     922                 :                 else if( ABS(dfSemiMajor-6378165.0) < 0.01
     923                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     924                 :                 {
     925                 :                     pszEllipsoidCode = "WGS60";
     926                 :                 }
     927                 :                 else if( ABS(dfSemiMajor-6378145.0) < 0.01
     928                 :                          && ABS(dfInvFlattening-298.25) < 0.0001 )
     929                 :                 {
     930                 :                     pszEllipsoidCode = "WGS66";
     931                 :                 }
     932                 : #endif
     933                 :                 else if( ABS(dfSemiMajor-6378135.0) < 0.01
     934                 :                          && ABS(dfInvFlattening-298.26) < 0.0001 )
     935                 :                 {
     936                 :                     pszEllipsoidCode = "WD";
     937                 :                 }
     938                 :                 else if( ABS(dfSemiMajor-6378137.0) < 0.01
     939                 :                          && ABS(dfInvFlattening-298.257223563) < 0.000001 )
     940                 :                 {
     941                 :                     pszEllipsoidCode = "WE";
     942                 :                 }
     943                 : #endif
     944                 : 
     945               0 :                 if( pszEllipsoidCode != NULL )
     946                 :                 {
     947               0 :                     poPDFDatumDict->Add("Ellipsoid", pszEllipsoidCode);
     948                 :                 }
     949                 :                 else
     950                 :                 {
     951                 :                     const char* pszEllipsoidDescription =
     952               0 :                         poSpheroidNode->GetChild(0)->GetValue();
     953                 : 
     954                 :                     CPLDebug("PDF",
     955                 :                          "Unhandled ellipsoid name (%s). Write ellipsoid parameters then.",
     956               0 :                          pszEllipsoidDescription);
     957                 : 
     958                 :                     poPDFDatumDict->Add("Ellipsoid",
     959                 :                         &((new GDALPDFDictionaryRW())
     960                 :                         ->Add("Description", pszEllipsoidDescription)
     961                 :                          .Add("SemiMajorAxis", dfSemiMajor, TRUE)
     962               0 :                          .Add("InvFlattening", dfInvFlattening, TRUE)));
     963                 :                 }
     964                 : 
     965               0 :                 const OGR_SRSNode *poTOWGS84 = poSRS->GetAttrNode( "TOWGS84" );
     966               0 :                 if( poTOWGS84 != NULL
     967                 :                     && poTOWGS84->GetChildCount() >= 3
     968                 :                     && (poTOWGS84->GetChildCount() < 7
     969                 :                     || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"")
     970                 :                         && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"")
     971                 :                         && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"")
     972                 :                         && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) )
     973                 :                 {
     974                 :                     poPDFDatumDict->Add("ToWGS84",
     975                 :                         &((new GDALPDFDictionaryRW())
     976                 :                         ->Add("dx", poTOWGS84->GetChild(0)->GetValue())
     977                 :                          .Add("dy", poTOWGS84->GetChild(1)->GetValue())
     978               0 :                          .Add("dz", poTOWGS84->GetChild(2)->GetValue())) );
     979                 :                 }
     980               0 :                 else if( poTOWGS84 != NULL && poTOWGS84->GetChildCount() >= 7)
     981                 :                 {
     982                 :                     poPDFDatumDict->Add("ToWGS84",
     983                 :                         &((new GDALPDFDictionaryRW())
     984                 :                         ->Add("dx", poTOWGS84->GetChild(0)->GetValue())
     985                 :                          .Add("dy", poTOWGS84->GetChild(1)->GetValue())
     986                 :                          .Add("dz", poTOWGS84->GetChild(2)->GetValue())
     987                 :                          .Add("rx", poTOWGS84->GetChild(3)->GetValue())
     988                 :                          .Add("ry", poTOWGS84->GetChild(4)->GetValue())
     989                 :                          .Add("rz", poTOWGS84->GetChild(5)->GetValue())
     990               0 :                          .Add("sf", poTOWGS84->GetChild(6)->GetValue())) );
     991                 :                 }
     992                 :             }
     993                 :         }
     994                 :     }
     995                 :     else
     996                 :     {
     997                 :         CPLError(CE_Warning, CPLE_NotSupported,
     998               0 :                  "No datum name. Defaulting to WGS84.");
     999                 :     }
    1000                 : 
    1001              14 :     if (poPDFDatum == NULL)
    1002               0 :         poPDFDatum = GDALPDFObjectRW::CreateString("WGE");
    1003                 : 
    1004              14 :     return poPDFDatum;
    1005                 : }
    1006                 : 
    1007                 : /************************************************************************/
    1008                 : /*                   GDALPDFBuildOGC_BP_Projection()                    */
    1009                 : /************************************************************************/
    1010                 : 
    1011              14 : static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS)
    1012                 : {
    1013                 : 
    1014              14 :     const char* pszProjectionOGCBP = "GEOGRAPHIC";
    1015              14 :     const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
    1016                 : 
    1017              14 :     GDALPDFDictionaryRW* poProjectionDict = new GDALPDFDictionaryRW();
    1018              14 :     poProjectionDict->Add("Type", GDALPDFObjectRW::CreateName("Projection"));
    1019              14 :     poProjectionDict->Add("Datum", GDALPDFBuildOGC_BP_Datum(poSRS));
    1020                 : 
    1021              14 :     if( pszProjection == NULL )
    1022                 :     {
    1023               2 :         if( poSRS->IsGeographic() )
    1024               2 :             pszProjectionOGCBP = "GEOGRAPHIC";
    1025               0 :         else if( poSRS->IsLocal() )
    1026               0 :             pszProjectionOGCBP = "LOCAL CARTESIAN";
    1027                 :         else
    1028                 :         {
    1029               0 :             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported SRS type");
    1030               0 :             delete poProjectionDict;
    1031               0 :             return NULL;
    1032                 :         }
    1033                 :     }
    1034              12 :     else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) )
    1035                 :     {
    1036                 :         int bNorth;
    1037              12 :         int nZone = poSRS->GetUTMZone( &bNorth );
    1038                 : 
    1039              12 :         if( nZone != 0 )
    1040                 :         {
    1041              12 :             pszProjectionOGCBP = "UT";
    1042              12 :             poProjectionDict->Add("Hemisphere", (bNorth) ? "N" : "S");
    1043              12 :             poProjectionDict->Add("Zone", nZone);
    1044                 :         }
    1045                 :         else
    1046                 :         {
    1047               0 :             double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.L);
    1048               0 :             double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1049               0 :             double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1050               0 :             double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1051               0 :             double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1052                 : 
    1053                 :             /* OGC_BP supports representing numbers as strings for better precision */
    1054                 :             /* so use it */
    1055                 : 
    1056               0 :             pszProjectionOGCBP = "TC";
    1057               0 :             poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1058               0 :             poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1059               0 :             poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1060               0 :             poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1061               0 :             poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1062                 :         }
    1063                 :     }
    1064               0 :     else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) )
    1065                 :     {
    1066               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1067               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1068               0 :         double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1069               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1070               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1071                 : 
    1072               0 :         if( fabs(dfCenterLat) == 90.0 && dfCenterLong == 0.0 &&
    1073                 :             dfScale == 0.994 && dfFalseEasting == 200000.0 && dfFalseNorthing == 200000.0)
    1074                 :         {
    1075               0 :             pszProjectionOGCBP = "UP";
    1076               0 :             poProjectionDict->Add("Hemisphere", (dfCenterLat > 0) ? "N" : "S");
    1077                 :         }
    1078                 :         else
    1079                 :         {
    1080               0 :             pszProjectionOGCBP = "PG";
    1081               0 :             poProjectionDict->Add("LatitudeTrueScale", dfCenterLat, TRUE);
    1082               0 :             poProjectionDict->Add("LongitudeDownFromPole", dfCenterLong, TRUE);
    1083               0 :             poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1084               0 :             poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1085               0 :             poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1086                 :         }
    1087                 :     }
    1088                 : 
    1089               0 :     else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    1090                 :     {
    1091               0 :         double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0);
    1092               0 :         double dfStdP2 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0);
    1093               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1094               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1095               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1096               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1097                 : 
    1098               0 :         pszProjectionOGCBP = "LE";
    1099               0 :         poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE);
    1100               0 :         poProjectionDict->Add("StandardParallelTwo", dfStdP2, TRUE);
    1101               0 :         poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1102               0 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1103               0 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1104               0 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1105                 :     }
    1106                 : 
    1107               0 :     else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) )
    1108                 :     {
    1109               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1110               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1111               0 :         double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1112               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1113               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1114                 : 
    1115               0 :         pszProjectionOGCBP = "MC";
    1116               0 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1117               0 :         poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1118               0 :         poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1119               0 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1120               0 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1121                 :     }
    1122                 : 
    1123                 : #ifdef not_supported
    1124                 :     else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) )
    1125                 :     {
    1126                 :         double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0);
    1127                 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1128                 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1129                 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1130                 : 
    1131                 :         pszProjectionOGCBP = "MC";
    1132                 :         poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE);
    1133                 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1134                 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1135                 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1136                 :     }
    1137                 : #endif
    1138                 : 
    1139                 :     else
    1140                 :     {
    1141                 :         CPLError(CE_Warning, CPLE_NotSupported,
    1142               0 :                  "Unhandled projection type (%s) for now", pszProjection);
    1143                 :     }
    1144                 : 
    1145              14 :     poProjectionDict->Add("ProjectionType", pszProjectionOGCBP);
    1146                 : 
    1147              14 :     if( poSRS->IsProjected() )
    1148                 :     {
    1149              12 :         char* pszUnitName = NULL;
    1150              12 :         double dfLinearUnits = poSRS->GetLinearUnits(&pszUnitName);
    1151              12 :         if (dfLinearUnits == 1.0)
    1152              12 :             poProjectionDict->Add("Units", "M");
    1153               0 :         else if (dfLinearUnits == 0.3048)
    1154               0 :             poProjectionDict->Add("Units", "FT");
    1155                 :     }
    1156                 : 
    1157              14 :     return poProjectionDict;
    1158                 : }
    1159                 : 
    1160                 : /************************************************************************/
    1161                 : /*                           WriteSRS_OGC_BP()                          */
    1162                 : /************************************************************************/
    1163                 : 
    1164              14 : int GDALPDFWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS,
    1165                 :                                    double dfUserUnit,
    1166                 :                                    const char* pszNEATLINE,
    1167                 :                                    PDFMargins* psMargins)
    1168                 : {
    1169              14 :     int  nWidth = poSrcDS->GetRasterXSize();
    1170              14 :     int  nHeight = poSrcDS->GetRasterYSize();
    1171              14 :     const char* pszWKT = poSrcDS->GetProjectionRef();
    1172                 :     double adfGeoTransform[6];
    1173                 : 
    1174              14 :     int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None);
    1175              14 :     int nGCPCount = poSrcDS->GetGCPCount();
    1176              14 :     const GDAL_GCP* pasGCPList = (nGCPCount >= 4) ? poSrcDS->GetGCPs() : NULL;
    1177              14 :     if (pasGCPList != NULL)
    1178               4 :         pszWKT = poSrcDS->GetGCPProjection();
    1179                 : 
    1180              14 :     if( !bHasGT && pasGCPList == NULL )
    1181               0 :         return 0;
    1182                 : 
    1183              14 :     if( pszWKT == NULL || EQUAL(pszWKT, "") )
    1184               0 :         return 0;
    1185                 :     
    1186              14 :     if( !bHasGT )
    1187                 :     {
    1188               4 :         if (!GDALGCPsToGeoTransform( nGCPCount, pasGCPList,
    1189                 :                                      adfGeoTransform, FALSE ))
    1190                 :         {
    1191               2 :             CPLDebug("PDF", "Could not compute GT with exact match. Writing Registration then");
    1192                 :         }
    1193                 :         else
    1194                 :         {
    1195               2 :             bHasGT = TRUE;
    1196                 :         }
    1197                 :     }
    1198                 : 
    1199              14 :     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
    1200              14 :     if( hSRS == NULL )
    1201               0 :         return 0;
    1202                 : 
    1203              14 :     const OGRSpatialReference* poSRS = (const OGRSpatialReference*)hSRS;
    1204              14 :     GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS);
    1205              14 :     if (poProjectionDict == NULL)
    1206                 :     {
    1207               0 :         OSRDestroySpatialReference(hSRS);
    1208               0 :         return 0;
    1209                 :     }
    1210                 : 
    1211              14 :     GDALPDFArrayRW* poNeatLineArray = NULL;
    1212                 : 
    1213              14 :     if (pszNEATLINE == NULL)
    1214              12 :         pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE");
    1215              14 :     if( bHasGT && pszNEATLINE != NULL && !EQUAL(pszNEATLINE, "NO") && pszNEATLINE[0] != '\0' )
    1216                 :     {
    1217               2 :         OGRGeometry* poGeom = NULL;
    1218               2 :         OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom );
    1219               2 :         if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
    1220                 :         {
    1221               2 :             OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing();
    1222                 :             double adfGeoTransformInv[6];
    1223               2 :             if( poLS != NULL && poLS->getNumPoints() >= 5 &&
    1224                 :                 GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) )
    1225                 :             {
    1226               2 :                 poNeatLineArray = new GDALPDFArrayRW();
    1227                 : 
    1228                 :                  // FIXME : ensure that they are in clockwise order ?
    1229              12 :                 for(int i=0;i<poLS->getNumPoints() - 1;i++)
    1230                 :                 {
    1231              10 :                     double X = poLS->getX(i);
    1232              10 :                     double Y = poLS->getY(i);
    1233              10 :                     double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2];
    1234              10 :                     double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5];
    1235              10 :                     poNeatLineArray->Add(x / dfUserUnit + psMargins->nLeft, TRUE);
    1236              10 :                     poNeatLineArray->Add((nHeight - y) / dfUserUnit + psMargins->nBottom, TRUE);
    1237                 :                 }
    1238                 :             }
    1239                 :         }
    1240               2 :         delete poGeom;
    1241                 :     }
    1242                 : 
    1243              14 :     if( pszNEATLINE != NULL && EQUAL(pszNEATLINE, "NO") )
    1244                 :     {
    1245                 :         // Do nothing
    1246                 :     }
    1247              18 :     else if( pasGCPList && poNeatLineArray == NULL)
    1248                 :     {
    1249               4 :         if (nGCPCount == 4)
    1250                 :         {
    1251               2 :             int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
    1252                 :             GDALPDFFind4Corners(pasGCPList,
    1253               2 :                                 iUL,iUR, iLR, iLL);
    1254                 : 
    1255                 :             double adfNL[8];
    1256               2 :             adfNL[0] = pasGCPList[iUL].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1257               2 :             adfNL[1] = (nHeight - pasGCPList[iUL].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1258               2 :             adfNL[2] = pasGCPList[iLL].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1259               2 :             adfNL[3] = (nHeight - pasGCPList[iLL].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1260               2 :             adfNL[4] = pasGCPList[iLR].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1261               2 :             adfNL[5] = (nHeight - pasGCPList[iLR].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1262               2 :             adfNL[6] = pasGCPList[iUR].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1263               2 :             adfNL[7] = (nHeight - pasGCPList[iUR].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1264                 : 
    1265               2 :             poNeatLineArray = new GDALPDFArrayRW();
    1266               2 :             poNeatLineArray->Add(adfNL, 8, TRUE);
    1267                 :         }
    1268                 :         else
    1269                 :         {
    1270               2 :             poNeatLineArray = new GDALPDFArrayRW();
    1271                 : 
    1272                 :             // FIXME : ensure that they are in clockwise order ?
    1273                 :             int i;
    1274              12 :             for(i = 0; i < nGCPCount; i++)
    1275                 :             {
    1276              10 :                 poNeatLineArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE);
    1277              10 :                 poNeatLineArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE);
    1278                 :             }
    1279                 :         }
    1280                 :     }
    1281              10 :     else if (poNeatLineArray == NULL)
    1282                 :     {
    1283               8 :         poNeatLineArray = new GDALPDFArrayRW();
    1284                 : 
    1285               8 :         poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE);
    1286               8 :         poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE);
    1287                 : 
    1288               8 :         poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE);
    1289               8 :         poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE);
    1290                 : 
    1291               8 :         poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE);
    1292               8 :         poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE);
    1293                 : 
    1294               8 :         poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE);
    1295               8 :         poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE);
    1296                 :     }
    1297                 : 
    1298              14 :     int nLGIDictId = AllocNewObject();
    1299              14 :     StartObj(nLGIDictId);
    1300              14 :     GDALPDFDictionaryRW oLGIDict;
    1301                 :     oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict"))
    1302              14 :             .Add("Version", "2.1");
    1303              14 :     if( bHasGT )
    1304                 :     {
    1305                 :         double adfCTM[6];
    1306              12 :         double dfX1 = psMargins->nLeft;
    1307              12 :         double dfY2 = nHeight / dfUserUnit + psMargins->nBottom ;
    1308                 : 
    1309              12 :         adfCTM[0] = adfGeoTransform[1] * dfUserUnit;
    1310              12 :         adfCTM[1] = adfGeoTransform[2] * dfUserUnit;
    1311              12 :         adfCTM[2] = - adfGeoTransform[4] * dfUserUnit;
    1312              12 :         adfCTM[3] = - adfGeoTransform[5] * dfUserUnit;
    1313              12 :         adfCTM[4] = adfGeoTransform[0] - (adfCTM[0] * dfX1 + adfCTM[2] * dfY2);
    1314              12 :         adfCTM[5] = adfGeoTransform[3] - (adfCTM[1] * dfX1 + adfCTM[3] * dfY2);
    1315                 : 
    1316              12 :         oLGIDict.Add("CTM", &((new GDALPDFArrayRW())->Add(adfCTM, 6, TRUE)));
    1317                 :     }
    1318                 :     else
    1319                 :     {
    1320               2 :         GDALPDFArrayRW* poRegistrationArray = new GDALPDFArrayRW();
    1321                 :         int i;
    1322              12 :         for(i = 0; i < nGCPCount; i++)
    1323                 :         {
    1324              10 :             GDALPDFArrayRW* poPTArray = new GDALPDFArrayRW();
    1325              10 :             poPTArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE);
    1326              10 :             poPTArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE);
    1327              10 :             poPTArray->Add(pasGCPList[i].dfGCPX, TRUE);
    1328              10 :             poPTArray->Add(pasGCPList[i].dfGCPY, TRUE);
    1329              10 :             poRegistrationArray->Add(poPTArray);
    1330                 :         }
    1331               2 :         oLGIDict.Add("Registration", poRegistrationArray);
    1332                 :     }
    1333              14 :     if( poNeatLineArray )
    1334                 :     {
    1335              14 :         oLGIDict.Add("Neatline", poNeatLineArray);
    1336                 :     }
    1337                 : 
    1338              14 :     const OGR_SRSNode* poNode = poSRS->GetRoot();
    1339              14 :     if( poNode != NULL )
    1340              14 :         poNode = poNode->GetChild(0);
    1341              14 :     const char* pszDescription = NULL;
    1342              14 :     if( poNode != NULL )
    1343              14 :         pszDescription = poNode->GetValue();
    1344              14 :     if( pszDescription )
    1345                 :     {
    1346              14 :         oLGIDict.Add("Description", pszDescription);
    1347                 :     }
    1348                 : 
    1349              14 :     oLGIDict.Add("Projection", poProjectionDict);
    1350                 : 
    1351                 :     /* GDAL extension */
    1352              14 :     if( CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) )
    1353               6 :         poProjectionDict->Add("WKT", pszWKT);
    1354                 : 
    1355              14 :     VSIFPrintfL(fp, "%s\n", oLGIDict.Serialize().c_str());
    1356              14 :     EndObj();
    1357                 : 
    1358              14 :     OSRDestroySpatialReference(hSRS);
    1359                 :     
    1360              14 :     return nLGIDictId;
    1361                 : }
    1362                 : 
    1363                 : /************************************************************************/
    1364                 : /*                     GDALPDFGetValueFromDSOrOption()                  */
    1365                 : /************************************************************************/
    1366                 : 
    1367             714 : static const char* GDALPDFGetValueFromDSOrOption(GDALDataset* poSrcDS,
    1368                 :                                                  char** papszOptions,
    1369                 :                                                  const char* pszKey)
    1370                 : {
    1371             714 :     const char* pszValue = CSLFetchNameValue(papszOptions, pszKey);
    1372             714 :     if (pszValue == NULL)
    1373             702 :         pszValue = poSrcDS->GetMetadataItem(pszKey);
    1374             714 :     if (pszValue != NULL && pszValue[0] == '\0')
    1375               4 :         return NULL;
    1376                 :     else
    1377             710 :         return pszValue;
    1378                 : }
    1379                 : 
    1380                 : /************************************************************************/
    1381                 : /*                             SetInfo()                                */
    1382                 : /************************************************************************/
    1383                 : 
    1384             102 : int GDALPDFWriter::SetInfo(GDALDataset* poSrcDS,
    1385                 :                            char** papszOptions)
    1386                 : {
    1387             102 :     const char* pszAUTHOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "AUTHOR");
    1388             102 :     const char* pszPRODUCER = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "PRODUCER");
    1389             102 :     const char* pszCREATOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATOR");
    1390             102 :     const char* pszCREATION_DATE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATION_DATE");
    1391             102 :     const char* pszSUBJECT = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "SUBJECT");
    1392             102 :     const char* pszTITLE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "TITLE");
    1393             102 :     const char* pszKEYWORDS = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "KEYWORDS");
    1394                 : 
    1395             102 :     if (pszAUTHOR == NULL && pszPRODUCER == NULL && pszCREATOR == NULL && pszCREATION_DATE == NULL &&
    1396                 :         pszSUBJECT == NULL && pszTITLE == NULL && pszKEYWORDS == NULL)
    1397              90 :         return 0;
    1398                 : 
    1399              12 :     if (nInfoId == 0)
    1400               8 :         nInfoId = AllocNewObject();
    1401              12 :     StartObj(nInfoId, nInfoGen);
    1402              12 :     GDALPDFDictionaryRW oDict;
    1403              12 :     if (pszAUTHOR != NULL)
    1404              12 :         oDict.Add("Author", pszAUTHOR);
    1405              12 :     if (pszPRODUCER != NULL)
    1406               4 :         oDict.Add("Producer", pszPRODUCER);
    1407              12 :     if (pszCREATOR != NULL)
    1408               4 :         oDict.Add("Creator", pszCREATOR);
    1409              12 :     if (pszCREATION_DATE != NULL)
    1410               0 :         oDict.Add("CreationDate", pszCREATION_DATE);
    1411              12 :     if (pszSUBJECT != NULL)
    1412               4 :         oDict.Add("Subject", pszSUBJECT);
    1413              12 :     if (pszTITLE != NULL)
    1414               4 :         oDict.Add("Title", pszTITLE);
    1415              12 :     if (pszKEYWORDS != NULL)
    1416               4 :         oDict.Add("Keywords", pszKEYWORDS);
    1417              12 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1418              12 :     EndObj();
    1419                 : 
    1420              12 :     return nInfoId;
    1421                 : }
    1422                 : 
    1423                 : /************************************************************************/
    1424                 : /*                             SetXMP()                                 */
    1425                 : /************************************************************************/
    1426                 : 
    1427             102 : int  GDALPDFWriter::SetXMP(GDALDataset* poSrcDS,
    1428                 :                            const char* pszXMP)
    1429                 : {
    1430             102 :     if (pszXMP != NULL && EQUALN(pszXMP, "NO", 2))
    1431               2 :         return 0;
    1432             100 :     if (pszXMP != NULL && pszXMP[0] == '\0')
    1433               0 :         return 0;
    1434                 : 
    1435             100 :     char** papszXMP = poSrcDS->GetMetadata("xml:XMP");
    1436             100 :     if (pszXMP == NULL && papszXMP != NULL && papszXMP[0] != NULL)
    1437              10 :         pszXMP = papszXMP[0];
    1438                 : 
    1439             100 :     if (pszXMP == NULL)
    1440              90 :         return 0;
    1441                 : 
    1442              10 :     CPLXMLNode* psNode = CPLParseXMLString(pszXMP);
    1443              10 :     if (psNode == NULL)
    1444               0 :         return 0;
    1445              10 :     CPLDestroyXMLNode(psNode);
    1446                 : 
    1447              10 :     if(nXMPId == 0)
    1448               6 :         nXMPId = AllocNewObject();
    1449              10 :     StartObj(nXMPId, nXMPGen);
    1450              10 :     GDALPDFDictionaryRW oDict;
    1451                 :     oDict.Add("Type", GDALPDFObjectRW::CreateName("Metadata"))
    1452                 :          .Add("Subtype", GDALPDFObjectRW::CreateName("XML"))
    1453              10 :          .Add("Length", (int)strlen(pszXMP));
    1454              10 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1455              10 :     VSIFPrintfL(fp, "stream\n");
    1456              10 :     VSIFPrintfL(fp, "%s\n", pszXMP);
    1457              10 :     VSIFPrintfL(fp, "endstream\n");
    1458              10 :     EndObj();
    1459              10 :     return nXMPId;
    1460                 : }
    1461                 : 
    1462                 : /************************************************************************/
    1463                 : /*                              WriteOCG()                              */
    1464                 : /************************************************************************/
    1465                 : 
    1466             188 : int GDALPDFWriter::WriteOCG(const char* pszLayerName, int nParentId)
    1467                 : {
    1468             188 :     if (pszLayerName == NULL || pszLayerName[0] == '\0')
    1469             178 :         return 0;
    1470                 : 
    1471              10 :     int nOGCId = AllocNewObject();
    1472                 : 
    1473                 :     GDALPDFOCGDesc oOCGDesc;
    1474              10 :     oOCGDesc.nId = nOGCId;
    1475              10 :     oOCGDesc.nParentId = nParentId;
    1476                 : 
    1477              10 :     asOCGs.push_back(oOCGDesc);
    1478                 : 
    1479              10 :     StartObj(nOGCId);
    1480                 :     {
    1481              10 :         GDALPDFDictionaryRW oDict;
    1482              10 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("OCG"));
    1483              10 :         oDict.Add("Name", pszLayerName);
    1484              10 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1485                 :     }
    1486              10 :     EndObj();
    1487                 : 
    1488              10 :     return nOGCId;
    1489                 : }
    1490                 : 
    1491                 : /************************************************************************/
    1492                 : /*                              StartPage()                             */
    1493                 : /************************************************************************/
    1494                 : 
    1495              92 : int GDALPDFWriter::StartPage(GDALDataset* poSrcDS,
    1496                 :                              double dfDPI,
    1497                 :                              const char* pszGEO_ENCODING,
    1498                 :                              const char* pszNEATLINE,
    1499                 :                              PDFMargins* psMargins,
    1500                 :                              PDFCompressMethod eStreamCompressMethod,
    1501                 :                              int bHasOGRData)
    1502                 : {
    1503              92 :     int  nWidth = poSrcDS->GetRasterXSize();
    1504              92 :     int  nHeight = poSrcDS->GetRasterYSize();
    1505              92 :     int  nBands = poSrcDS->GetRasterCount();
    1506                 : 
    1507              92 :     double dfUserUnit = dfDPI / 72.0;
    1508              92 :     double dfWidthInUserUnit = nWidth / dfUserUnit + psMargins->nLeft + psMargins->nRight;
    1509              92 :     double dfHeightInUserUnit = nHeight / dfUserUnit + psMargins->nBottom + psMargins->nTop;
    1510                 : 
    1511              92 :     int nPageId = AllocNewObject();
    1512              92 :     asPageId.push_back(nPageId);
    1513                 : 
    1514              92 :     int nContentId = AllocNewObject();
    1515              92 :     int nResourcesId = AllocNewObject();
    1516                 : 
    1517                 :     int bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") ||
    1518              92 :                     EQUAL(pszGEO_ENCODING, "BOTH");
    1519                 :     int bOGC_BP   = EQUAL(pszGEO_ENCODING, "OGC_BP") ||
    1520              92 :                     EQUAL(pszGEO_ENCODING, "BOTH");
    1521                 : 
    1522              92 :     int nViewportId = 0;
    1523              92 :     if( bISO32000 )
    1524              76 :         nViewportId = WriteSRS_ISO32000(poSrcDS, dfUserUnit, pszNEATLINE, psMargins);
    1525                 : 
    1526              92 :     int nLGIDictId = 0;
    1527              92 :     if( bOGC_BP )
    1528              12 :         nLGIDictId = WriteSRS_OGC_BP(poSrcDS, dfUserUnit, pszNEATLINE, psMargins);
    1529                 : 
    1530              92 :     StartObj(nPageId);
    1531              92 :     GDALPDFDictionaryRW oDictPage;
    1532                 :     oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page"))
    1533                 :              .Add("Parent", nPageResourceId, 0)
    1534                 :              .Add("MediaBox", &((new GDALPDFArrayRW())
    1535                 :                                ->Add(0).Add(0).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit)))
    1536                 :              .Add("UserUnit", dfUserUnit)
    1537                 :              .Add("Contents", nContentId, 0)
    1538              92 :              .Add("Resources", nResourcesId, 0);
    1539                 : 
    1540              92 :     if (nBands == 4)
    1541                 :     {
    1542                 :         oDictPage.Add("Group",
    1543                 :                       &((new GDALPDFDictionaryRW())
    1544                 :                         ->Add("Type", GDALPDFObjectRW::CreateName("Group"))
    1545                 :                          .Add("S", GDALPDFObjectRW::CreateName("Transparency"))
    1546               6 :                          .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB"))));
    1547                 :     }
    1548              92 :     if (nViewportId)
    1549                 :     {
    1550                 :         oDictPage.Add("VP", &((new GDALPDFArrayRW())
    1551              68 :                                ->Add(nViewportId, 0)));
    1552                 :     }
    1553              92 :     if (nLGIDictId)
    1554                 :     {
    1555              12 :         oDictPage.Add("LGIDict", nLGIDictId, 0);
    1556                 :     }
    1557                 : 
    1558              92 :     if (bHasOGRData)
    1559               4 :         oDictPage.Add("StructParents", 0);
    1560                 : 
    1561              92 :     VSIFPrintfL(fp, "%s\n", oDictPage.Serialize().c_str());
    1562              92 :     EndObj();
    1563                 : 
    1564              92 :     oPageContext.poSrcDS = poSrcDS;
    1565              92 :     oPageContext.nPageId = nPageId;
    1566              92 :     oPageContext.nContentId = nContentId;
    1567              92 :     oPageContext.nResourcesId = nResourcesId;
    1568              92 :     oPageContext.dfDPI = dfDPI;
    1569              92 :     oPageContext.sMargins = *psMargins;
    1570              92 :     oPageContext.nOCGRasterId = 0;
    1571              92 :     oPageContext.eStreamCompressMethod = eStreamCompressMethod;
    1572                 : 
    1573              92 :     return TRUE;
    1574                 : }
    1575                 : 
    1576                 : /************************************************************************/
    1577                 : /*                             WriteColorTable()                        */
    1578                 : /************************************************************************/
    1579                 : 
    1580              92 : int GDALPDFWriter::WriteColorTable(GDALDataset* poSrcDS)
    1581                 : {
    1582                 :     /* Does the source image has a color table ? */
    1583              92 :     GDALColorTable* poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    1584              92 :     int nColorTableId = 0;
    1585              92 :     if (poCT != NULL && poCT->GetColorEntryCount() <= 256)
    1586                 :     {
    1587               2 :         int nColors = poCT->GetColorEntryCount();
    1588               2 :         nColorTableId = AllocNewObject();
    1589                 : 
    1590               2 :         int nLookupTableId = AllocNewObject();
    1591                 : 
    1592                 :         /* Index object */
    1593               2 :         StartObj(nColorTableId);
    1594                 :         {
    1595               2 :             GDALPDFArrayRW oArray;
    1596                 :             oArray.Add(GDALPDFObjectRW::CreateName("Indexed"))
    1597                 :                   .Add(&((new GDALPDFArrayRW())->Add(GDALPDFObjectRW::CreateName("DeviceRGB"))))
    1598                 :                   .Add(nColors-1)
    1599               2 :                   .Add(nLookupTableId, 0);
    1600               2 :             VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str());
    1601                 :         }
    1602               2 :         EndObj();
    1603                 : 
    1604                 :         /* Lookup table object */
    1605               2 :         StartObj(nLookupTableId);
    1606                 :         {
    1607               2 :             GDALPDFDictionaryRW oDict;
    1608               2 :             oDict.Add("Length", nColors * 3);
    1609               2 :             VSIFPrintfL(fp, "%s %% Lookup table\n", oDict.Serialize().c_str());
    1610                 :         }
    1611               2 :         VSIFPrintfL(fp, "stream\n");
    1612                 :         GByte pabyLookup[768];
    1613              34 :         for(int i=0;i<nColors;i++)
    1614                 :         {
    1615              32 :             const GDALColorEntry* poEntry = poCT->GetColorEntry(i);
    1616              32 :             pabyLookup[3 * i + 0] = (GByte)poEntry->c1;
    1617              32 :             pabyLookup[3 * i + 1] = (GByte)poEntry->c2;
    1618              32 :             pabyLookup[3 * i + 2] = (GByte)poEntry->c3;
    1619                 :         }
    1620               2 :         VSIFWriteL(pabyLookup, 3 * nColors, 1, fp);
    1621               2 :         VSIFPrintfL(fp, "\n");
    1622               2 :         VSIFPrintfL(fp, "endstream\n");
    1623               2 :         EndObj();
    1624                 :     }
    1625                 : 
    1626              92 :     return nColorTableId;
    1627                 : }
    1628                 : 
    1629                 : /************************************************************************/
    1630                 : /*                             WriteImagery()                           */
    1631                 : /************************************************************************/
    1632                 : 
    1633              90 : int GDALPDFWriter::WriteImagery(const char* pszLayerName,
    1634                 :                                 PDFCompressMethod eCompressMethod,
    1635                 :                                 int nPredictor,
    1636                 :                                 int nJPEGQuality,
    1637                 :                                 const char* pszJPEG2000_DRIVER,
    1638                 :                                 int nBlockXSize, int nBlockYSize,
    1639                 :                                 GDALProgressFunc pfnProgress,
    1640                 :                                 void * pProgressData)
    1641                 : {
    1642              90 :     GDALDataset* poSrcDS = oPageContext.poSrcDS;
    1643              90 :     int  nWidth = poSrcDS->GetRasterXSize();
    1644              90 :     int  nHeight = poSrcDS->GetRasterYSize();
    1645              90 :     double dfUserUnit = oPageContext.dfDPI / 72.0;
    1646                 : 
    1647              90 :     oPageContext.nOCGRasterId = WriteOCG(pszLayerName);
    1648                 : 
    1649                 :     /* Does the source image has a color table ? */
    1650              90 :     int nColorTableId = WriteColorTable(poSrcDS);
    1651                 : 
    1652              90 :     int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
    1653              90 :     int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
    1654              90 :     int nBlocks = nXBlocks * nYBlocks;
    1655                 :     int nBlockXOff, nBlockYOff;
    1656             188 :     for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
    1657                 :     {
    1658             224 :         for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++)
    1659                 :         {
    1660             126 :             int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize);
    1661             126 :             int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize);
    1662             126 :             int iImage = nBlockYOff * nXBlocks + nBlockXOff;
    1663                 : 
    1664                 :             void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks,
    1665                 :                                                           (iImage + 1) / (double)nBlocks,
    1666             126 :                                                           pfnProgress, pProgressData);
    1667                 : 
    1668                 :             int nImageId = WriteBlock(poSrcDS,
    1669                 :                                       nBlockXOff * nBlockXSize,
    1670                 :                                       nBlockYOff * nBlockYSize,
    1671                 :                                       nReqWidth, nReqHeight,
    1672                 :                                       nColorTableId,
    1673                 :                                       eCompressMethod,
    1674                 :                                       nPredictor,
    1675                 :                                       nJPEGQuality,
    1676                 :                                       pszJPEG2000_DRIVER,
    1677                 :                                       GDALScaledProgress,
    1678             126 :                                       pScaledData);
    1679                 : 
    1680             126 :             GDALDestroyScaledProgress(pScaledData);
    1681                 : 
    1682             126 :             if (nImageId == 0)
    1683               0 :                 return FALSE;
    1684                 : 
    1685                 :             GDALPDFImageDesc oImageDesc;
    1686             126 :             oImageDesc.nImageId = nImageId;
    1687             126 :             oImageDesc.dfXOff = (nBlockXOff * nBlockXSize) / dfUserUnit + oPageContext.sMargins.nLeft;
    1688             126 :             oImageDesc.dfYOff = (nHeight - nBlockYOff * nBlockYSize - nReqHeight) / dfUserUnit + oPageContext.sMargins.nBottom;
    1689             126 :             oImageDesc.dfXSize = nReqWidth / dfUserUnit;
    1690             126 :             oImageDesc.dfYSize = nReqHeight / dfUserUnit;
    1691                 : 
    1692             126 :             oPageContext.asImageDesc.push_back(oImageDesc);
    1693                 :         }
    1694                 :     }
    1695                 : 
    1696              90 :     return TRUE;
    1697                 : }
    1698                 : 
    1699                 : #ifdef OGR_ENABLED
    1700                 : 
    1701                 : /************************************************************************/
    1702                 : /*                          WriteOGRDataSource()                        */
    1703                 : /************************************************************************/
    1704                 : 
    1705               2 : int GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource,
    1706                 :                                       const char* pszOGRDisplayField,
    1707                 :                                       const char* pszOGRDisplayLayerNames,
    1708                 :                                       int bWriteOGRAttributes)
    1709                 : {
    1710               2 :     if (OGRGetDriverCount() == 0)
    1711               0 :         OGRRegisterAll();
    1712                 : 
    1713               2 :     OGRDataSourceH hDS = OGROpen(pszOGRDataSource, 0, NULL);
    1714               2 :     if (hDS == NULL)
    1715               0 :         return FALSE;
    1716                 : 
    1717               2 :     int iObj = 0;
    1718                 : 
    1719               2 :     int nLayers = OGR_DS_GetLayerCount(hDS);
    1720                 : 
    1721               2 :     char** papszLayerNames = CSLTokenizeString2(pszOGRDisplayLayerNames,",",0);
    1722                 : 
    1723               4 :     for(int iLayer = 0; iLayer < nLayers; iLayer ++)
    1724                 :     {
    1725               2 :         CPLString osLayerName;
    1726               2 :         if (CSLCount(papszLayerNames) < nLayers)
    1727               0 :             osLayerName = CPLSPrintf("Layer%d", iLayer + 1);
    1728                 :         else
    1729               2 :             osLayerName = papszLayerNames[iLayer];
    1730                 : 
    1731                 :         WriteOGRLayer(hDS, iLayer,
    1732                 :                       pszOGRDisplayField,
    1733                 :                       osLayerName,
    1734                 :                       bWriteOGRAttributes,
    1735               2 :                       iObj);
    1736                 :     }
    1737                 : 
    1738               2 :     OGRReleaseDataSource(hDS);
    1739                 : 
    1740               2 :     CSLDestroy(papszLayerNames);
    1741                 : 
    1742               2 :     return TRUE;
    1743                 : }
    1744                 : 
    1745                 : /************************************************************************/
    1746                 : /*                           StartOGRLayer()                            */
    1747                 : /************************************************************************/
    1748                 : 
    1749               4 : GDALPDFLayerDesc GDALPDFWriter::StartOGRLayer(CPLString osLayerName,
    1750                 :                                               int bWriteOGRAttributes)
    1751                 : {
    1752               4 :     GDALPDFLayerDesc osVectorDesc;
    1753               4 :     osVectorDesc.osLayerName = osLayerName;
    1754               4 :     osVectorDesc.bWriteOGRAttributes = bWriteOGRAttributes;
    1755               4 :     osVectorDesc.nOGCId = WriteOCG(osLayerName);
    1756               4 :     osVectorDesc.nFeatureLayerId = (bWriteOGRAttributes) ? AllocNewObject() : 0;
    1757               4 :     osVectorDesc.nOCGTextId = 0;
    1758                 : 
    1759               0 :     return osVectorDesc;
    1760                 : }
    1761                 : 
    1762                 : /************************************************************************/
    1763                 : /*                           EndOGRLayer()                              */
    1764                 : /************************************************************************/
    1765                 : 
    1766               4 : void GDALPDFWriter::EndOGRLayer(GDALPDFLayerDesc& osVectorDesc)
    1767                 : {
    1768               4 :     if (osVectorDesc.bWriteOGRAttributes)
    1769                 :     {
    1770               4 :         StartObj(osVectorDesc.nFeatureLayerId);
    1771                 : 
    1772               4 :         GDALPDFDictionaryRW oDict;
    1773                 :         oDict.Add("A", &(new GDALPDFDictionaryRW())->Add("O",
    1774               4 :                 GDALPDFObjectRW::CreateName("UserProperties")));
    1775                 : 
    1776               4 :         GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
    1777               4 :         oDict.Add("K", poArray);
    1778                 : 
    1779              18 :         for(int i = 0; i < (int)osVectorDesc.aUserPropertiesIds.size(); i++)
    1780                 :         {
    1781              14 :             poArray->Add(osVectorDesc.aUserPropertiesIds[i], 0);
    1782                 :         }
    1783                 : 
    1784               4 :         if (nStructTreeRootId == 0)
    1785               4 :             nStructTreeRootId = AllocNewObject();
    1786                 : 
    1787               4 :         oDict.Add("P", nStructTreeRootId, 0);
    1788               4 :         oDict.Add("S", GDALPDFObjectRW::CreateName(osVectorDesc.osLayerName));
    1789                 : 
    1790               4 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1791                 : 
    1792               4 :         EndObj();
    1793                 :     }
    1794                 : 
    1795               4 :     oPageContext.asVectorDesc.push_back(osVectorDesc);
    1796               4 : }
    1797                 : 
    1798                 : /************************************************************************/
    1799                 : /*                           WriteOGRLayer()                            */
    1800                 : /************************************************************************/
    1801                 : 
    1802               2 : int GDALPDFWriter::WriteOGRLayer(OGRDataSourceH hDS,
    1803                 :                                  int iLayer,
    1804                 :                                  const char* pszOGRDisplayField,
    1805                 :                                  CPLString osLayerName,
    1806                 :                                  int bWriteOGRAttributes,
    1807                 :                                  int& iObj)
    1808                 : {
    1809                 :     GDALPDFLayerDesc osVectorDesc = StartOGRLayer(osLayerName,
    1810               2 :                                                   bWriteOGRAttributes);
    1811               2 :     OGRLayerH hLyr = OGR_DS_GetLayer(hDS, iLayer);
    1812                 : 
    1813                 :     OGRFeatureH hFeat;
    1814               2 :     int iObjLayer = 0;
    1815              10 :     while( (hFeat = OGR_L_GetNextFeature(hLyr)) != NULL)
    1816                 :     {
    1817                 :         WriteOGRFeature(osVectorDesc,
    1818                 :                         hFeat,
    1819                 :                         pszOGRDisplayField,
    1820                 :                         bWriteOGRAttributes,
    1821                 :                         iObj,
    1822               6 :                         iObjLayer);
    1823                 : 
    1824               6 :         OGR_F_Destroy(hFeat);
    1825                 :     }
    1826                 : 
    1827               2 :     EndOGRLayer(osVectorDesc);
    1828                 : 
    1829               2 :     return TRUE;
    1830                 : }
    1831                 : 
    1832                 : /************************************************************************/
    1833                 : /*                             DrawGeometry()                           */
    1834                 : /************************************************************************/
    1835                 : 
    1836              26 : static void DrawGeometry(VSILFILE* fp, OGRGeometryH hGeom, double adfMatrix[4], int bPaint = TRUE)
    1837                 : {
    1838              26 :     switch(wkbFlatten(OGR_G_GetGeometryType(hGeom)))
    1839                 :     {
    1840                 :         case wkbLineString:
    1841                 :         {
    1842              16 :             int nPoints = OGR_G_GetPointCount(hGeom);
    1843              84 :             for(int i=0;i<nPoints;i++)
    1844                 :             {
    1845              68 :                 double dfX = OGR_G_GetX(hGeom, i) * adfMatrix[1] + adfMatrix[0];
    1846              68 :                 double dfY = OGR_G_GetY(hGeom, i) * adfMatrix[3] + adfMatrix[2];
    1847              68 :                 VSIFPrintfL(fp, "%f %f %c\n", dfX, dfY, (i == 0) ? 'm' : 'l');
    1848                 :             }
    1849              16 :             if (bPaint)
    1850               4 :                 VSIFPrintfL(fp, "S\n");
    1851              16 :             break;
    1852                 :         }
    1853                 : 
    1854                 :         case wkbPolygon:
    1855                 :         {
    1856               8 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    1857              20 :             for(int i=0;i<nParts;i++)
    1858                 :             {
    1859              12 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    1860              12 :                 VSIFPrintfL(fp, "h\n");
    1861                 :             }
    1862               8 :             if (bPaint)
    1863               4 :                 VSIFPrintfL(fp, "b*\n");
    1864               8 :             break;
    1865                 :         }
    1866                 : 
    1867                 :         case wkbMultiLineString:
    1868                 :         {
    1869               0 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    1870               0 :             for(int i=0;i<nParts;i++)
    1871                 :             {
    1872               0 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    1873                 :             }
    1874               0 :             if (bPaint)
    1875               0 :                 VSIFPrintfL(fp, "S\n");
    1876               0 :             break;
    1877                 :         }
    1878                 : 
    1879                 :         case wkbMultiPolygon:
    1880                 :         {
    1881               2 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    1882               6 :             for(int i=0;i<nParts;i++)
    1883                 :             {
    1884               4 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    1885                 :             }
    1886               2 :             if (bPaint)
    1887               2 :                 VSIFPrintfL(fp, "b*\n");
    1888                 :             break;
    1889                 :         }
    1890                 : 
    1891                 :         default:
    1892                 :             break;
    1893                 :     }
    1894              26 : }
    1895                 : 
    1896                 : /************************************************************************/
    1897                 : /*                          WriteOGRFeature()                           */
    1898                 : /************************************************************************/
    1899                 : 
    1900              14 : int GDALPDFWriter::WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc,
    1901                 :                                    OGRFeatureH hFeat,
    1902                 :                                    const char* pszOGRDisplayField,
    1903                 :                                    int bWriteOGRAttributes,
    1904                 :                                    int& iObj,
    1905                 :                                    int& iObjLayer)
    1906                 : {
    1907              14 :     GDALDataset* poSrcDS = oPageContext.poSrcDS;
    1908              14 :     int  nHeight = poSrcDS->GetRasterYSize();
    1909              14 :     double dfUserUnit = oPageContext.dfDPI / 72.0;
    1910                 :     double adfGeoTransform[6];
    1911              14 :     poSrcDS->GetGeoTransform(adfGeoTransform);
    1912                 : 
    1913                 :     double adfMatrix[4];
    1914              14 :     adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft;
    1915              14 :     adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit);
    1916              14 :     adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom;
    1917              14 :     adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit);
    1918                 : 
    1919              14 :     double dfRadius = 5 * dfUserUnit;
    1920                 : 
    1921              14 :     OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
    1922              14 :     if (hGeom == NULL)
    1923                 :     {
    1924               0 :         return TRUE;
    1925                 :     }
    1926                 : 
    1927                 :     /* -------------------------------------------------------------- */
    1928                 :     /*  Get envelope                                                  */
    1929                 :     /* -------------------------------------------------------------- */
    1930              14 :     OGREnvelope sEnvelope;
    1931              14 :     OGR_G_GetEnvelope(hGeom, &sEnvelope);
    1932                 : 
    1933                 : 
    1934                 :     /* -------------------------------------------------------------- */
    1935                 :     /*  Get style                                                     */
    1936                 :     /* -------------------------------------------------------------- */
    1937              14 :     int nPenR = 0, nPenG = 0, nPenB = 0, nPenA = 255;
    1938              14 :     int nBrushR = 127, nBrushG = 127, nBrushB = 127, nBrushA = 127;
    1939              14 :     int nTextR = 0, nTextG = 0, nTextB = 0, nTextA = 255;
    1940              14 :     double dfTextSize = 12, dfTextAngle = 0;
    1941              14 :     double dfPenWidth = 1;
    1942              14 :     CPLString osDashArray;
    1943              14 :     CPLString osLabelText;
    1944                 : 
    1945              14 :     OGRStyleMgrH hSM = OGR_SM_Create(NULL);
    1946              14 :     OGR_SM_InitFromFeature(hSM, hFeat);
    1947              14 :     int nCount = OGR_SM_GetPartCount(hSM, NULL);
    1948              24 :     for(int iPart = 0; iPart < nCount; iPart++)
    1949                 :     {
    1950              10 :         OGRStyleToolH hTool = OGR_SM_GetPart(hSM, iPart, NULL);
    1951              10 :         if (hTool)
    1952                 :         {
    1953               8 :             if (OGR_ST_GetType(hTool) == OGRSTCPen)
    1954                 :             {
    1955               4 :                 int bIsNull = TRUE;
    1956               4 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTPenColor, &bIsNull);
    1957               4 :                 if (pszColor && !bIsNull)
    1958                 :                 {
    1959               4 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    1960               4 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    1961               4 :                     if (nVals >= 3)
    1962                 :                     {
    1963               4 :                         nPenR = nRed;
    1964               4 :                         nPenG = nGreen;
    1965               4 :                         nPenB = nBlue;
    1966               4 :                         if (nVals == 4)
    1967               0 :                             nPenA = nAlpha;
    1968                 :                     }
    1969                 :                 }
    1970                 : 
    1971               4 :                 const char* pszDash = OGR_ST_GetParamStr(hTool, OGRSTPenPattern, &bIsNull);
    1972               4 :                 if (pszDash && !bIsNull)
    1973                 :                 {
    1974               2 :                     char** papszTokens = CSLTokenizeString2(pszDash, " ", 0);
    1975               2 :                     int nTokens = CSLCount(papszTokens);
    1976               2 :                     if ((nTokens % 2) == 0)
    1977                 :                     {
    1978               6 :                         for(int i=0;i<nTokens;i++)
    1979                 :                         {
    1980               4 :                             osDashArray += CPLSPrintf("%d ", atoi(papszTokens[i]));
    1981                 :                         }
    1982                 :                     }
    1983               2 :                     CSLDestroy(papszTokens);
    1984                 :                 }
    1985                 : 
    1986                 :                 //OGRSTUnitId eUnit = OGR_ST_GetUnit(hTool);
    1987               4 :                 double dfWidth = OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bIsNull);
    1988               4 :                 if (!bIsNull)
    1989               4 :                     dfPenWidth = dfWidth;
    1990                 :             }
    1991               4 :             else if (OGR_ST_GetType(hTool) == OGRSTCBrush)
    1992                 :             {
    1993                 :                 int bIsNull;
    1994               2 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTBrushFColor, &bIsNull);
    1995               2 :                 if (pszColor)
    1996                 :                 {
    1997               2 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    1998               2 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    1999               2 :                     if (nVals >= 3)
    2000                 :                     {
    2001               2 :                         nBrushR = nRed;
    2002               2 :                         nBrushG = nGreen;
    2003               2 :                         nBrushB = nBlue;
    2004               2 :                         if (nVals == 4)
    2005               0 :                             nBrushA = nAlpha;
    2006                 :                     }
    2007                 :                 }
    2008                 :             }
    2009               2 :             else if (OGR_ST_GetType(hTool) == OGRSTCLabel)
    2010                 :             {
    2011                 :                 int bIsNull;
    2012               2 :                 const char* pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelTextString, &bIsNull);
    2013               2 :                 if (pszStr)
    2014                 :                 {
    2015               2 :                     osLabelText = pszStr;
    2016                 :                 }
    2017                 : 
    2018               2 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTLabelFColor, &bIsNull);
    2019               2 :                 if (pszColor && !bIsNull)
    2020                 :                 {
    2021               2 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    2022               2 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    2023               2 :                     if (nVals >= 3)
    2024                 :                     {
    2025               2 :                         nTextR = nRed;
    2026               2 :                         nTextG = nGreen;
    2027               2 :                         nTextB = nBlue;
    2028               2 :                         if (nVals == 4)
    2029               2 :                             nTextA = nAlpha;
    2030                 :                     }
    2031                 :                 }
    2032                 : 
    2033               2 :                 double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelSize, &bIsNull);
    2034               2 :                 if (!bIsNull)
    2035                 :                 {
    2036               2 :                     dfTextSize = dfVal;
    2037                 :                 }
    2038                 : 
    2039               2 :                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelAngle, &bIsNull);
    2040               2 :                 if (!bIsNull)
    2041                 :                 {
    2042               2 :                     dfTextAngle = dfVal;
    2043                 :                 }
    2044                 :             }
    2045               8 :             OGR_ST_Destroy(hTool);
    2046                 :         }
    2047                 :     }
    2048              14 :     OGR_SM_Destroy(hSM);
    2049                 : 
    2050                 :     /* -------------------------------------------------------------- */
    2051                 :     /*  Write object dictionary                                       */
    2052                 :     /* -------------------------------------------------------------- */
    2053              14 :     int nObjectId = AllocNewObject();
    2054              14 :     int nObjectLengthId = AllocNewObject();
    2055                 : 
    2056              14 :     osVectorDesc.aIds.push_back(nObjectId);
    2057                 : 
    2058              14 :     StartObj(nObjectId);
    2059                 : 
    2060                 :     {
    2061              14 :         GDALPDFDictionaryRW oDict;
    2062              14 :         double dfMargin = (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint) ? dfRadius + dfPenWidth : dfPenWidth;
    2063                 :         oDict.Add("Length", nObjectLengthId, 0)
    2064                 :             .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    2065                 :             .Add("BBox", &((new GDALPDFArrayRW())
    2066              28 :                             ->Add((int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin)).
    2067              28 :                                 Add((int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin)).
    2068              28 :                                 Add((int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin)).
    2069              28 :                                 Add((int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin))))
    2070             126 :             .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
    2071              14 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2072                 :         {
    2073              14 :             oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2074                 :         }
    2075                 : 
    2076              14 :         GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
    2077              28 :         poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
    2078              14 :         if (nPenA != 255)
    2079               0 :             poGS1->Add("CA", (nPenA == 127 || nPenA == 128) ? 0.5 : nPenA / 255.0);
    2080              14 :         if (nBrushA != 255)
    2081              14 :             poGS1->Add("ca", (nBrushA == 127 || nBrushA == 128) ? 0.5 : nBrushA / 255.0 );
    2082                 : 
    2083              14 :         GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
    2084              14 :         poExtGState->Add("GS1", poGS1);
    2085                 : 
    2086              14 :         GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
    2087              14 :         poResources->Add("ExtGState", poExtGState);
    2088                 : 
    2089              14 :         oDict.Add("Resources", poResources);
    2090                 : 
    2091              14 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2092                 :     }
    2093                 : 
    2094                 :     /* -------------------------------------------------------------- */
    2095                 :     /*  Write object stream                                           */
    2096                 :     /* -------------------------------------------------------------- */
    2097              14 :     VSIFPrintfL(fp, "stream\n");
    2098                 : 
    2099              14 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    2100                 : 
    2101              14 :     VSILFILE* fpGZip = NULL;
    2102              14 :     VSILFILE* fpBack = fp;
    2103              14 :     if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2104                 :     {
    2105              14 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2106              14 :         fp = fpGZip;
    2107                 :     }
    2108                 : 
    2109              14 :     VSIFPrintfL(fp, "q\n");
    2110                 : 
    2111              14 :     VSIFPrintfL(fp, "/GS1 gs\n");
    2112                 : 
    2113                 :     VSIFPrintfL(fp, "%f w\n"
    2114                 :                     "0 J\n"
    2115                 :                     "0 j\n"
    2116                 :                     "10 M\n"
    2117                 :                     "[%s]0 d\n",
    2118                 :                     dfPenWidth,
    2119              14 :                     osDashArray.c_str());
    2120                 : 
    2121              14 :     VSIFPrintfL(fp, "%f %f %f RG\n", nPenR / 255.0, nPenG / 255.0, nPenB / 255.0);
    2122              14 :     VSIFPrintfL(fp, "%f %f %f rg\n", nBrushR / 255.0, nBrushG / 255.0, nBrushB / 255.0);
    2123                 : 
    2124              14 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
    2125                 :     {
    2126               4 :         double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0];
    2127               4 :         double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2];
    2128                 : 
    2129                 :         /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */
    2130               4 :         const double dfKappa = 0.5522847498;
    2131                 : 
    2132               4 :         VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY);
    2133                 :         VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2134                 :                     dfX - dfRadius, dfY - dfRadius * dfKappa,
    2135                 :                     dfX - dfRadius * dfKappa, dfY - dfRadius,
    2136               4 :                     dfX, dfY - dfRadius);
    2137                 :         VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2138                 :                     dfX + dfRadius * dfKappa, dfY - dfRadius,
    2139                 :                     dfX + dfRadius, dfY - dfRadius * dfKappa,
    2140               4 :                     dfX + dfRadius, dfY);
    2141                 :         VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2142                 :                     dfX + dfRadius, dfY + dfRadius * dfKappa,
    2143                 :                     dfX + dfRadius * dfKappa, dfY + dfRadius,
    2144               4 :                     dfX, dfY + dfRadius);
    2145                 :         VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2146                 :                     dfX - dfRadius * dfKappa, dfY + dfRadius,
    2147                 :                     dfX - dfRadius, dfY + dfRadius * dfKappa,
    2148               4 :                     dfX - dfRadius, dfY);
    2149               4 :         VSIFPrintfL(fp, "b*\n");
    2150                 :     }
    2151                 :     else
    2152                 :     {
    2153              10 :         DrawGeometry(fp, hGeom, adfMatrix);
    2154                 :     }
    2155                 : 
    2156              14 :     VSIFPrintfL(fp, "Q");
    2157                 : 
    2158              14 :     if (fpGZip)
    2159              14 :         VSIFCloseL(fpGZip);
    2160              14 :     fp = fpBack;
    2161                 : 
    2162              14 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2163              14 :     VSIFPrintfL(fp, "\n");
    2164              14 :     VSIFPrintfL(fp, "endstream\n");
    2165              14 :     EndObj();
    2166                 : 
    2167              14 :     StartObj(nObjectLengthId);
    2168                 :     VSIFPrintfL(fp,
    2169                 :                 "   %ld\n",
    2170              14 :                 (long)(nStreamEnd - nStreamStart));
    2171              14 :     EndObj();
    2172                 : 
    2173                 :     /* -------------------------------------------------------------- */
    2174                 :     /*  Write label                                                   */
    2175                 :     /* -------------------------------------------------------------- */
    2176              14 :     if (osLabelText.size() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
    2177                 :     {
    2178               2 :         if (osVectorDesc.nOCGTextId == 0)
    2179               2 :             osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOGCId);
    2180                 : 
    2181                 :         /* -------------------------------------------------------------- */
    2182                 :         /*  Write object dictionary                                       */
    2183                 :         /* -------------------------------------------------------------- */
    2184               2 :         nObjectId = AllocNewObject();
    2185               2 :         nObjectLengthId = AllocNewObject();
    2186                 : 
    2187               2 :         osVectorDesc.aIdsText.push_back(nObjectId);
    2188                 : 
    2189               2 :         StartObj(nObjectId);
    2190                 :         {
    2191               2 :             GDALPDFDictionaryRW oDict;
    2192                 : 
    2193               2 :             GDALDataset* poSrcDS = oPageContext.poSrcDS;
    2194               2 :             int  nWidth = poSrcDS->GetRasterXSize();
    2195               2 :             int  nHeight = poSrcDS->GetRasterYSize();
    2196               2 :             double dfUserUnit = oPageContext.dfDPI / 72.0;
    2197               2 :             double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight;
    2198               2 :             double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop;
    2199                 : 
    2200                 :             oDict.Add("Length", nObjectLengthId, 0)
    2201                 :                 .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    2202                 :                 .Add("BBox", &((new GDALPDFArrayRW())
    2203                 :                                 ->Add(0).Add(0)).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit))
    2204               2 :                 .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
    2205               2 :             if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2206                 :             {
    2207               2 :                 oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2208                 :             }
    2209                 : 
    2210               2 :             GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
    2211                 : 
    2212               2 :             if (nTextA != 255)
    2213                 :             {
    2214               2 :                 GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
    2215               4 :                 poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
    2216               2 :                 poGS1->Add("ca", (nTextA == 127 || nTextA == 128) ? 0.5 : nTextA / 255.0);
    2217                 : 
    2218               2 :                 GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
    2219               2 :                 poExtGState->Add("GS1", poGS1);
    2220                 : 
    2221               2 :                 poResources->Add("ExtGState", poExtGState);
    2222                 :             }
    2223                 : 
    2224               2 :             GDALPDFDictionaryRW* poDictFTimesRoman = NULL;
    2225               2 :             poDictFTimesRoman = new GDALPDFDictionaryRW();
    2226               4 :             poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    2227               2 :             poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman"));
    2228               2 :             poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    2229               2 :             poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    2230                 : 
    2231               2 :             GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
    2232               2 :             if (poDictFTimesRoman)
    2233               2 :                 poDictFont->Add("FTimesRoman", poDictFTimesRoman);
    2234               2 :             poResources->Add("Font", poDictFont);
    2235                 : 
    2236               2 :             oDict.Add("Resources", poResources);
    2237                 : 
    2238               2 :             VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2239                 :         }
    2240                 : 
    2241                 :         /* -------------------------------------------------------------- */
    2242                 :         /*  Write object stream                                           */
    2243                 :         /* -------------------------------------------------------------- */
    2244               2 :         VSIFPrintfL(fp, "stream\n");
    2245                 : 
    2246               2 :         vsi_l_offset nStreamStart = VSIFTellL(fp);
    2247                 : 
    2248               2 :         VSILFILE* fpGZip = NULL;
    2249               2 :         VSILFILE* fpBack = fp;
    2250               2 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2251                 :         {
    2252               2 :             fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2253               2 :             fp = fpGZip;
    2254                 :         }
    2255                 : 
    2256               2 :         double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0];
    2257               2 :         double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2];
    2258                 : 
    2259               2 :         VSIFPrintfL(fp, "q\n");
    2260               2 :         VSIFPrintfL(fp, "BT\n");
    2261               2 :         if (nTextA != 255)
    2262                 :         {
    2263               2 :             VSIFPrintfL(fp, "/GS1 gs\n");
    2264                 :         }
    2265               2 :         if (dfTextAngle == 0)
    2266                 :         {
    2267               0 :             VSIFPrintfL(fp, "%f %f Td\n", dfX, dfY);
    2268                 :         }
    2269                 :         else
    2270                 :         {
    2271               2 :             dfTextAngle = - dfTextAngle * M_PI / 180.0;
    2272                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f Tm\n",
    2273                 :                         cos(dfTextAngle), -sin(dfTextAngle),
    2274                 :                         sin(dfTextAngle), cos(dfTextAngle),
    2275               2 :                         dfX, dfY);
    2276                 :         }
    2277               2 :         VSIFPrintfL(fp, "%f %f %f rg\n", nTextR / 255.0, nTextG / 255.0, nTextB / 255.0);
    2278               2 :         VSIFPrintfL(fp, "/FTimesRoman %f Tf\n", dfTextSize);
    2279               2 :         VSIFPrintfL(fp, "(");
    2280              26 :         for(size_t i=0;i<osLabelText.size();i++)
    2281                 :         {
    2282                 :             /*if (osLabelText[i] == '\n')
    2283                 :                 VSIFPrintfL(fp, ") Tj T* (");
    2284              24 :             else */if (osLabelText[i] >= 32 && osLabelText[i] <= 127)
    2285              24 :                 VSIFPrintfL(fp, "%c", osLabelText[i]);
    2286                 :             else
    2287               0 :                 VSIFPrintfL(fp, "_");
    2288                 :         }
    2289               2 :         VSIFPrintfL(fp, ") Tj\n");
    2290               2 :         VSIFPrintfL(fp, "ET\n");
    2291               2 :         VSIFPrintfL(fp, "Q");
    2292                 : 
    2293               2 :         if (fpGZip)
    2294               2 :             VSIFCloseL(fpGZip);
    2295               2 :         fp = fpBack;
    2296                 : 
    2297               2 :         vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2298               2 :         VSIFPrintfL(fp, "\n");
    2299               2 :         VSIFPrintfL(fp, "endstream\n");
    2300               2 :         EndObj();
    2301                 : 
    2302               2 :         StartObj(nObjectLengthId);
    2303                 :         VSIFPrintfL(fp,
    2304                 :                     "   %ld\n",
    2305               2 :                     (long)(nStreamEnd - nStreamStart));
    2306               2 :         EndObj();
    2307                 :     }
    2308                 :     else
    2309                 :     {
    2310              12 :         osVectorDesc.aIdsText.push_back(0);
    2311                 :     }
    2312                 : 
    2313                 :     /* -------------------------------------------------------------- */
    2314                 :     /*  Write feature attributes                                      */
    2315                 :     /* -------------------------------------------------------------- */
    2316              14 :     int nFeatureUserProperties = 0;
    2317                 : 
    2318              14 :     int iField = -1;
    2319              14 :     CPLString osFeatureName;
    2320                 : 
    2321              14 :     if (bWriteOGRAttributes)
    2322                 :     {
    2323              14 :         if (pszOGRDisplayField &&
    2324                 :             (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField)) >= 0)
    2325               6 :             osFeatureName = OGR_F_GetFieldAsString(hFeat, iField);
    2326                 :         else
    2327               8 :             osFeatureName = CPLSPrintf("feature%d", iObjLayer + 1);
    2328                 : 
    2329              14 :         nFeatureUserProperties = AllocNewObject();
    2330              14 :         StartObj(nFeatureUserProperties);
    2331                 : 
    2332              14 :         GDALPDFDictionaryRW oDict;
    2333              14 :         GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW();
    2334              14 :         oDict.Add("A", poDictA);
    2335              14 :         poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties"));
    2336                 : 
    2337              14 :         int nFields = OGR_F_GetFieldCount(hFeat);
    2338              14 :         GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
    2339              56 :         for(int i = 0; i < nFields; i++)
    2340                 :         {
    2341              42 :             if (OGR_F_IsFieldSet(hFeat, i))
    2342                 :             {
    2343              24 :                 OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i );
    2344              24 :                 GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW();
    2345              48 :                 poKV->Add("N", OGR_Fld_GetNameRef(hFDefn));
    2346              24 :                 if (OGR_Fld_GetType(hFDefn) == OFTInteger)
    2347               8 :                     poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i));
    2348              16 :                 else if (OGR_Fld_GetType(hFDefn) == OFTReal)
    2349               2 :                     poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i));
    2350                 :                 else
    2351              14 :                     poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i));
    2352              24 :                 poArray->Add(poKV);
    2353                 :             }
    2354                 :         }
    2355                 : 
    2356              14 :         poDictA->Add("P", poArray);
    2357                 : 
    2358              14 :         oDict.Add("K", iObj);
    2359              14 :         oDict.Add("P", osVectorDesc.nFeatureLayerId, 0);
    2360              14 :         oDict.Add("Pg", oPageContext.nPageId, 0);
    2361              14 :         oDict.Add("S", GDALPDFObjectRW::CreateName("feature"));
    2362              14 :         oDict.Add("T", osFeatureName);
    2363                 : 
    2364              14 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2365                 : 
    2366              14 :         EndObj();
    2367                 :     }
    2368                 : 
    2369              14 :     iObj ++;
    2370              14 :     iObjLayer ++;
    2371                 : 
    2372              14 :     osVectorDesc.aUserPropertiesIds.push_back(nFeatureUserProperties);
    2373              14 :     osVectorDesc.aFeatureNames.push_back(osFeatureName);
    2374                 : 
    2375              14 :     return TRUE;
    2376                 : }
    2377                 : 
    2378                 : #endif
    2379                 : 
    2380                 : /************************************************************************/
    2381                 : /*                               EndPage()                              */
    2382                 : /************************************************************************/
    2383                 : 
    2384              92 : int GDALPDFWriter::EndPage(const char* pszExtraImages,
    2385                 :                            const char* pszExtraStream,
    2386                 :                            const char* pszExtraLayerName)
    2387                 : {
    2388              92 :     int nLayerExtraId = WriteOCG(pszExtraLayerName);
    2389                 : 
    2390              92 :     int bHasTimesRoman = pszExtraStream && strstr(pszExtraStream, "/FTimesRoman");
    2391              92 :     int bHasTimesBold = pszExtraStream && strstr(pszExtraStream, "/FTimesBold");
    2392                 : 
    2393                 :     /* -------------------------------------------------------------- */
    2394                 :     /*  Write extra images                                            */
    2395                 :     /* -------------------------------------------------------------- */
    2396              92 :     std::vector<GDALPDFImageDesc> asExtraImageDesc;
    2397              92 :     if (pszExtraImages)
    2398                 :     {
    2399               2 :         char** papszExtraImagesTokens = CSLTokenizeString2(pszExtraImages, ",", 0);
    2400               2 :         int nCount = CSLCount(papszExtraImagesTokens);
    2401               2 :         if ((nCount % 4) == 0)
    2402                 :         {
    2403               2 :             double dfUserUnit = oPageContext.dfDPI / 72.0;
    2404               4 :             for(int i=0;i<nCount;i+=4)
    2405                 :             {
    2406               2 :                 const char* pszImageFilename = papszExtraImagesTokens[i+0];
    2407               2 :                 double dfX = atof(papszExtraImagesTokens[i+1]);
    2408               2 :                 double dfY = atof(papszExtraImagesTokens[i+2]);
    2409               2 :                 double dfScale = atof(papszExtraImagesTokens[i+3]);
    2410               2 :                 GDALDataset* poImageDS = (GDALDataset* )GDALOpen(pszImageFilename, GA_ReadOnly);
    2411               2 :                 if (poImageDS)
    2412                 :                 {
    2413               2 :                     int nColorTableId = WriteColorTable(poImageDS);
    2414                 :                     int nImageId = WriteBlock( poImageDS,
    2415                 :                                                0, 0,
    2416                 :                                                poImageDS->GetRasterXSize(),
    2417                 :                                                poImageDS->GetRasterYSize(),
    2418                 :                                                nColorTableId,
    2419                 :                                                COMPRESS_DEFAULT,
    2420                 :                                                0,
    2421                 :                                                -1,
    2422                 :                                                NULL,
    2423                 :                                                NULL,
    2424               2 :                                                NULL );
    2425                 : 
    2426               2 :                     if (nImageId)
    2427                 :                     {
    2428                 :                         GDALPDFImageDesc oImageDesc;
    2429               2 :                         oImageDesc.nImageId = nImageId;
    2430               2 :                         oImageDesc.dfXSize = poImageDS->GetRasterXSize() / dfUserUnit * dfScale;
    2431               2 :                         oImageDesc.dfYSize = poImageDS->GetRasterYSize() / dfUserUnit * dfScale;
    2432               2 :                         oImageDesc.dfXOff = dfX;
    2433               2 :                         oImageDesc.dfYOff = dfY - oImageDesc.dfYSize;
    2434                 : 
    2435               2 :                         asExtraImageDesc.push_back(oImageDesc);
    2436                 :                     }
    2437                 : 
    2438               2 :                     GDALClose(poImageDS);
    2439                 :                 }
    2440                 :             }
    2441                 :         }
    2442               2 :         CSLDestroy(papszExtraImagesTokens);
    2443                 :     }
    2444                 : 
    2445                 :     /* -------------------------------------------------------------- */
    2446                 :     /*  Write content dictionary                                      */
    2447                 :     /* -------------------------------------------------------------- */
    2448              92 :     int nContentLengthId = AllocNewObject();
    2449                 : 
    2450              92 :     StartObj(oPageContext.nContentId);
    2451                 :     {
    2452              92 :         GDALPDFDictionaryRW oDict;
    2453              92 :         oDict.Add("Length", nContentLengthId, 0);
    2454              92 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2455                 :         {
    2456              88 :             oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2457                 :         }
    2458              92 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2459                 :     }
    2460                 : 
    2461                 :     /* -------------------------------------------------------------- */
    2462                 :     /*  Write content stream                                          */
    2463                 :     /* -------------------------------------------------------------- */
    2464              92 :     VSIFPrintfL(fp, "stream\n");
    2465              92 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    2466                 : 
    2467              92 :     VSILFILE* fpGZip = NULL;
    2468              92 :     VSILFILE* fpBack = fp;
    2469              92 :     if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2470                 :     {
    2471              88 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2472              88 :         fp = fpGZip;
    2473                 :     }
    2474                 : 
    2475                 :     /* -------------------------------------------------------------- */
    2476                 :     /*  Write drawing instructions for raster blocks                  */
    2477                 :     /* -------------------------------------------------------------- */
    2478              92 :     if (oPageContext.nOCGRasterId)
    2479               2 :         VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oPageContext.nOCGRasterId);
    2480                 : 
    2481             218 :     for(size_t iImage = 0; iImage < oPageContext.asImageDesc.size(); iImage ++)
    2482                 :     {
    2483             126 :         VSIFPrintfL(fp, "q\n");
    2484             126 :         GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(oPageContext.asImageDesc[iImage].dfXSize);
    2485             126 :         GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(oPageContext.asImageDesc[iImage].dfYSize);
    2486             126 :         GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(oPageContext.asImageDesc[iImage].dfXOff);
    2487             126 :         GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(oPageContext.asImageDesc[iImage].dfYOff);
    2488                 :         VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
    2489                 :                     poXSize->Serialize().c_str(),
    2490                 :                     poYSize->Serialize().c_str(),
    2491                 :                     poXOff->Serialize().c_str(),
    2492             126 :                     poYOff->Serialize().c_str());
    2493             126 :         delete poXSize;
    2494             126 :         delete poYSize;
    2495             126 :         delete poXOff;
    2496             126 :         delete poYOff;
    2497                 :         VSIFPrintfL(fp, "/Image%d Do\n",
    2498             126 :                     oPageContext.asImageDesc[iImage].nImageId);
    2499             126 :         VSIFPrintfL(fp, "Q\n");
    2500                 :     }
    2501                 : 
    2502              92 :     if (oPageContext.nOCGRasterId)
    2503               2 :         VSIFPrintfL(fp, "EMC\n");
    2504                 : 
    2505                 :     /* -------------------------------------------------------------- */
    2506                 :     /*  Write drawing instructions for vector features                */
    2507                 :     /* -------------------------------------------------------------- */
    2508              92 :     int iObj = 0;
    2509              96 :     for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    2510                 :     {
    2511               4 :         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    2512                 : 
    2513               4 :         VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
    2514                 : 
    2515              18 :         for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    2516                 :         {
    2517              14 :             CPLString osName = oLayerDesc.aFeatureNames[iVector];
    2518              14 :             if (osName.size())
    2519                 :             {
    2520                 :                 VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
    2521              14 :                             iObj);
    2522                 :             }
    2523                 : 
    2524              14 :             iObj ++;
    2525                 : 
    2526              14 :             VSIFPrintfL(fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector]);
    2527                 : 
    2528              14 :             if (osName.size())
    2529                 :             {
    2530              14 :                 VSIFPrintfL(fp, "EMC\n");
    2531                 :             }
    2532                 :         }
    2533                 : 
    2534               4 :         VSIFPrintfL(fp, "EMC\n");
    2535                 :     }
    2536                 : 
    2537                 :     /* -------------------------------------------------------------- */
    2538                 :     /*  Write drawing instructions for labels of vector features      */
    2539                 :     /* -------------------------------------------------------------- */
    2540              92 :     iObj = 0;
    2541              96 :     for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    2542                 :     {
    2543               4 :         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    2544               4 :         if (oLayerDesc.nOCGTextId)
    2545                 :         {
    2546               2 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
    2547               2 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId);
    2548                 : 
    2549               8 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    2550                 :             {
    2551               6 :                 if (oLayerDesc.aIdsText[iVector])
    2552                 :                 {
    2553               2 :                     CPLString osName = oLayerDesc.aFeatureNames[iVector];
    2554               2 :                     if (osName.size())
    2555                 :                     {
    2556                 :                         VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
    2557               2 :                                     iObj);
    2558                 :                     }
    2559                 : 
    2560               2 :                     VSIFPrintfL(fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector]);
    2561                 : 
    2562               2 :                     if (osName.size())
    2563                 :                     {
    2564               2 :                         VSIFPrintfL(fp, "EMC\n");
    2565               2 :                     }
    2566                 :                 }
    2567                 : 
    2568               6 :                 iObj ++;
    2569                 :             }
    2570                 : 
    2571               2 :             VSIFPrintfL(fp, "EMC\n");
    2572               2 :             VSIFPrintfL(fp, "EMC\n");
    2573                 :         }
    2574                 :         else
    2575               2 :             iObj += oLayerDesc.aIds.size();
    2576                 :     }
    2577                 : 
    2578                 :     /* -------------------------------------------------------------- */
    2579                 :     /*  Write drawing instructions for extra content.                 */
    2580                 :     /* -------------------------------------------------------------- */
    2581              92 :     if (pszExtraStream || asExtraImageDesc.size())
    2582                 :     {
    2583               2 :         if (nLayerExtraId)
    2584               2 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", nLayerExtraId);
    2585                 : 
    2586                 :         /* -------------------------------------------------------------- */
    2587                 :         /*  Write drawing instructions for extra images.                  */
    2588                 :         /* -------------------------------------------------------------- */
    2589               4 :         for(size_t iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
    2590                 :         {
    2591               2 :             VSIFPrintfL(fp, "q\n");
    2592               2 :             GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXSize);
    2593               2 :             GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYSize);
    2594               2 :             GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXOff);
    2595               2 :             GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYOff);
    2596                 :             VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
    2597                 :                         poXSize->Serialize().c_str(),
    2598                 :                         poYSize->Serialize().c_str(),
    2599                 :                         poXOff->Serialize().c_str(),
    2600               2 :                         poYOff->Serialize().c_str());
    2601               2 :             delete poXSize;
    2602               2 :             delete poYSize;
    2603               2 :             delete poXOff;
    2604               2 :             delete poYOff;
    2605                 :             VSIFPrintfL(fp, "/Image%d Do\n",
    2606               2 :                         asExtraImageDesc[iImage].nImageId);
    2607               2 :             VSIFPrintfL(fp, "Q\n");
    2608                 :         }
    2609                 : 
    2610               2 :         if (pszExtraStream)
    2611               2 :             VSIFPrintfL(fp, "%s\n", pszExtraStream);
    2612                 : 
    2613               2 :         if (nLayerExtraId)
    2614               2 :             VSIFPrintfL(fp, "EMC\n");
    2615                 :     }
    2616                 : 
    2617              92 :     if (fpGZip)
    2618              88 :         VSIFCloseL(fpGZip);
    2619              92 :     fp = fpBack;
    2620                 : 
    2621              92 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2622              92 :     if (fpGZip)
    2623              88 :         VSIFPrintfL(fp, "\n");
    2624              92 :     VSIFPrintfL(fp, "endstream\n");
    2625              92 :     EndObj();
    2626                 : 
    2627              92 :     StartObj(nContentLengthId);
    2628                 :     VSIFPrintfL(fp,
    2629                 :                 "   %ld\n",
    2630              92 :                 (long)(nStreamEnd - nStreamStart));
    2631              92 :     EndObj();
    2632                 : 
    2633                 :     /* -------------------------------------------------------------- */
    2634                 :     /*  Write objects for feature tree.                               */
    2635                 :     /* -------------------------------------------------------------- */
    2636              92 :     if (nStructTreeRootId)
    2637                 :     {
    2638               4 :         int nParentTreeId = AllocNewObject();
    2639               4 :         StartObj(nParentTreeId);
    2640               4 :         VSIFPrintfL(fp, "<< /Nums [ 0 ");
    2641               4 :         VSIFPrintfL(fp, "[ ");
    2642               8 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    2643                 :         {
    2644               4 :             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    2645              18 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    2646                 :             {
    2647              14 :                 int nId = oLayerDesc.aUserPropertiesIds[iVector];
    2648              14 :                 if (nId)
    2649              14 :                     VSIFPrintfL(fp, "%d 0 R ", nId);
    2650                 :             }
    2651                 :         }
    2652               4 :         VSIFPrintfL(fp, " ]\n");
    2653               4 :         VSIFPrintfL(fp, " ] >> \n");
    2654               4 :         EndObj();
    2655                 : 
    2656               4 :         StartObj(nStructTreeRootId);
    2657                 :         VSIFPrintfL(fp,
    2658                 :                     "<< "
    2659                 :                     "/Type /StructTreeRoot "
    2660                 :                     "/ParentTree %d 0 R "
    2661               4 :                     "/K [ ", nParentTreeId);
    2662               8 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    2663                 :         {
    2664               4 :             VSIFPrintfL(fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId);
    2665                 :         }
    2666               4 :         VSIFPrintfL(fp,"] >>\n");
    2667               4 :         EndObj();
    2668                 :     }
    2669                 : 
    2670                 :     /* -------------------------------------------------------------- */
    2671                 :     /*  Write page resource dictionary.                               */
    2672                 :     /* -------------------------------------------------------------- */
    2673              92 :     StartObj(oPageContext.nResourcesId);
    2674                 :     {
    2675              92 :         GDALPDFDictionaryRW oDict;
    2676              92 :         GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW();
    2677              92 :         oDict.Add("XObject", poDictXObject);
    2678                 :         size_t iImage;
    2679             218 :         for(iImage = 0; iImage < oPageContext.asImageDesc.size(); iImage ++)
    2680                 :         {
    2681                 :             poDictXObject->Add(CPLSPrintf("Image%d", oPageContext.asImageDesc[iImage].nImageId),
    2682             126 :                                oPageContext.asImageDesc[iImage].nImageId, 0);
    2683                 :         }
    2684              94 :         for(iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
    2685                 :         {
    2686                 :             poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId),
    2687               2 :                                asExtraImageDesc[iImage].nImageId, 0);
    2688                 :         }
    2689              96 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    2690                 :         {
    2691               4 :             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    2692              18 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    2693                 :             {
    2694                 :                 poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector]),
    2695              14 :                                 oLayerDesc.aIds[iVector], 0);
    2696              14 :                 if (oLayerDesc.aIdsText[iVector])
    2697                 :                     poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector]),
    2698               2 :                                 oLayerDesc.aIdsText[iVector], 0);
    2699                 :             }
    2700                 :         }
    2701                 : 
    2702              92 :         GDALPDFDictionaryRW* poDictFTimesRoman = NULL;
    2703              92 :         if (bHasTimesRoman)
    2704                 :         {
    2705               2 :             poDictFTimesRoman = new GDALPDFDictionaryRW();
    2706               4 :             poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    2707               2 :             poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman"));
    2708               2 :             poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    2709               2 :             poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    2710                 :         }
    2711                 : 
    2712              92 :         GDALPDFDictionaryRW* poDictFTimesBold = NULL;
    2713              92 :         if (bHasTimesBold)
    2714                 :         {
    2715               0 :             poDictFTimesBold = new GDALPDFDictionaryRW();
    2716               0 :             poDictFTimesBold->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    2717               0 :             poDictFTimesBold->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Bold"));
    2718               0 :             poDictFTimesBold->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    2719               0 :             poDictFTimesBold->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    2720                 :         }
    2721                 : 
    2722              92 :         if (poDictFTimesRoman != NULL || poDictFTimesBold != NULL)
    2723                 :         {
    2724               2 :             GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
    2725               2 :             if (poDictFTimesRoman)
    2726               2 :                 poDictFont->Add("FTimesRoman", poDictFTimesRoman);
    2727               2 :             if (poDictFTimesBold)
    2728               0 :                 poDictFont->Add("FTimesBold", poDictFTimesBold);
    2729               2 :             oDict.Add("Font", poDictFont);
    2730                 :         }
    2731                 : 
    2732              92 :         if (asOCGs.size())
    2733                 :         {
    2734               6 :             GDALPDFDictionaryRW* poDictProperties = new GDALPDFDictionaryRW();
    2735              16 :             for(size_t i=0; i<asOCGs.size(); i++)
    2736                 :                 poDictProperties->Add(CPLSPrintf("Lyr%d", asOCGs[i].nId),
    2737              10 :                                       asOCGs[i].nId, 0);
    2738               6 :             oDict.Add("Properties", poDictProperties);
    2739                 :         }
    2740                 : 
    2741              92 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2742                 :     }
    2743              92 :     EndObj();
    2744                 : 
    2745              92 :     return TRUE;
    2746                 : }
    2747                 : 
    2748                 : /************************************************************************/
    2749                 : /*                             WriteMask()                              */
    2750                 : /************************************************************************/
    2751                 : 
    2752               6 : int GDALPDFWriter::WriteMask(GDALDataset* poSrcDS,
    2753                 :                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
    2754                 :                              PDFCompressMethod eCompressMethod)
    2755                 : {
    2756               6 :     int nMaskSize = nReqXSize * nReqYSize;
    2757               6 :     GByte* pabyMask = (GByte*)VSIMalloc(nMaskSize);
    2758               6 :     if (pabyMask == NULL)
    2759               0 :         return 0;
    2760                 : 
    2761                 :     CPLErr eErr;
    2762                 :     eErr = poSrcDS->GetRasterBand(4)->RasterIO(
    2763                 :             GF_Read,
    2764                 :             nXOff, nYOff,
    2765                 :             nReqXSize, nReqYSize,
    2766                 :             pabyMask, nReqXSize, nReqYSize, GDT_Byte,
    2767               6 :             0, 0);
    2768               6 :     if (eErr != CE_None)
    2769                 :     {
    2770               0 :         VSIFree(pabyMask);
    2771               0 :         return 0;
    2772                 :     }
    2773                 : 
    2774               6 :     int bOnly0or255 = TRUE;
    2775               6 :     int bOnly255 = TRUE;
    2776               6 :     int bOnly0 = TRUE;
    2777                 :     int i;
    2778            6366 :     for(i=0;i<nReqXSize * nReqYSize;i++)
    2779                 :     {
    2780            6364 :         if (pabyMask[i] == 0)
    2781            6360 :             bOnly255 = FALSE;
    2782               4 :         else if (pabyMask[i] == 255)
    2783               0 :             bOnly0 = FALSE;
    2784                 :         else
    2785                 :         {
    2786               4 :             bOnly0or255 = FALSE;
    2787               4 :             break;
    2788                 :         }
    2789                 :     }
    2790                 : 
    2791               6 :     if (bOnly255)
    2792                 :     {
    2793               0 :         CPLFree(pabyMask);
    2794               0 :         return 0;
    2795                 :     }
    2796                 : 
    2797               6 :     if (bOnly0or255)
    2798                 :     {
    2799                 :         /* Translate to 1 bit */
    2800               2 :         int nReqXSize1 = (nReqXSize + 7) / 8;
    2801               2 :         GByte* pabyMask1 = (GByte*)VSICalloc(nReqXSize1, nReqYSize);
    2802               2 :         if (pabyMask1 == NULL)
    2803                 :         {
    2804               0 :             CPLFree(pabyMask);
    2805               0 :             return 0;
    2806                 :         }
    2807              22 :         for(int y=0;y<nReqYSize;y++)
    2808                 :         {
    2809             220 :             for(int x=0;x<nReqXSize;x++)
    2810                 :             {
    2811             200 :                 if (pabyMask[y * nReqXSize + x])
    2812               0 :                     pabyMask1[y * nReqXSize1 + x / 8] |= 1 << (7 - (x % 8));
    2813                 :             }
    2814                 :         }
    2815               2 :         VSIFree(pabyMask);
    2816               2 :         pabyMask = pabyMask1;
    2817               2 :         nMaskSize = nReqXSize1 * nReqYSize;
    2818                 :     }
    2819                 : 
    2820               6 :     int nMaskId = AllocNewObject();
    2821               6 :     int nMaskLengthId = AllocNewObject();
    2822                 : 
    2823               6 :     StartObj(nMaskId);
    2824               6 :     GDALPDFDictionaryRW oDict;
    2825                 :     oDict.Add("Length", nMaskLengthId, 0)
    2826               6 :          .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
    2827               6 :     if( eCompressMethod != COMPRESS_NONE )
    2828                 :     {
    2829               6 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2830                 :     }
    2831                 :     oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    2832                 :          .Add("Width", nReqXSize)
    2833                 :          .Add("Height", nReqYSize)
    2834                 :          .Add("ColorSpace", GDALPDFObjectRW::CreateName("DeviceGray"))
    2835               6 :          .Add("BitsPerComponent", (bOnly0or255) ? 1 : 8);
    2836               6 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2837               6 :     VSIFPrintfL(fp, "stream\n");
    2838               6 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    2839                 : 
    2840               6 :     VSILFILE* fpGZip = NULL;
    2841               6 :     VSILFILE* fpBack = fp;
    2842               6 :     if( eCompressMethod != COMPRESS_NONE )
    2843                 :     {
    2844               6 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2845               6 :         fp = fpGZip;
    2846                 :     }
    2847                 : 
    2848               6 :     VSIFWriteL(pabyMask, nMaskSize, 1, fp);
    2849               6 :     CPLFree(pabyMask);
    2850                 : 
    2851               6 :     if (fpGZip)
    2852               6 :         VSIFCloseL(fpGZip);
    2853               6 :     fp = fpBack;
    2854                 : 
    2855               6 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2856                 :     VSIFPrintfL(fp,
    2857                 :                 "\n"
    2858               6 :                 "endstream\n");
    2859               6 :     EndObj();
    2860                 : 
    2861               6 :     StartObj(nMaskLengthId);
    2862                 :     VSIFPrintfL(fp,
    2863                 :                 "   %ld\n",
    2864               6 :                 (long)(nStreamEnd - nStreamStart));
    2865               6 :     EndObj();
    2866                 : 
    2867               6 :     return nMaskId;
    2868                 : }
    2869                 : 
    2870                 : /************************************************************************/
    2871                 : /*                             WriteBlock()                             */
    2872                 : /************************************************************************/
    2873                 : 
    2874             128 : int GDALPDFWriter::WriteBlock(GDALDataset* poSrcDS,
    2875                 :                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
    2876                 :                              int nColorTableId,
    2877                 :                              PDFCompressMethod eCompressMethod,
    2878                 :                              int nPredictor,
    2879                 :                              int nJPEGQuality,
    2880                 :                              const char* pszJPEG2000_DRIVER,
    2881                 :                              GDALProgressFunc pfnProgress,
    2882                 :                              void * pProgressData)
    2883                 : {
    2884             128 :     int  nBands = poSrcDS->GetRasterCount();
    2885                 : 
    2886             128 :     CPLErr eErr = CE_None;
    2887             128 :     GDALDataset* poBlockSrcDS = NULL;
    2888             128 :     GDALDatasetH hMemDS = NULL;
    2889             128 :     GByte* pabyMEMDSBuffer = NULL;
    2890                 : 
    2891             128 :     if (eCompressMethod == COMPRESS_DEFAULT)
    2892                 :     {
    2893             106 :         GDALDataset* poSrcDSToTest = poSrcDS;
    2894                 : 
    2895                 :         /* Test if we can directly copy original JPEG content */
    2896                 :         /* if available */
    2897             212 :         if (poSrcDS->GetDriver() != NULL &&
    2898             106 :             poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
    2899                 :         {
    2900               8 :             VRTDataset* poVRTDS = (VRTDataset* )poSrcDS;
    2901               8 :             poSrcDSToTest = poVRTDS->GetSingleSimpleSource();
    2902                 :         }
    2903                 : 
    2904             318 :         if (poSrcDSToTest != NULL &&
    2905             106 :             poSrcDSToTest->GetDriver() != NULL &&
    2906             106 :             poSrcDSToTest->GetDriver() == GDALGetDriverByName("JPEG") &&
    2907                 :             nXOff == 0 && nYOff == 0 &&
    2908                 :             nReqXSize == poSrcDSToTest->GetRasterXSize() &&
    2909                 :             nReqYSize == poSrcDSToTest->GetRasterYSize() &&
    2910                 :             nJPEGQuality < 0)
    2911                 :         {
    2912               4 :             VSILFILE* fpSrc = VSIFOpenL(poSrcDSToTest->GetDescription(), "rb");
    2913               4 :             if (fpSrc != NULL)
    2914                 :             {
    2915               4 :                 CPLDebug("PDF", "Copying directly original JPEG file");
    2916                 : 
    2917               4 :                 VSIFSeekL(fpSrc, 0, SEEK_END);
    2918               4 :                 int nLength = (int)VSIFTellL(fpSrc);
    2919               4 :                 VSIFSeekL(fpSrc, 0, SEEK_SET);
    2920                 : 
    2921               4 :                 int nImageId = AllocNewObject();
    2922                 : 
    2923               4 :                 StartObj(nImageId);
    2924                 : 
    2925               4 :                 GDALPDFDictionaryRW oDict;
    2926                 :                 oDict.Add("Length", nLength)
    2927                 :                      .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    2928                 :                      .Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode"))
    2929                 :                      .Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    2930                 :                      .Add("Width", nReqXSize)
    2931                 :                      .Add("Height", nReqYSize)
    2932                 :                      .Add("ColorSpace",
    2933                 :                         (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
    2934                 :                                                 GDALPDFObjectRW::CreateName("DeviceRGB"))
    2935               4 :                      .Add("BitsPerComponent", 8);
    2936               4 :                 VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2937               4 :                 VSIFPrintfL(fp, "stream\n");
    2938                 : 
    2939                 :                 GByte abyBuffer[1024];
    2940              24 :                 for(int i=0;i<nLength;i += 1024)
    2941                 :                 {
    2942              20 :                     int nRead = (int) VSIFReadL(abyBuffer, 1, 1024, fpSrc);
    2943              20 :                     if ((int)VSIFWriteL(abyBuffer, 1, nRead, fp) != nRead)
    2944                 :                     {
    2945               0 :                         eErr = CE_Failure;
    2946               0 :                         break;
    2947                 :                     }
    2948                 : 
    2949              20 :                     if( eErr == CE_None && pfnProgress != NULL
    2950                 :                         && !pfnProgress( (i + nRead) / (double)nLength,
    2951                 :                                         NULL, pProgressData ) )
    2952                 :                     {
    2953                 :                         CPLError( CE_Failure, CPLE_UserInterrupt,
    2954               0 :                                 "User terminated CreateCopy()" );
    2955               0 :                         eErr = CE_Failure;
    2956               0 :                         break;
    2957                 :                     }
    2958                 :                 }
    2959                 : 
    2960               4 :                 VSIFPrintfL(fp, "\nendstream\n");
    2961                 : 
    2962               4 :                 EndObj();
    2963                 : 
    2964               4 :                 VSIFCloseL(fpSrc);
    2965                 : 
    2966               4 :                 return eErr == CE_None ? nImageId : 0;
    2967                 :             }
    2968                 :         }
    2969                 : 
    2970             102 :         eCompressMethod = COMPRESS_DEFLATE;
    2971                 :     }
    2972                 : 
    2973             124 :     int nMaskId = 0;
    2974             124 :     if (nBands == 4)
    2975                 :     {
    2976                 :         nMaskId = WriteMask(poSrcDS,
    2977                 :                             nXOff, nYOff, nReqXSize, nReqYSize,
    2978               6 :                             eCompressMethod);
    2979                 :     }
    2980                 : 
    2981             124 :     if( nReqXSize == poSrcDS->GetRasterXSize() &&
    2982                 :         nReqYSize == poSrcDS->GetRasterYSize() &&
    2983                 :         nBands != 4)
    2984                 :     {
    2985              78 :         poBlockSrcDS = poSrcDS;
    2986                 :     }
    2987                 :     else
    2988                 :     {
    2989              46 :         if (nBands == 4)
    2990               6 :             nBands = 3;
    2991                 : 
    2992              46 :         GDALDriverH hMemDriver = GDALGetDriverByName("MEM");
    2993              46 :         if( hMemDriver == NULL )
    2994               0 :             return 0;
    2995                 : 
    2996                 :         hMemDS = GDALCreate(hMemDriver, "MEM:::",
    2997                 :                             nReqXSize, nReqYSize, 0,
    2998              46 :                             GDT_Byte, NULL);
    2999              46 :         if (hMemDS == NULL)
    3000               0 :             return 0;
    3001                 : 
    3002                 :         pabyMEMDSBuffer =
    3003              46 :             (GByte*)VSIMalloc3(nReqXSize, nReqYSize, nBands);
    3004              46 :         if (pabyMEMDSBuffer == NULL)
    3005                 :         {
    3006               0 :             GDALClose(hMemDS);
    3007               0 :             return 0;
    3008                 :         }
    3009                 : 
    3010                 :         eErr = poSrcDS->RasterIO(GF_Read,
    3011                 :                                 nXOff, nYOff,
    3012                 :                                 nReqXSize, nReqYSize,
    3013                 :                                 pabyMEMDSBuffer, nReqXSize, nReqYSize,
    3014                 :                                 GDT_Byte, nBands, NULL,
    3015              46 :                                 0, 0, 0);
    3016                 : 
    3017              46 :         if( eErr != CE_None )
    3018                 :         {
    3019               0 :             CPLFree(pabyMEMDSBuffer);
    3020               0 :             GDALClose(hMemDS);
    3021               0 :             return 0;
    3022                 :         }
    3023                 : 
    3024                 :         int iBand;
    3025             104 :         for(iBand = 0; iBand < nBands; iBand ++)
    3026                 :         {
    3027              58 :             char** papszMEMDSOptions = NULL;
    3028                 :             char szTmp[64];
    3029              58 :             memset(szTmp, 0, sizeof(szTmp));
    3030                 :             CPLPrintPointer(szTmp,
    3031              58 :                             pabyMEMDSBuffer + iBand * nReqXSize * nReqYSize, sizeof(szTmp));
    3032              58 :             papszMEMDSOptions = CSLSetNameValue(papszMEMDSOptions, "DATAPOINTER", szTmp);
    3033              58 :             GDALAddBand(hMemDS, GDT_Byte, papszMEMDSOptions);
    3034              58 :             CSLDestroy(papszMEMDSOptions);
    3035                 :         }
    3036                 : 
    3037              46 :         poBlockSrcDS = (GDALDataset*) hMemDS;
    3038                 :     }
    3039                 : 
    3040             124 :     int nImageId = AllocNewObject();
    3041             124 :     int nImageLengthId = AllocNewObject();
    3042                 : 
    3043             124 :     StartObj(nImageId);
    3044                 : 
    3045             124 :     GDALPDFDictionaryRW oDict;
    3046                 :     oDict.Add("Length", nImageLengthId, 0)
    3047             124 :          .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
    3048                 : 
    3049             124 :     if( eCompressMethod == COMPRESS_DEFLATE )
    3050                 :     {
    3051             106 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    3052             106 :         if( nPredictor == 2 )
    3053                 :             oDict.Add("DecodeParms", &((new GDALPDFDictionaryRW())
    3054                 :                                   ->Add("Predictor", 2)
    3055                 :                                    .Add("Colors", nBands)
    3056               4 :                                    .Add("Columns", nReqXSize)));
    3057                 :     }
    3058              18 :     else if( eCompressMethod == COMPRESS_JPEG )
    3059                 :     {
    3060               6 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode"));
    3061                 :     }
    3062              12 :     else if( eCompressMethod == COMPRESS_JPEG2000 )
    3063                 :     {
    3064              10 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("JPXDecode"));
    3065                 :     }
    3066                 : 
    3067                 :     oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    3068                 :          .Add("Width", nReqXSize)
    3069                 :          .Add("Height", nReqYSize)
    3070                 :          .Add("ColorSpace",
    3071                 :               (nColorTableId != 0) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) :
    3072                 :               (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
    3073                 :                                      GDALPDFObjectRW::CreateName("DeviceRGB"))
    3074             124 :          .Add("BitsPerComponent", 8);
    3075             124 :     if( nMaskId )
    3076                 :     {
    3077               6 :         oDict.Add("SMask", nMaskId, 0);
    3078                 :     }
    3079             124 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3080             124 :     VSIFPrintfL(fp, "stream\n");
    3081                 : 
    3082             124 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    3083                 : 
    3084             140 :     if( eCompressMethod == COMPRESS_JPEG ||
    3085                 :         eCompressMethod == COMPRESS_JPEG2000 )
    3086                 :     {
    3087              16 :         GDALDriver* poJPEGDriver = NULL;
    3088                 :         char szTmp[64];
    3089              16 :         char** papszOptions = NULL;
    3090                 : 
    3091              16 :         if( eCompressMethod == COMPRESS_JPEG )
    3092                 :         {
    3093               6 :             poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG");
    3094               6 :             if (poJPEGDriver != NULL && nJPEGQuality > 0)
    3095               0 :                 papszOptions = CSLAddString(papszOptions, CPLSPrintf("QUALITY=%d", nJPEGQuality));
    3096               6 :             sprintf(szTmp, "/vsimem/pdftemp/%p.jpg", this);
    3097                 :         }
    3098                 :         else
    3099                 :         {
    3100              10 :             if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2KAK"))
    3101               2 :                 poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2KAK");
    3102              10 :             if (poJPEGDriver == NULL)
    3103                 :             {
    3104              10 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2ECW"))
    3105               6 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2ECW");
    3106              10 :                 if (poJPEGDriver)
    3107                 :                 {
    3108               6 :                     papszOptions = CSLAddString(papszOptions, "PROFILE=NPJE");
    3109               6 :                     papszOptions = CSLAddString(papszOptions, "LAYERS=1");
    3110               6 :                     papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF");
    3111               6 :                     papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF");
    3112                 :                 }
    3113                 :             }
    3114              10 :             if (poJPEGDriver == NULL)
    3115                 :             {
    3116               4 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2OpenJPEG"))
    3117               2 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2OpenJPEG");
    3118                 :             }
    3119              10 :             if (poJPEGDriver == NULL)
    3120                 :             {
    3121               2 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JPEG2000"))
    3122               2 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG2000");
    3123                 :             }
    3124              10 :             sprintf(szTmp, "/vsimem/pdftemp/%p.jp2", this);
    3125                 :         }
    3126                 : 
    3127              16 :         if( poJPEGDriver == NULL )
    3128                 :         {
    3129                 :             CPLError(CE_Failure, CPLE_NotSupported,
    3130                 :                      "No %s driver found",
    3131               0 :                      ( eCompressMethod == COMPRESS_JPEG ) ? "JPEG" : "JPEG2000");
    3132               0 :             eErr = CE_Failure;
    3133               0 :             goto end;
    3134                 :         }
    3135                 : 
    3136              16 :         GDALDataset* poJPEGDS = NULL;
    3137                 : 
    3138                 :         poJPEGDS = poJPEGDriver->CreateCopy(szTmp, poBlockSrcDS,
    3139                 :                                             FALSE, papszOptions,
    3140              16 :                                             pfnProgress, pProgressData);
    3141                 : 
    3142              16 :         CSLDestroy(papszOptions);
    3143              16 :         if( poJPEGDS == NULL )
    3144                 :         {
    3145               0 :             eErr = CE_Failure;
    3146               0 :             goto end;
    3147                 :         }
    3148                 : 
    3149              16 :         GDALClose(poJPEGDS);
    3150                 : 
    3151              16 :         vsi_l_offset nJPEGDataSize = 0;
    3152              16 :         GByte* pabyJPEGData = VSIGetMemFileBuffer(szTmp, &nJPEGDataSize, TRUE);
    3153              16 :         VSIFWriteL(pabyJPEGData, nJPEGDataSize, 1, fp);
    3154              16 :         CPLFree(pabyJPEGData);
    3155                 :     }
    3156                 :     else
    3157                 :     {
    3158             108 :         VSILFILE* fpGZip = NULL;
    3159             108 :         VSILFILE* fpBack = fp;
    3160             108 :         if( eCompressMethod == COMPRESS_DEFLATE )
    3161                 :         {
    3162             106 :             fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    3163             106 :             fp = fpGZip;
    3164                 :         }
    3165                 : 
    3166             108 :         GByte* pabyLine = (GByte*)CPLMalloc(nReqXSize * nBands);
    3167           12838 :         for(int iLine = 0; iLine < nReqYSize; iLine ++)
    3168                 :         {
    3169                 :             /* Get pixel interleaved data */
    3170                 :             eErr = poBlockSrcDS->RasterIO(GF_Read,
    3171                 :                                           0, iLine, nReqXSize, 1,
    3172                 :                                           pabyLine, nReqXSize, 1, GDT_Byte,
    3173           12730 :                                           nBands, NULL, nBands, 0, 1);
    3174           12730 :             if( eErr != CE_None )
    3175               0 :                 break;
    3176                 : 
    3177                 :             /* Apply predictor if needed */
    3178           12730 :             if( nPredictor == 2 )
    3179                 :             {
    3180            1124 :                 if( nBands == 1 )
    3181                 :                 {
    3182            1024 :                     int nPrevValue = pabyLine[0];
    3183          524288 :                     for(int iPixel = 1; iPixel < nReqXSize; iPixel ++)
    3184                 :                     {
    3185          523264 :                         int nCurValue = pabyLine[iPixel];
    3186          523264 :                         pabyLine[iPixel] = (GByte) (nCurValue - nPrevValue);
    3187          523264 :                         nPrevValue = nCurValue;
    3188                 :                     }
    3189                 :                 }
    3190             100 :                 else if( nBands == 3 )
    3191                 :                 {
    3192             100 :                     int nPrevValueR = pabyLine[0];
    3193             100 :                     int nPrevValueG = pabyLine[1];
    3194             100 :                     int nPrevValueB = pabyLine[2];
    3195            5000 :                     for(int iPixel = 1; iPixel < nReqXSize; iPixel ++)
    3196                 :                     {
    3197            4900 :                         int nCurValueR = pabyLine[3 * iPixel + 0];
    3198            4900 :                         int nCurValueG = pabyLine[3 * iPixel + 1];
    3199            4900 :                         int nCurValueB = pabyLine[3 * iPixel + 2];
    3200            4900 :                         pabyLine[3 * iPixel + 0] = (GByte) (nCurValueR - nPrevValueR);
    3201            4900 :                         pabyLine[3 * iPixel + 1] = (GByte) (nCurValueG - nPrevValueG);
    3202            4900 :                         pabyLine[3 * iPixel + 2] = (GByte) (nCurValueB - nPrevValueB);
    3203            4900 :                         nPrevValueR = nCurValueR;
    3204            4900 :                         nPrevValueG = nCurValueG;
    3205            4900 :                         nPrevValueB = nCurValueB;
    3206                 :                     }
    3207                 :                 }
    3208                 :             }
    3209                 : 
    3210           12730 :             if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, fp) != 1 )
    3211                 :             {
    3212               0 :                 eErr = CE_Failure;
    3213               0 :                 break;
    3214                 :             }
    3215                 : 
    3216           12730 :             if( eErr == CE_None && pfnProgress != NULL
    3217                 :                 && !pfnProgress( (iLine+1) / (double)nReqYSize,
    3218                 :                                 NULL, pProgressData ) )
    3219                 :             {
    3220                 :                 CPLError( CE_Failure, CPLE_UserInterrupt,
    3221               0 :                         "User terminated CreateCopy()" );
    3222               0 :                 eErr = CE_Failure;
    3223               0 :                 break;
    3224                 :             }
    3225                 :         }
    3226                 : 
    3227             108 :         CPLFree(pabyLine);
    3228                 : 
    3229             108 :         if (fpGZip)
    3230             106 :             VSIFCloseL(fpGZip);
    3231             108 :         fp = fpBack;
    3232                 :     }
    3233                 : 
    3234                 : end:
    3235             124 :     CPLFree(pabyMEMDSBuffer);
    3236             124 :     pabyMEMDSBuffer = NULL;
    3237             124 :     if( hMemDS != NULL )
    3238                 :     {
    3239              46 :         GDALClose(hMemDS);
    3240              46 :         hMemDS = NULL;
    3241                 :     }
    3242                 : 
    3243             124 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    3244                 :     VSIFPrintfL(fp,
    3245                 :                 "\n"
    3246             124 :                 "endstream\n");
    3247             124 :     EndObj();
    3248                 : 
    3249             124 :     StartObj(nImageLengthId);
    3250                 :     VSIFPrintfL(fp,
    3251                 :                 "   %ld\n",
    3252             124 :                 (long)(nStreamEnd - nStreamStart));
    3253             124 :     EndObj();
    3254                 : 
    3255             124 :     return eErr == CE_None ? nImageId : 0;
    3256                 : }
    3257                 : 
    3258                 : /************************************************************************/
    3259                 : /*                              WritePages()                            */
    3260                 : /************************************************************************/
    3261                 : 
    3262              92 : void GDALPDFWriter::WritePages()
    3263                 : {
    3264              92 :     StartObj(nPageResourceId);
    3265                 :     {
    3266              92 :         GDALPDFDictionaryRW oDict;
    3267              92 :         GDALPDFArrayRW* poKids = new GDALPDFArrayRW();
    3268                 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages"))
    3269                 :              .Add("Count", (int)asPageId.size())
    3270             184 :              .Add("Kids", poKids);
    3271                 : 
    3272             184 :         for(size_t i=0;i<asPageId.size();i++)
    3273              92 :             poKids->Add(asPageId[i], 0);
    3274                 : 
    3275              92 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3276                 :     }
    3277              92 :     EndObj();
    3278                 : 
    3279              92 :     StartObj(nCatalogId);
    3280                 :     {
    3281              92 :         GDALPDFDictionaryRW oDict;
    3282                 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog"))
    3283             184 :              .Add("Pages", nPageResourceId, 0);
    3284              92 :         if (nXMPId)
    3285               2 :             oDict.Add("Metadata", nXMPId, 0);
    3286              92 :         if (asOCGs.size())
    3287                 :         {
    3288               6 :             GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW();
    3289               6 :             oDict.Add("OCProperties", poDictOCProperties);
    3290                 : 
    3291               6 :             GDALPDFDictionaryRW* poDictD = new GDALPDFDictionaryRW();
    3292               6 :             poDictOCProperties->Add("D", poDictD);
    3293                 : 
    3294               6 :             GDALPDFArrayRW* poArrayOrder = new GDALPDFArrayRW();
    3295                 :             size_t i;
    3296              14 :             for(i=0;i<asOCGs.size();i++)
    3297                 :             {
    3298               8 :                 poArrayOrder->Add(asOCGs[i].nId, 0);
    3299               8 :                 if (i + 1 < asOCGs.size() && asOCGs[i+1].nParentId == asOCGs[i].nId)
    3300                 :                 {
    3301               2 :                     GDALPDFArrayRW* poSubArrayOrder = new GDALPDFArrayRW();
    3302               4 :                     poSubArrayOrder->Add(asOCGs[i+1].nId, 0);
    3303               2 :                     poArrayOrder->Add(poSubArrayOrder);
    3304               2 :                     i ++;
    3305                 :                 }
    3306                 :             }
    3307               6 :             poDictD->Add("Order", poArrayOrder);
    3308                 : 
    3309                 : 
    3310               6 :             GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW();
    3311              16 :             for(i=0;i<asOCGs.size();i++)
    3312              10 :                 poArrayOGCs->Add(asOCGs[i].nId, 0);
    3313               6 :             poDictOCProperties->Add("OCGs", poArrayOGCs);
    3314                 :         }
    3315                 : 
    3316              92 :         if (nStructTreeRootId)
    3317                 :         {
    3318               4 :             GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW();
    3319               4 :             oDict.Add("MarkInfo", poDictMarkInfo);
    3320               4 :             poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE));
    3321                 : 
    3322               4 :             oDict.Add("StructTreeRoot", nStructTreeRootId, 0);
    3323                 :         }
    3324                 : 
    3325              92 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3326                 :     }
    3327              92 :     EndObj();
    3328              92 : }
    3329                 : 
    3330                 : /************************************************************************/
    3331                 : /*                        GDALPDFGetJPEGQuality()                       */
    3332                 : /************************************************************************/
    3333                 : 
    3334              94 : static int GDALPDFGetJPEGQuality(char** papszOptions)
    3335                 : {
    3336              94 :     int nJpegQuality = -1;
    3337              94 :     const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" );
    3338              94 :     if( pszValue  != NULL )
    3339                 :     {
    3340               0 :         nJpegQuality = atoi( pszValue );
    3341               0 :         if (!(nJpegQuality >= 1 && nJpegQuality <= 100))
    3342                 :         {
    3343                 :             CPLError( CE_Warning, CPLE_IllegalArg,
    3344                 :                     "JPEG_QUALITY=%s value not recognised, ignoring.",
    3345               0 :                     pszValue );
    3346               0 :             nJpegQuality = -1;
    3347                 :         }
    3348                 :     }
    3349              94 :     return nJpegQuality;
    3350                 : }
    3351                 : 
    3352                 : /************************************************************************/
    3353                 : /*                          GDALPDFCreateCopy()                         */
    3354                 : /************************************************************************/
    3355                 : 
    3356             120 : GDALDataset *GDALPDFCreateCopy( const char * pszFilename,
    3357                 :                                 GDALDataset *poSrcDS,
    3358                 :                                 int bStrict,
    3359                 :                                 char **papszOptions,
    3360                 :                                 GDALProgressFunc pfnProgress,
    3361                 :                                 void * pProgressData )
    3362                 : {
    3363             120 :     int  nBands = poSrcDS->GetRasterCount();
    3364             120 :     int  nWidth = poSrcDS->GetRasterXSize();
    3365             120 :     int  nHeight = poSrcDS->GetRasterYSize();
    3366                 : 
    3367             120 :     if( !pfnProgress( 0.0, NULL, pProgressData ) )
    3368               0 :         return NULL;
    3369                 : 
    3370                 : /* -------------------------------------------------------------------- */
    3371                 : /*      Some some rudimentary checks                                    */
    3372                 : /* -------------------------------------------------------------------- */
    3373             120 :     if( nBands != 1 && nBands != 3 && nBands != 4 )
    3374                 :     {
    3375                 :         CPLError( CE_Failure, CPLE_NotSupported,
    3376                 :                   "PDF driver doesn't support %d bands.  Must be 1 (grey or with color table), "
    3377               6 :                   "3 (RGB) or 4 bands.\n", nBands );
    3378                 : 
    3379               6 :         return NULL;
    3380                 :     }
    3381                 : 
    3382             114 :     GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    3383             114 :     if( eDT != GDT_Byte )
    3384                 :     {
    3385                 :         CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    3386                 :                   "PDF driver doesn't support data type %s. "
    3387                 :                   "Only eight bit byte bands supported.\n",
    3388                 :                   GDALGetDataTypeName(
    3389              20 :                       poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
    3390                 : 
    3391              20 :         if (bStrict)
    3392              20 :             return NULL;
    3393                 :     }
    3394                 : 
    3395                 : /* -------------------------------------------------------------------- */
    3396                 : /*     Read options.                                                    */
    3397                 : /* -------------------------------------------------------------------- */
    3398              94 :     PDFCompressMethod eCompressMethod = COMPRESS_DEFAULT;
    3399              94 :     const char* pszCompressMethod = CSLFetchNameValue(papszOptions, "COMPRESS");
    3400              94 :     if (pszCompressMethod)
    3401                 :     {
    3402              18 :         if( EQUAL(pszCompressMethod, "NONE") )
    3403               2 :             eCompressMethod = COMPRESS_NONE;
    3404              16 :         else if( EQUAL(pszCompressMethod, "DEFLATE") )
    3405               0 :             eCompressMethod = COMPRESS_DEFLATE;
    3406              16 :         else if( EQUAL(pszCompressMethod, "JPEG") )
    3407               6 :             eCompressMethod = COMPRESS_JPEG;
    3408              10 :         else if( EQUAL(pszCompressMethod, "JPEG2000") )
    3409              10 :             eCompressMethod = COMPRESS_JPEG2000;
    3410                 :         else
    3411                 :         {
    3412                 :             CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    3413               0 :                     "Unsupported value for COMPRESS.");
    3414                 : 
    3415               0 :             if (bStrict)
    3416               0 :                 return NULL;
    3417                 :         }
    3418                 :     }
    3419                 : 
    3420              94 :     PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE;
    3421              94 :     const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS");
    3422              94 :     if (pszStreamCompressMethod)
    3423                 :     {
    3424               4 :         if( EQUAL(pszStreamCompressMethod, "NONE") )
    3425               4 :             eStreamCompressMethod = COMPRESS_NONE;
    3426               0 :         else if( EQUAL(pszStreamCompressMethod, "DEFLATE") )
    3427               0 :             eStreamCompressMethod = COMPRESS_DEFLATE;
    3428                 :         else
    3429                 :         {
    3430                 :             CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    3431               0 :                     "Unsupported value for STREAM_COMPRESS.");
    3432                 : 
    3433               0 :             if (bStrict)
    3434               0 :                 return NULL;
    3435                 :         }
    3436                 :     }
    3437                 : 
    3438             170 :     if (nBands == 1 &&
    3439              76 :         poSrcDS->GetRasterBand(1)->GetColorTable() != NULL &&
    3440                 :         (eCompressMethod == COMPRESS_JPEG || eCompressMethod == COMPRESS_JPEG2000))
    3441                 :     {
    3442                 :         CPLError( CE_Warning, CPLE_AppDefined,
    3443                 :                   "The source raster band has a color table, which is not appropriate with JPEG or JPEG2000 compression.\n"
    3444               0 :                   "You should rather consider using color table expansion (-expand option in gdal_translate)");
    3445                 :     }
    3446                 : 
    3447                 : 
    3448              94 :     int nBlockXSize = nWidth;
    3449              94 :     int nBlockYSize = nHeight;
    3450                 :     const char* pszValue;
    3451                 : 
    3452              94 :     int bTiled = CSLFetchBoolean( papszOptions, "TILED", FALSE );
    3453              94 :     if( bTiled )
    3454               2 :         nBlockXSize = nBlockYSize = 256;
    3455                 : 
    3456              94 :     pszValue = CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
    3457              94 :     if( pszValue != NULL )
    3458                 :     {
    3459               2 :         nBlockXSize = atoi( pszValue );
    3460               2 :         if (nBlockXSize < 0 || nBlockXSize >= nWidth)
    3461               0 :             nBlockXSize = nWidth;
    3462                 :     }
    3463                 : 
    3464              94 :     pszValue = CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
    3465              94 :     if( pszValue != NULL )
    3466                 :     {
    3467               2 :         nBlockYSize = atoi( pszValue );
    3468               2 :         if (nBlockYSize < 0 || nBlockYSize >= nHeight)
    3469               0 :             nBlockYSize = nHeight;
    3470                 :     }
    3471                 : 
    3472              94 :     int nJPEGQuality = GDALPDFGetJPEGQuality(papszOptions);
    3473                 : 
    3474              94 :     const char* pszJPEG2000_DRIVER = CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER");
    3475                 : 
    3476                 :     const char* pszGEO_ENCODING =
    3477              94 :         CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000");
    3478                 : 
    3479              94 :     const char* pszXMP = CSLFetchNameValue(papszOptions, "XMP");
    3480                 : 
    3481              94 :     double dfDPI = atof(CSLFetchNameValueDef(papszOptions, "DPI", "72"));
    3482              94 :     if (dfDPI < 72.0)
    3483               0 :         dfDPI = 72.0;
    3484                 : 
    3485              94 :     const char* pszPredictor = CSLFetchNameValue(papszOptions, "PREDICTOR");
    3486              94 :     int nPredictor = 1;
    3487              94 :     if (pszPredictor)
    3488                 :     {
    3489               4 :         if (eCompressMethod == COMPRESS_DEFAULT)
    3490               4 :             eCompressMethod = COMPRESS_DEFLATE;
    3491                 : 
    3492               4 :         if (eCompressMethod != COMPRESS_DEFLATE)
    3493                 :         {
    3494                 :             CPLError(CE_Warning, CPLE_NotSupported,
    3495               0 :                      "PREDICTOR option is only taken into account for DEFLATE compression");
    3496                 :         }
    3497                 :         else
    3498                 :         {
    3499               4 :             nPredictor = atoi(pszPredictor);
    3500               4 :             if (nPredictor != 1 && nPredictor != 2)
    3501                 :             {
    3502                 :                 CPLError(CE_Warning, CPLE_NotSupported,
    3503               0 :                                     "Supported PREDICTOR values are 1 or 2");
    3504               0 :                 nPredictor = 1;
    3505                 :             }
    3506                 :         }
    3507                 :     }
    3508                 : 
    3509              94 :     const char* pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE");
    3510                 : 
    3511              94 :     int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0"));
    3512                 : 
    3513                 :     PDFMargins sMargins;
    3514              94 :     sMargins.nLeft = nMargin;
    3515              94 :     sMargins.nRight = nMargin;
    3516              94 :     sMargins.nTop = nMargin;
    3517              94 :     sMargins.nBottom = nMargin;
    3518                 : 
    3519              94 :     const char* pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN");
    3520              94 :     if (pszLeftMargin) sMargins.nLeft = atoi(pszLeftMargin);
    3521                 : 
    3522              94 :     const char* pszRightMargin = CSLFetchNameValue(papszOptions, "RIGHT_MARGIN");
    3523              94 :     if (pszRightMargin) sMargins.nRight = atoi(pszRightMargin);
    3524                 : 
    3525              94 :     const char* pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN");
    3526              94 :     if (pszTopMargin) sMargins.nTop = atoi(pszTopMargin);
    3527                 : 
    3528              94 :     const char* pszBottomMargin = CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN");
    3529              94 :     if (pszBottomMargin) sMargins.nBottom = atoi(pszBottomMargin);
    3530                 : 
    3531              94 :     const char* pszLayerName = CSLFetchNameValue(papszOptions, "LAYER_NAME");
    3532                 : 
    3533              94 :     const char* pszExtraImages = CSLFetchNameValue(papszOptions, "EXTRA_IMAGES");
    3534              94 :     const char* pszExtraStream = CSLFetchNameValue(papszOptions, "EXTRA_STREAM");
    3535              94 :     const char* pszExtraLayerName = CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME");
    3536                 : 
    3537              94 :     const char* pszOGRDataSource = CSLFetchNameValue(papszOptions, "OGR_DATASOURCE");
    3538              94 :     const char* pszOGRDisplayField = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD");
    3539              94 :     const char* pszOGRDisplayLayerNames = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES");
    3540              94 :     int bWriteOGRAttributes = CSLFetchBoolean(papszOptions, "OGR_WRITE_ATTRIBUTES", TRUE);
    3541                 : 
    3542                 : /* -------------------------------------------------------------------- */
    3543                 : /*      Create file.                                                    */
    3544                 : /* -------------------------------------------------------------------- */
    3545              94 :     VSILFILE* fp = VSIFOpenL(pszFilename, "wb");
    3546              94 :     if( fp == NULL )
    3547                 :     {
    3548                 :         CPLError( CE_Failure, CPLE_OpenFailed,
    3549                 :                   "Unable to create PDF file %s.\n",
    3550               4 :                   pszFilename );
    3551               4 :         return NULL;
    3552                 :     }
    3553                 : 
    3554                 : 
    3555              90 :     GDALPDFWriter oWriter(fp);
    3556                 : 
    3557              90 :     if( CSLFetchBoolean(papszOptions, "WRITE_INFO", TRUE) )
    3558              88 :         oWriter.SetInfo(poSrcDS, papszOptions);
    3559              90 :     oWriter.SetXMP(poSrcDS, pszXMP);
    3560                 : 
    3561                 :     oWriter.StartPage(poSrcDS,
    3562                 :                       dfDPI,
    3563                 :                       pszGEO_ENCODING,
    3564                 :                       pszNEATLINE,
    3565                 :                       &sMargins,
    3566                 :                       eStreamCompressMethod,
    3567              90 :                       pszOGRDataSource != NULL && bWriteOGRAttributes);
    3568                 : 
    3569                 :     int bRet = oWriter.WriteImagery(pszLayerName,
    3570                 :                                     eCompressMethod,
    3571                 :                                     nPredictor,
    3572                 :                                     nJPEGQuality,
    3573                 :                                     pszJPEG2000_DRIVER,
    3574                 :                                     nBlockXSize, nBlockYSize,
    3575              90 :                                     pfnProgress, pProgressData);
    3576                 : 
    3577                 : #ifdef OGR_ENABLED
    3578              90 :     if (bRet && pszOGRDataSource != NULL)
    3579                 :         oWriter.WriteOGRDataSource(pszOGRDataSource,
    3580                 :                                    pszOGRDisplayField,
    3581                 :                                    pszOGRDisplayLayerNames,
    3582               2 :                                    bWriteOGRAttributes);
    3583                 : #endif
    3584                 : 
    3585              90 :     if (bRet)
    3586                 :         oWriter.EndPage(pszExtraImages,
    3587                 :                         pszExtraStream,
    3588              90 :                         pszExtraLayerName);
    3589              90 :     oWriter.Close();
    3590                 : 
    3591              90 :     if (!bRet)
    3592                 :     {
    3593               0 :         VSIUnlink(pszFilename);
    3594               0 :         return NULL;
    3595                 :     }
    3596                 :     else
    3597                 :     {
    3598                 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
    3599              90 :         return (GDALDataset*) GDALOpen(pszFilename, GA_ReadOnly);
    3600                 : #else
    3601                 :         return new GDALFakePDFDataset();
    3602                 : #endif
    3603               0 :     }
    3604                 : }

Generated by: LCOV version 1.7