LCOV - code coverage report
Current view: directory - frmts/pdf - pdfcreatecopy.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2255 1978 87.7 %
Date: 2012-12-26 Functions: 50 45 90.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: pdfcreatecopy.cpp 25294 2012-12-09 10:04:51Z 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 25294 2012-12-09 10:04:51Z 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              79 : void GDALPDFWriter::Init()
      65                 : {
      66              79 :     nPageResourceId = 0;
      67              79 :     nStructTreeRootId = 0;
      68              79 :     nCatalogId = nCatalogGen = 0;
      69              79 :     bInWriteObj = FALSE;
      70              79 :     nInfoId = nInfoGen = 0;
      71              79 :     nXMPId = nXMPGen = 0;
      72              79 :     nNamesId = 0;
      73                 : 
      74              79 :     nLastStartXRef = 0;
      75              79 :     nLastXRefSize = 0;
      76              79 :     bCanUpdate = FALSE;
      77              79 : }
      78                 : 
      79                 : /************************************************************************/
      80                 : /*                         GDALPDFWriter()                              */
      81                 : /************************************************************************/
      82                 : 
      83              79 : GDALPDFWriter::GDALPDFWriter(VSILFILE* fpIn, int bAppend) : fp(fpIn)
      84                 : {
      85              79 :     Init();
      86                 : 
      87              79 :     if (!bAppend)
      88                 :     {
      89              53 :         VSIFPrintfL(fp, "%%PDF-1.6\n");
      90                 : 
      91                 :         /* See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate that the content will be binary */
      92              53 :         VSIFPrintfL(fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF);
      93                 : 
      94              53 :         nPageResourceId = AllocNewObject();
      95              53 :         nCatalogId = AllocNewObject();
      96                 :     }
      97              79 : }
      98                 : 
      99                 : /************************************************************************/
     100                 : /*                         ~GDALPDFWriter()                             */
     101                 : /************************************************************************/
     102                 : 
     103              79 : GDALPDFWriter::~GDALPDFWriter()
     104                 : {
     105              79 :     Close();
     106              79 : }
     107                 : 
     108                 : /************************************************************************/
     109                 : /*                          ParseIndirectRef()                          */
     110                 : /************************************************************************/
     111                 : 
     112              30 : static int ParseIndirectRef(const char* pszStr, int& nNum, int &nGen)
     113                 : {
     114              60 :     while(*pszStr == ' ')
     115               0 :         pszStr ++;
     116                 : 
     117              30 :     nNum = atoi(pszStr);
     118              94 :     while(*pszStr >= '0' && *pszStr <= '9')
     119              34 :         pszStr ++;
     120              30 :     if (*pszStr != ' ')
     121               0 :         return FALSE;
     122                 : 
     123              90 :     while(*pszStr == ' ')
     124              30 :         pszStr ++;
     125                 : 
     126              30 :     nGen = atoi(pszStr);
     127              90 :     while(*pszStr >= '0' && *pszStr <= '9')
     128              30 :         pszStr ++;
     129              30 :     if (*pszStr != ' ')
     130               0 :         return FALSE;
     131                 : 
     132              90 :     while(*pszStr == ' ')
     133              30 :         pszStr ++;
     134                 : 
     135              30 :     return *pszStr == 'R';
     136                 : }
     137                 : 
     138                 : /************************************************************************/
     139                 : /*                       ParseTrailerAndXRef()                          */
     140                 : /************************************************************************/
     141                 : 
     142              26 : int GDALPDFWriter::ParseTrailerAndXRef()
     143                 : {
     144              26 :     VSIFSeekL(fp, 0, SEEK_END);
     145                 :     char szBuf[1024+1];
     146              26 :     vsi_l_offset nOffset = VSIFTellL(fp);
     147                 : 
     148              26 :     if (nOffset > 128)
     149              26 :         nOffset -= 128;
     150                 :     else
     151               0 :         nOffset = 0;
     152                 : 
     153                 :     /* Find startxref section */
     154              26 :     VSIFSeekL(fp, nOffset, SEEK_SET);
     155              26 :     int nRead = VSIFReadL(szBuf, 1, 128, fp);
     156              26 :     szBuf[nRead] = 0;
     157              26 :     if (nRead < 9)
     158               0 :         return FALSE;
     159                 : 
     160              26 :     const char* pszStartXRef = NULL;
     161                 :     int i;
     162             336 :     for(i = nRead - 9; i>= 0; i --)
     163                 :     {
     164             336 :         if (strncmp(szBuf + i, "startxref", 9) == 0)
     165                 :         {
     166              26 :             pszStartXRef = szBuf + i;
     167              26 :             break;
     168                 :         }
     169                 :     }
     170              26 :     if (pszStartXRef == NULL)
     171                 :     {
     172               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref");
     173               0 :         return FALSE;
     174                 :     }
     175              26 :     pszStartXRef += 9;
     176              78 :     while(*pszStartXRef == '\r' || *pszStartXRef == '\n')
     177              26 :         pszStartXRef ++;
     178              26 :     if (*pszStartXRef == '\0')
     179                 :     {
     180               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref");
     181               0 :         return FALSE;
     182                 :     }
     183                 : 
     184              26 :     nLastStartXRef = atoi(pszStartXRef);
     185                 : 
     186                 :     /* Skip to beginning of xref section */
     187              26 :     VSIFSeekL(fp, nLastStartXRef, SEEK_SET);
     188                 : 
     189                 :     /* And skip to trailer */
     190                 :     const char* pszLine;
     191              26 :     while( (pszLine = CPLReadLineL(fp)) != NULL)
     192                 :     {
     193             324 :         if (strncmp(pszLine, "trailer", 7) == 0)
     194              26 :             break;
     195                 :     }
     196                 : 
     197              26 :     if( pszLine == NULL )
     198                 :     {
     199               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer");
     200               0 :         return FALSE;
     201                 :     }
     202                 : 
     203                 :     /* Read trailer content */
     204              26 :     nRead = VSIFReadL(szBuf, 1, 1024, fp);
     205              26 :     szBuf[nRead] = 0;
     206                 : 
     207                 :     /* Find XRef size */
     208              26 :     const char* pszSize = strstr(szBuf, "/Size");
     209              26 :     if (pszSize == NULL)
     210                 :     {
     211               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Size");
     212               0 :         return FALSE;
     213                 :     }
     214              26 :     pszSize += 5;
     215              78 :     while(*pszSize == ' ')
     216              26 :         pszSize ++;
     217              26 :     nLastXRefSize = atoi(pszSize);
     218                 : 
     219                 :     /* Find Root object */
     220              26 :     const char* pszRoot = strstr(szBuf, "/Root");
     221              26 :     if (pszRoot == NULL)
     222                 :     {
     223               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Root");
     224               0 :         return FALSE;
     225                 :     }
     226              26 :     pszRoot += 5;
     227              78 :     while(*pszRoot == ' ')
     228              26 :         pszRoot ++;
     229                 : 
     230              26 :     if (!ParseIndirectRef(pszRoot, nCatalogId, nCatalogGen))
     231                 :     {
     232               0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Root");
     233               0 :         return FALSE;
     234                 :     }
     235                 : 
     236                 :     /* Find Info object */
     237              26 :     const char* pszInfo = strstr(szBuf, "/Info");
     238              26 :     if (pszInfo != NULL)
     239                 :     {
     240               4 :         pszInfo += 5;
     241              12 :         while(*pszInfo == ' ')
     242               4 :             pszInfo ++;
     243                 : 
     244               4 :         if (!ParseIndirectRef(pszInfo, nInfoId, nInfoGen))
     245                 :         {
     246               0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Info");
     247               0 :             nInfoId = nInfoGen = 0;
     248                 :         }
     249                 :     }
     250                 : 
     251              26 :     VSIFSeekL(fp, 0, SEEK_END);
     252                 : 
     253              26 :     return TRUE;
     254                 : }
     255                 : 
     256                 : /************************************************************************/
     257                 : /*                              Close()                                 */
     258                 : /************************************************************************/
     259                 : 
     260             158 : void GDALPDFWriter::Close()
     261                 : {
     262             158 :     if (fp)
     263                 :     {
     264              79 :         CPLAssert(!bInWriteObj);
     265              79 :         if (nPageResourceId)
     266                 :         {
     267              53 :             WritePages();
     268              53 :             WriteXRefTableAndTrailer();
     269                 :         }
     270              26 :         else if (bCanUpdate)
     271                 :         {
     272              22 :             WriteXRefTableAndTrailer();
     273                 :         }
     274              79 :         VSIFCloseL(fp);
     275                 :     }
     276             158 :     fp = NULL;
     277             158 : }
     278                 : 
     279                 : /************************************************************************/
     280                 : /*                           UpdateProj()                               */
     281                 : /************************************************************************/
     282                 : 
     283              10 : void GDALPDFWriter::UpdateProj(GDALDataset* poSrcDS,
     284                 :                                double dfDPI,
     285                 :                                GDALPDFDictionaryRW* poPageDict,
     286                 :                                int nPageNum, int nPageGen)
     287                 : {
     288              10 :     bCanUpdate = TRUE;
     289              10 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     290              10 :         asXRefEntries.resize(nLastXRefSize - 1);
     291                 : 
     292              10 :     int nViewportId = 0;
     293              10 :     int nLGIDictId = 0;
     294                 : 
     295              10 :     CPLAssert(nPageNum != 0);
     296              10 :     CPLAssert(poPageDict != NULL);
     297                 : 
     298              10 :     PDFMargins sMargins = {0, 0, 0, 0};
     299                 : 
     300              10 :     const char* pszGEO_ENCODING = CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000");
     301              10 :     if (EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH"))
     302               9 :         nViewportId = WriteSRS_ISO32000(poSrcDS, dfDPI / 72.0, NULL, &sMargins, TRUE);
     303              10 :     if (EQUAL(pszGEO_ENCODING, "OGC_BP") || EQUAL(pszGEO_ENCODING, "BOTH"))
     304               1 :         nLGIDictId = WriteSRS_OGC_BP(poSrcDS, dfDPI / 72.0, NULL, &sMargins);
     305                 : 
     306                 : #ifdef invalidate_xref_entry
     307                 :     GDALPDFObject* poVP = poPageDict->Get("VP");
     308                 :     if (poVP)
     309                 :     {
     310                 :         if (poVP->GetType() == PDFObjectType_Array &&
     311                 :             poVP->GetArray()->GetLength() == 1)
     312                 :             poVP = poVP->GetArray()->Get(0);
     313                 : 
     314                 :         int nVPId = poVP->GetRefNum();
     315                 :         if (nVPId)
     316                 :         {
     317                 :             asXRefEntries[nVPId - 1].bFree = TRUE;
     318                 :             asXRefEntries[nVPId - 1].nGen ++;
     319                 :         }
     320                 :     }
     321                 : #endif
     322                 : 
     323              10 :     poPageDict->Remove("VP");
     324              10 :     poPageDict->Remove("LGIDict");
     325                 : 
     326              10 :     if (nViewportId)
     327                 :     {
     328                 :         poPageDict->Add("VP", &((new GDALPDFArrayRW())->
     329               7 :                 Add(nViewportId, 0)));
     330                 :     }
     331                 : 
     332              10 :     if (nLGIDictId)
     333                 :     {
     334               1 :         poPageDict->Add("LGIDict", nLGIDictId, 0);
     335                 :     }
     336                 : 
     337              10 :     StartObj(nPageNum, nPageGen);
     338              10 :     VSIFPrintfL(fp, "%s\n", poPageDict->Serialize().c_str());
     339              10 :     EndObj();
     340              10 : }
     341                 : 
     342                 : /************************************************************************/
     343                 : /*                           UpdateInfo()                               */
     344                 : /************************************************************************/
     345                 : 
     346               6 : void GDALPDFWriter::UpdateInfo(GDALDataset* poSrcDS)
     347                 : {
     348               6 :     bCanUpdate = TRUE;
     349               6 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     350               6 :         asXRefEntries.resize(nLastXRefSize - 1);
     351                 : 
     352               6 :     int nNewInfoId = SetInfo(poSrcDS, NULL);
     353                 :     /* Write empty info, because podofo driver will find the dangling info instead */
     354               6 :     if (nNewInfoId == 0 && nInfoId != 0)
     355                 :     {
     356                 : #ifdef invalidate_xref_entry
     357                 :         asXRefEntries[nInfoId - 1].bFree = TRUE;
     358                 :         asXRefEntries[nInfoId - 1].nGen ++;
     359                 : #else
     360               2 :         StartObj(nInfoId, nInfoGen);
     361               2 :         VSIFPrintfL(fp, "<< >>\n");
     362               2 :         EndObj();
     363                 : #endif
     364                 :     }
     365               6 : }
     366                 : 
     367                 : /************************************************************************/
     368                 : /*                           UpdateXMP()                                */
     369                 : /************************************************************************/
     370                 : 
     371               6 : void GDALPDFWriter::UpdateXMP(GDALDataset* poSrcDS,
     372                 :                               GDALPDFDictionaryRW* poCatalogDict)
     373                 : {
     374               6 :     bCanUpdate = TRUE;
     375               6 :     if ((int)asXRefEntries.size() < nLastXRefSize - 1)
     376               6 :         asXRefEntries.resize(nLastXRefSize - 1);
     377                 : 
     378               6 :     CPLAssert(nCatalogId != 0);
     379               6 :     CPLAssert(poCatalogDict != NULL);
     380                 : 
     381               6 :     GDALPDFObject* poMetadata = poCatalogDict->Get("Metadata");
     382               6 :     if (poMetadata)
     383                 :     {
     384               4 :         nXMPId = poMetadata->GetRefNum();
     385               4 :         nXMPGen = poMetadata->GetRefGen();
     386                 :     }
     387                 : 
     388               6 :     poCatalogDict->Remove("Metadata");
     389               6 :     int nNewXMPId = SetXMP(poSrcDS, NULL);
     390                 : 
     391                 :     /* Write empty metadata, because podofo driver will find the dangling info instead */
     392               6 :     if (nNewXMPId == 0 && nXMPId != 0)
     393                 :     {
     394               2 :         StartObj(nXMPId, nXMPGen);
     395               2 :         VSIFPrintfL(fp, "<< >>\n");
     396               2 :         EndObj();
     397                 :     }
     398                 : 
     399               6 :     if (nXMPId)
     400               6 :         poCatalogDict->Add("Metadata", nXMPId, 0);
     401                 : 
     402               6 :     StartObj(nCatalogId, nCatalogGen);
     403               6 :     VSIFPrintfL(fp, "%s\n", poCatalogDict->Serialize().c_str());
     404               6 :     EndObj();
     405               6 : }
     406                 : 
     407                 : /************************************************************************/
     408                 : /*                           AllocNewObject()                           */
     409                 : /************************************************************************/
     410                 : 
     411            1044 : int GDALPDFWriter::AllocNewObject()
     412                 : {
     413            1044 :     asXRefEntries.push_back(GDALXRefEntry());
     414            1044 :     return (int)asXRefEntries.size();
     415                 : }
     416                 : 
     417                 : /************************************************************************/
     418                 : /*                        WriteXRefTableAndTrailer()                    */
     419                 : /************************************************************************/
     420                 : 
     421              75 : void GDALPDFWriter::WriteXRefTableAndTrailer()
     422                 : {
     423              75 :     vsi_l_offset nOffsetXREF = VSIFTellL(fp);
     424              75 :     VSIFPrintfL(fp, "xref\n");
     425                 : 
     426                 :     char buffer[16];
     427              75 :     if (bCanUpdate)
     428                 :     {
     429              22 :         VSIFPrintfL(fp, "0 1\n");
     430              22 :         VSIFPrintfL(fp, "0000000000 65535 f \n");
     431             332 :         for(size_t i=0;i<asXRefEntries.size();)
     432                 :         {
     433             288 :             if (asXRefEntries[i].nOffset != 0 || asXRefEntries[i].bFree)
     434                 :             {
     435                 :                 /* Find number of consecutive objects */
     436              36 :                 size_t nCount = 1;
     437              86 :                 while(i + nCount <asXRefEntries.size() &&
     438                 :                     (asXRefEntries[i + nCount].nOffset != 0 || asXRefEntries[i + nCount].bFree))
     439              14 :                     nCount ++;
     440                 : 
     441              36 :                 VSIFPrintfL(fp, "%d %d\n", (int)i + 1, (int)nCount);
     442              36 :                 size_t iEnd = i + nCount;
     443              86 :                 for(; i < iEnd; i++)
     444                 :                 {
     445              50 :                     snprintf (buffer, sizeof(buffer), "%010ld", (long)asXRefEntries[i].nOffset);
     446                 :                     VSIFPrintfL(fp, "%s %05d %c \n",
     447                 :                                 buffer, asXRefEntries[i].nGen,
     448              50 :                                 asXRefEntries[i].bFree ? 'f' : 'n');
     449                 :                 }
     450                 :             }
     451                 :             else
     452                 :             {
     453             252 :                 i++;
     454                 :             }
     455                 :         }
     456                 :     }
     457                 :     else
     458                 :     {
     459                 :         VSIFPrintfL(fp, "%d %d\n",
     460              53 :                     0, (int)asXRefEntries.size() + 1);
     461              53 :         VSIFPrintfL(fp, "0000000000 65535 f \n");
     462            1071 :         for(size_t i=0;i<asXRefEntries.size();i++)
     463                 :         {
     464            1018 :             snprintf (buffer, sizeof(buffer), "%010ld", (long)asXRefEntries[i].nOffset);
     465            1018 :             VSIFPrintfL(fp, "%s %05d n \n", buffer, asXRefEntries[i].nGen);
     466                 :         }
     467                 :     }
     468                 : 
     469              75 :     VSIFPrintfL(fp, "trailer\n");
     470              75 :     GDALPDFDictionaryRW oDict;
     471                 :     oDict.Add("Size", (int)asXRefEntries.size() + 1)
     472              75 :          .Add("Root", nCatalogId, nCatalogGen);
     473              75 :     if (nInfoId)
     474               8 :         oDict.Add("Info", nInfoId, nInfoGen);
     475              75 :     if (nLastStartXRef)
     476              22 :         oDict.Add("Prev", nLastStartXRef);
     477              75 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
     478                 : 
     479                 :     VSIFPrintfL(fp,
     480                 :                 "startxref\n"
     481                 :                 "%ld\n"
     482                 :                 "%%%%EOF\n",
     483              75 :                 (long)nOffsetXREF);
     484              75 : }
     485                 : 
     486                 : /************************************************************************/
     487                 : /*                              StartObj()                              */
     488                 : /************************************************************************/
     489                 : 
     490            1068 : void GDALPDFWriter::StartObj(int nObjectId, int nGen)
     491                 : {
     492            1068 :     CPLAssert(!bInWriteObj);
     493            1068 :     CPLAssert(nObjectId - 1 < (int)asXRefEntries.size());
     494            1068 :     CPLAssert(asXRefEntries[nObjectId - 1].nOffset == 0);
     495            1068 :     asXRefEntries[nObjectId - 1].nOffset = VSIFTellL(fp);
     496            1068 :     asXRefEntries[nObjectId - 1].nGen = nGen;
     497            1068 :     VSIFPrintfL(fp, "%d %d obj\n", nObjectId, nGen);
     498            1068 :     bInWriteObj = TRUE;
     499            1068 : }
     500                 : 
     501                 : /************************************************************************/
     502                 : /*                               EndObj()                               */
     503                 : /************************************************************************/
     504                 : 
     505            1068 : void GDALPDFWriter::EndObj()
     506                 : {
     507            1068 :     CPLAssert(bInWriteObj);
     508            1068 :     VSIFPrintfL(fp, "endobj\n");
     509            1068 :     bInWriteObj = FALSE;
     510            1068 : }
     511                 : 
     512                 : 
     513                 : /************************************************************************/
     514                 : /*                         GDALPDFFind4Corners()                        */
     515                 : /************************************************************************/
     516                 : 
     517                 : static
     518              12 : void GDALPDFFind4Corners(const GDAL_GCP* pasGCPList,
     519                 :                          int& iUL, int& iUR, int& iLR, int& iLL)
     520                 : {
     521              12 :     double dfMeanX = 0, dfMeanY = 0;
     522                 :     int i;
     523                 : 
     524              12 :     iUL = 0;
     525              12 :     iUR = 0;
     526              12 :     iLR = 0;
     527              12 :     iLL = 0;
     528                 : 
     529              60 :     for(i = 0; i < 4; i++ )
     530                 :     {
     531              48 :         dfMeanX += pasGCPList[i].dfGCPPixel;
     532              48 :         dfMeanY += pasGCPList[i].dfGCPLine;
     533                 :     }
     534              12 :     dfMeanX /= 4;
     535              12 :     dfMeanY /= 4;
     536                 : 
     537              60 :     for(i = 0; i < 4; i++ )
     538                 :     {
     539              84 :         if (pasGCPList[i].dfGCPPixel < dfMeanX &&
     540              24 :             pasGCPList[i].dfGCPLine  < dfMeanY )
     541              12 :             iUL = i;
     542                 : 
     543              72 :         else if (pasGCPList[i].dfGCPPixel > dfMeanX &&
     544              24 :                     pasGCPList[i].dfGCPLine  < dfMeanY )
     545              12 :             iUR = i;
     546                 : 
     547              48 :         else if (pasGCPList[i].dfGCPPixel > dfMeanX &&
     548              12 :                     pasGCPList[i].dfGCPLine  > dfMeanY )
     549              12 :             iLR = i;
     550                 : 
     551              24 :         else if (pasGCPList[i].dfGCPPixel < dfMeanX &&
     552              12 :                     pasGCPList[i].dfGCPLine  > dfMeanY )
     553              12 :             iLL = i;
     554                 :     }
     555              12 : }
     556                 : 
     557                 : /************************************************************************/
     558                 : /*                         WriteSRS_ISO32000()                          */
     559                 : /************************************************************************/
     560                 : 
     561              54 : int  GDALPDFWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS,
     562                 :                                       double dfUserUnit,
     563                 :                                       const char* pszNEATLINE,
     564                 :                                       PDFMargins* psMargins,
     565                 :                                       int bWriteViewport)
     566                 : {
     567              54 :     int  nWidth = poSrcDS->GetRasterXSize();
     568              54 :     int  nHeight = poSrcDS->GetRasterYSize();
     569              54 :     const char* pszWKT = poSrcDS->GetProjectionRef();
     570                 :     double adfGeoTransform[6];
     571                 : 
     572              54 :     int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None);
     573              54 :     const GDAL_GCP* pasGCPList = (poSrcDS->GetGCPCount() == 4) ? poSrcDS->GetGCPs() : NULL;
     574              54 :     if (pasGCPList != NULL)
     575               1 :         pszWKT = poSrcDS->GetGCPProjection();
     576                 : 
     577              54 :     if( !bHasGT && pasGCPList == NULL )
     578               6 :         return 0;
     579                 : 
     580              48 :     if( pszWKT == NULL || EQUAL(pszWKT, "") )
     581               2 :         return 0;
     582                 : 
     583                 :     double adfGPTS[8];
     584                 : 
     585              46 :     double dfULPixel = 0;
     586              46 :     double dfULLine = 0;
     587              46 :     double dfLRPixel = nWidth;
     588              46 :     double dfLRLine = nHeight;
     589                 : 
     590                 :     GDAL_GCP asNeatLineGCPs[4];
     591              46 :     if (pszNEATLINE == NULL)
     592              45 :         pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE");
     593              46 :     if( bHasGT && pszNEATLINE != NULL && pszNEATLINE[0] != '\0' )
     594                 :     {
     595               5 :         OGRGeometry* poGeom = NULL;
     596               5 :         OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom );
     597               5 :         if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
     598                 :         {
     599               5 :             OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing();
     600                 :             double adfGeoTransformInv[6];
     601               5 :             if( poLS != NULL && poLS->getNumPoints() == 5 &&
     602                 :                 GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) )
     603                 :             {
     604              25 :                 for(int i=0;i<4;i++)
     605                 :                 {
     606              20 :                     double X = asNeatLineGCPs[i].dfGCPX = poLS->getX(i);
     607              20 :                     double Y = asNeatLineGCPs[i].dfGCPY = poLS->getY(i);
     608              20 :                     double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2];
     609              20 :                     double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5];
     610              20 :                     asNeatLineGCPs[i].dfGCPPixel = x;
     611              20 :                     asNeatLineGCPs[i].dfGCPLine = y;
     612                 :                 }
     613                 : 
     614               5 :                 int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
     615                 :                 GDALPDFFind4Corners(asNeatLineGCPs,
     616               5 :                                     iUL,iUR, iLR, iLL);
     617                 : 
     618               5 :                 if (fabs(asNeatLineGCPs[iUL].dfGCPPixel - asNeatLineGCPs[iLL].dfGCPPixel) > .5 ||
     619                 :                     fabs(asNeatLineGCPs[iUR].dfGCPPixel - asNeatLineGCPs[iLR].dfGCPPixel) > .5 ||
     620                 :                     fabs(asNeatLineGCPs[iUL].dfGCPLine - asNeatLineGCPs[iUR].dfGCPLine) > .5 ||
     621                 :                     fabs(asNeatLineGCPs[iLL].dfGCPLine - asNeatLineGCPs[iLR].dfGCPLine) > .5)
     622                 :                 {
     623                 :                     CPLError(CE_Warning, CPLE_NotSupported,
     624               0 :                             "Neatline coordinates should form a rectangle in pixel space. Ignoring it");
     625               0 :                     for(int i=0;i<4;i++)
     626                 :                     {
     627                 :                         CPLDebug("PDF", "pixel[%d] = %.1f, line[%d] = %.1f",
     628                 :                                 i, asNeatLineGCPs[i].dfGCPPixel,
     629               0 :                                 i, asNeatLineGCPs[i].dfGCPLine);
     630                 :                     }
     631                 :                 }
     632                 :                 else
     633                 :                 {
     634               5 :                     pasGCPList = asNeatLineGCPs;
     635                 :                 }
     636                 :             }
     637                 :         }
     638               5 :         delete poGeom;
     639                 :     }
     640                 : 
     641              46 :     if( pasGCPList )
     642                 :     {
     643               6 :         int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
     644                 :         GDALPDFFind4Corners(pasGCPList,
     645               6 :                             iUL,iUR, iLR, iLL);
     646                 : 
     647              42 :         if (fabs(pasGCPList[iUL].dfGCPPixel - pasGCPList[iLL].dfGCPPixel) > .5 ||
     648              12 :             fabs(pasGCPList[iUR].dfGCPPixel - pasGCPList[iLR].dfGCPPixel) > .5 ||
     649              12 :             fabs(pasGCPList[iUL].dfGCPLine - pasGCPList[iUR].dfGCPLine) > .5 ||
     650              12 :             fabs(pasGCPList[iLL].dfGCPLine - pasGCPList[iLR].dfGCPLine) > .5)
     651                 :         {
     652                 :             CPLError(CE_Failure, CPLE_NotSupported,
     653               0 :                      "GCPs should form a rectangle in pixel space");
     654               0 :             return 0;
     655                 :         }
     656                 :         
     657               6 :         dfULPixel = pasGCPList[iUL].dfGCPPixel;
     658               6 :         dfULLine = pasGCPList[iUL].dfGCPLine;
     659               6 :         dfLRPixel = pasGCPList[iLR].dfGCPPixel;
     660               6 :         dfLRLine = pasGCPList[iLR].dfGCPLine;
     661                 :         
     662                 :         /* Upper-left */
     663               6 :         adfGPTS[0] = pasGCPList[iUL].dfGCPX;
     664               6 :         adfGPTS[1] = pasGCPList[iUL].dfGCPY;
     665                 :         
     666                 :         /* Lower-left */
     667               6 :         adfGPTS[2] = pasGCPList[iLL].dfGCPX;
     668               6 :         adfGPTS[3] = pasGCPList[iLL].dfGCPY;
     669                 :         
     670                 :         /* Lower-right */
     671               6 :         adfGPTS[4] = pasGCPList[iLR].dfGCPX;
     672               6 :         adfGPTS[5] = pasGCPList[iLR].dfGCPY;
     673                 :         
     674                 :         /* Upper-right */
     675               6 :         adfGPTS[6] = pasGCPList[iUR].dfGCPX;
     676               6 :         adfGPTS[7] = pasGCPList[iUR].dfGCPY;
     677                 :     }
     678                 :     else
     679                 :     {
     680                 :         /* Upper-left */
     681              40 :         adfGPTS[0] = PIXEL_TO_GEO_X(0, 0);
     682              40 :         adfGPTS[1] = PIXEL_TO_GEO_Y(0, 0);
     683                 : 
     684                 :         /* Lower-left */
     685              40 :         adfGPTS[2] = PIXEL_TO_GEO_X(0, nHeight);
     686              40 :         adfGPTS[3] = PIXEL_TO_GEO_Y(0, nHeight);
     687                 : 
     688                 :         /* Lower-right */
     689              40 :         adfGPTS[4] = PIXEL_TO_GEO_X(nWidth, nHeight);
     690              40 :         adfGPTS[5] = PIXEL_TO_GEO_Y(nWidth, nHeight);
     691                 : 
     692                 :         /* Upper-right */
     693              40 :         adfGPTS[6] = PIXEL_TO_GEO_X(nWidth, 0);
     694              40 :         adfGPTS[7] = PIXEL_TO_GEO_Y(nWidth, 0);
     695                 :     }
     696                 :     
     697              46 :     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
     698              46 :     if( hSRS == NULL )
     699               0 :         return 0;
     700              46 :     OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS);
     701              46 :     if( hSRSGeog == NULL )
     702                 :     {
     703               0 :         OSRDestroySpatialReference(hSRS);
     704               0 :         return 0;
     705                 :     }
     706              46 :     OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog);
     707              46 :     if( hCT == NULL )
     708                 :     {
     709               0 :         OSRDestroySpatialReference(hSRS);
     710               0 :         OSRDestroySpatialReference(hSRSGeog);
     711               0 :         return 0;
     712                 :     }
     713                 : 
     714              46 :     int bSuccess = TRUE;
     715                 :     
     716              46 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 0, adfGPTS + 1, NULL ) == 1);
     717              46 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 2, adfGPTS + 3, NULL ) == 1);
     718              46 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 4, adfGPTS + 5, NULL ) == 1);
     719              46 :     bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 6, adfGPTS + 7, NULL ) == 1);
     720                 : 
     721              46 :     if (!bSuccess)
     722                 :     {
     723               0 :         OSRDestroySpatialReference(hSRS);
     724               0 :         OSRDestroySpatialReference(hSRSGeog);
     725               0 :         OCTDestroyCoordinateTransformation(hCT);
     726               0 :         return 0;
     727                 :     }
     728                 : 
     729              46 :     const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, NULL );
     730              46 :     const char * pszAuthorityName = OSRGetAuthorityName( hSRS, NULL );
     731              46 :     int nEPSGCode = 0;
     732              46 :     if( pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") &&
     733                 :         pszAuthorityCode != NULL )
     734              44 :         nEPSGCode = atoi(pszAuthorityCode);
     735                 : 
     736              46 :     int bIsGeographic = OSRIsGeographic(hSRS);
     737                 : 
     738              46 :     OSRMorphToESRI(hSRS);
     739              46 :     char* pszESRIWKT = NULL;
     740              46 :     OSRExportToWkt(hSRS, &pszESRIWKT);
     741                 : 
     742              46 :     OSRDestroySpatialReference(hSRS);
     743              46 :     OSRDestroySpatialReference(hSRSGeog);
     744              46 :     OCTDestroyCoordinateTransformation(hCT);
     745              46 :     hSRS = NULL;
     746              46 :     hSRSGeog = NULL;
     747              46 :     hCT = NULL;
     748                 : 
     749              46 :     if (pszESRIWKT == NULL)
     750               0 :         return 0;
     751                 : 
     752              46 :     int nViewportId = (bWriteViewport) ? AllocNewObject() : 0;
     753              46 :     int nMeasureId = AllocNewObject();
     754              46 :     int nGCSId = AllocNewObject();
     755                 : 
     756              46 :     if (nViewportId)
     757                 :     {
     758              44 :         StartObj(nViewportId);
     759              44 :         GDALPDFDictionaryRW oViewPortDict;
     760                 :         oViewPortDict.Add("Type", GDALPDFObjectRW::CreateName("Viewport"))
     761                 :                     .Add("Name", "Layer")
     762                 :                     .Add("BBox", &((new GDALPDFArrayRW())
     763                 :                                     ->Add(dfULPixel / dfUserUnit + psMargins->nLeft)
     764                 :                                     .Add((nHeight - dfLRLine) / dfUserUnit + psMargins->nBottom)
     765                 :                                     .Add(dfLRPixel / dfUserUnit + psMargins->nLeft)
     766                 :                                     .Add((nHeight - dfULLine) / dfUserUnit + psMargins->nBottom)))
     767              44 :                     .Add("Measure", nMeasureId, 0);
     768              44 :         VSIFPrintfL(fp, "%s\n", oViewPortDict.Serialize().c_str());
     769              44 :         EndObj();
     770                 :     }
     771                 : 
     772              46 :     StartObj(nMeasureId);
     773              46 :     GDALPDFDictionaryRW oMeasureDict;
     774                 :     oMeasureDict .Add("Type", GDALPDFObjectRW::CreateName("Measure"))
     775                 :                  .Add("Subtype", GDALPDFObjectRW::CreateName("GEO"))
     776                 :                  .Add("Bounds", &((new GDALPDFArrayRW())
     777                 :                                 ->Add(0).Add(1).
     778                 :                                   Add(0).Add(0).
     779                 :                                   Add(1).Add(0).
     780                 :                                   Add(1).Add(1)))
     781                 :                  .Add("GPTS", &((new GDALPDFArrayRW())
     782                 :                                 ->Add(adfGPTS[1]).Add(adfGPTS[0]).
     783                 :                                   Add(adfGPTS[3]).Add(adfGPTS[2]).
     784                 :                                   Add(adfGPTS[5]).Add(adfGPTS[4]).
     785                 :                                   Add(adfGPTS[7]).Add(adfGPTS[6])))
     786                 :                  .Add("LPTS", &((new GDALPDFArrayRW())
     787                 :                                 ->Add(0).Add(1).
     788                 :                                   Add(0).Add(0).
     789                 :                                   Add(1).Add(0).
     790                 :                                   Add(1).Add(1)))
     791              46 :                  .Add("GCS", nGCSId, 0);
     792              46 :     VSIFPrintfL(fp, "%s\n", oMeasureDict.Serialize().c_str());
     793              46 :     EndObj();
     794                 : 
     795                 : 
     796              46 :     StartObj(nGCSId);
     797              46 :     GDALPDFDictionaryRW oGCSDict;
     798                 :     oGCSDict.Add("Type", GDALPDFObjectRW::CreateName(bIsGeographic ? "GEOGCS" : "PROJCS"))
     799              46 :             .Add("WKT", pszESRIWKT);
     800              46 :     if (nEPSGCode)
     801              44 :         oGCSDict.Add("EPSG", nEPSGCode);
     802              46 :     VSIFPrintfL(fp, "%s\n", oGCSDict.Serialize().c_str());
     803              46 :     EndObj();
     804                 : 
     805              46 :     CPLFree(pszESRIWKT);
     806                 : 
     807              46 :     return nViewportId ? nViewportId : nMeasureId;
     808                 : }
     809                 : 
     810                 : /************************************************************************/
     811                 : /*                     GDALPDFBuildOGC_BP_Datum()                       */
     812                 : /************************************************************************/
     813                 : 
     814               7 : static GDALPDFObject* GDALPDFBuildOGC_BP_Datum(const OGRSpatialReference* poSRS)
     815                 : {
     816               7 :     const OGR_SRSNode* poDatumNode = poSRS->GetAttrNode("DATUM");
     817               7 :     const char* pszDatumDescription = NULL;
     818               7 :     if (poDatumNode && poDatumNode->GetChildCount() > 0)
     819               7 :         pszDatumDescription = poDatumNode->GetChild(0)->GetValue();
     820                 : 
     821               7 :     GDALPDFObjectRW* poPDFDatum = NULL;
     822                 : 
     823               7 :     if (pszDatumDescription)
     824                 :     {
     825               7 :         double dfSemiMajor = poSRS->GetSemiMajor();
     826               7 :         double dfInvFlattening = poSRS->GetInvFlattening();
     827               7 :         int nEPSGDatum = -1;
     828               7 :         const char *pszAuthority = poSRS->GetAuthorityName( "DATUM" );
     829               7 :         if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") )
     830               7 :             nEPSGDatum = atoi(poSRS->GetAuthorityCode( "DATUM" ));
     831                 : 
     832               8 :         if( EQUAL(pszDatumDescription,SRS_DN_WGS84) || nEPSGDatum == 6326 )
     833               1 :             poPDFDatum = GDALPDFObjectRW::CreateString("WGE");
     834              12 :         else if( EQUAL(pszDatumDescription, SRS_DN_NAD27) || nEPSGDatum == 6267 )
     835               6 :             poPDFDatum = GDALPDFObjectRW::CreateString("NAS");
     836               0 :         else if( EQUAL(pszDatumDescription, SRS_DN_NAD83) || nEPSGDatum == 6269 )
     837               0 :             poPDFDatum = GDALPDFObjectRW::CreateString("NAR");
     838                 :         else
     839                 :         {
     840                 :             CPLDebug("PDF",
     841                 :                      "Unhandled datum name (%s). Write datum parameters then.",
     842               0 :                      pszDatumDescription);
     843                 : 
     844               0 :             GDALPDFDictionaryRW* poPDFDatumDict = new GDALPDFDictionaryRW();
     845               0 :             poPDFDatum = GDALPDFObjectRW::CreateDictionary(poPDFDatumDict);
     846                 : 
     847               0 :             const OGR_SRSNode* poSpheroidNode = poSRS->GetAttrNode("SPHEROID");
     848               0 :             if (poSpheroidNode && poSpheroidNode->GetChildCount() >= 3)
     849                 :             {
     850               0 :                 poPDFDatumDict->Add("Description", pszDatumDescription);
     851                 : 
     852               0 :                 const char* pszEllipsoidCode = NULL;
     853                 : #ifdef disabled_because_terrago_toolbar_does_not_like_it
     854                 :                 if( ABS(dfSemiMajor-6378249.145) < 0.01
     855                 :                     && ABS(dfInvFlattening-293.465) < 0.0001 )
     856                 :                 {
     857                 :                     pszEllipsoidCode = "CD";     /* Clark 1880 */
     858                 :                 }
     859                 :                 else if( ABS(dfSemiMajor-6378245.0) < 0.01
     860                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     861                 :                 {
     862                 :                     pszEllipsoidCode = "KA";      /* Krassovsky */
     863                 :                 }
     864                 :                 else if( ABS(dfSemiMajor-6378388.0) < 0.01
     865                 :                          && ABS(dfInvFlattening-297.0) < 0.0001 )
     866                 :                 {
     867                 :                     pszEllipsoidCode = "IN";       /* International 1924 */
     868                 :                 }
     869                 :                 else if( ABS(dfSemiMajor-6378160.0) < 0.01
     870                 :                          && ABS(dfInvFlattening-298.25) < 0.0001 )
     871                 :                 {
     872                 :                     pszEllipsoidCode = "AN";    /* Australian */
     873                 :                 }
     874                 :                 else if( ABS(dfSemiMajor-6377397.155) < 0.01
     875                 :                          && ABS(dfInvFlattening-299.1528128) < 0.0001 )
     876                 :                 {
     877                 :                     pszEllipsoidCode = "BR";     /* Bessel 1841 */
     878                 :                 }
     879                 :                 else if( ABS(dfSemiMajor-6377483.865) < 0.01
     880                 :                          && ABS(dfInvFlattening-299.1528128) < 0.0001 )
     881                 :                 {
     882                 :                     pszEllipsoidCode = "BN";   /* Bessel 1841 (Namibia / Schwarzeck)*/
     883                 :                 }
     884                 : #if 0
     885                 :                 else if( ABS(dfSemiMajor-6378160.0) < 0.01
     886                 :                          && ABS(dfInvFlattening-298.247167427) < 0.0001 )
     887                 :                 {
     888                 :                     pszEllipsoidCode = "GRS67";      /* GRS 1967 */
     889                 :                 }
     890                 : #endif
     891                 :                 else if( ABS(dfSemiMajor-6378137) < 0.01
     892                 :                          && ABS(dfInvFlattening-298.257222101) < 0.000001 )
     893                 :                 {
     894                 :                     pszEllipsoidCode = "RF";      /* GRS 1980 */
     895                 :                 }
     896                 :                 else if( ABS(dfSemiMajor-6378206.4) < 0.01
     897                 :                          && ABS(dfInvFlattening-294.9786982) < 0.0001 )
     898                 :                 {
     899                 :                     pszEllipsoidCode = "CC";     /* Clarke 1866 */
     900                 :                 }
     901                 :                 else if( ABS(dfSemiMajor-6377340.189) < 0.01
     902                 :                          && ABS(dfInvFlattening-299.3249646) < 0.0001 )
     903                 :                 {
     904                 :                     pszEllipsoidCode = "AM";   /* Modified Airy */
     905                 :                 }
     906                 :                 else if( ABS(dfSemiMajor-6377563.396) < 0.01
     907                 :                          && ABS(dfInvFlattening-299.3249646) < 0.0001 )
     908                 :                 {
     909                 :                     pszEllipsoidCode = "AA";       /* Airy */
     910                 :                 }
     911                 :                 else if( ABS(dfSemiMajor-6378200) < 0.01
     912                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     913                 :                 {
     914                 :                     pszEllipsoidCode = "HE";    /* Helmert 1906 */
     915                 :                 }
     916                 :                 else if( ABS(dfSemiMajor-6378155) < 0.01
     917                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     918                 :                 {
     919                 :                     pszEllipsoidCode = "FA";   /* Modified Fischer 1960 */
     920                 :                 }
     921                 : #if 0
     922                 :                 else if( ABS(dfSemiMajor-6377298.556) < 0.01
     923                 :                          && ABS(dfInvFlattening-300.8017) < 0.0001 )
     924                 :                 {
     925                 :                     pszEllipsoidCode = "evrstSS";    /* Everest (Sabah & Sarawak) */
     926                 :                 }
     927                 :                 else if( ABS(dfSemiMajor-6378165.0) < 0.01
     928                 :                          && ABS(dfInvFlattening-298.3) < 0.0001 )
     929                 :                 {
     930                 :                     pszEllipsoidCode = "WGS60";
     931                 :                 }
     932                 :                 else if( ABS(dfSemiMajor-6378145.0) < 0.01
     933                 :                          && ABS(dfInvFlattening-298.25) < 0.0001 )
     934                 :                 {
     935                 :                     pszEllipsoidCode = "WGS66";
     936                 :                 }
     937                 : #endif
     938                 :                 else if( ABS(dfSemiMajor-6378135.0) < 0.01
     939                 :                          && ABS(dfInvFlattening-298.26) < 0.0001 )
     940                 :                 {
     941                 :                     pszEllipsoidCode = "WD";
     942                 :                 }
     943                 :                 else if( ABS(dfSemiMajor-6378137.0) < 0.01
     944                 :                          && ABS(dfInvFlattening-298.257223563) < 0.000001 )
     945                 :                 {
     946                 :                     pszEllipsoidCode = "WE";
     947                 :                 }
     948                 : #endif
     949                 : 
     950               0 :                 if( pszEllipsoidCode != NULL )
     951                 :                 {
     952               0 :                     poPDFDatumDict->Add("Ellipsoid", pszEllipsoidCode);
     953                 :                 }
     954                 :                 else
     955                 :                 {
     956                 :                     const char* pszEllipsoidDescription =
     957               0 :                         poSpheroidNode->GetChild(0)->GetValue();
     958                 : 
     959                 :                     CPLDebug("PDF",
     960                 :                          "Unhandled ellipsoid name (%s). Write ellipsoid parameters then.",
     961               0 :                          pszEllipsoidDescription);
     962                 : 
     963                 :                     poPDFDatumDict->Add("Ellipsoid",
     964                 :                         &((new GDALPDFDictionaryRW())
     965                 :                         ->Add("Description", pszEllipsoidDescription)
     966                 :                          .Add("SemiMajorAxis", dfSemiMajor, TRUE)
     967               0 :                          .Add("InvFlattening", dfInvFlattening, TRUE)));
     968                 :                 }
     969                 : 
     970               0 :                 const OGR_SRSNode *poTOWGS84 = poSRS->GetAttrNode( "TOWGS84" );
     971               0 :                 if( poTOWGS84 != NULL
     972                 :                     && poTOWGS84->GetChildCount() >= 3
     973                 :                     && (poTOWGS84->GetChildCount() < 7
     974                 :                     || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"")
     975                 :                         && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"")
     976                 :                         && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"")
     977                 :                         && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) )
     978                 :                 {
     979                 :                     poPDFDatumDict->Add("ToWGS84",
     980                 :                         &((new GDALPDFDictionaryRW())
     981                 :                         ->Add("dx", poTOWGS84->GetChild(0)->GetValue())
     982                 :                          .Add("dy", poTOWGS84->GetChild(1)->GetValue())
     983               0 :                          .Add("dz", poTOWGS84->GetChild(2)->GetValue())) );
     984                 :                 }
     985               0 :                 else if( poTOWGS84 != NULL && poTOWGS84->GetChildCount() >= 7)
     986                 :                 {
     987                 :                     poPDFDatumDict->Add("ToWGS84",
     988                 :                         &((new GDALPDFDictionaryRW())
     989                 :                         ->Add("dx", poTOWGS84->GetChild(0)->GetValue())
     990                 :                          .Add("dy", poTOWGS84->GetChild(1)->GetValue())
     991                 :                          .Add("dz", poTOWGS84->GetChild(2)->GetValue())
     992                 :                          .Add("rx", poTOWGS84->GetChild(3)->GetValue())
     993                 :                          .Add("ry", poTOWGS84->GetChild(4)->GetValue())
     994                 :                          .Add("rz", poTOWGS84->GetChild(5)->GetValue())
     995               0 :                          .Add("sf", poTOWGS84->GetChild(6)->GetValue())) );
     996                 :                 }
     997                 :             }
     998                 :         }
     999                 :     }
    1000                 :     else
    1001                 :     {
    1002                 :         CPLError(CE_Warning, CPLE_NotSupported,
    1003               0 :                  "No datum name. Defaulting to WGS84.");
    1004                 :     }
    1005                 : 
    1006               7 :     if (poPDFDatum == NULL)
    1007               0 :         poPDFDatum = GDALPDFObjectRW::CreateString("WGE");
    1008                 : 
    1009               7 :     return poPDFDatum;
    1010                 : }
    1011                 : 
    1012                 : /************************************************************************/
    1013                 : /*                   GDALPDFBuildOGC_BP_Projection()                    */
    1014                 : /************************************************************************/
    1015                 : 
    1016               7 : static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS)
    1017                 : {
    1018                 : 
    1019               7 :     const char* pszProjectionOGCBP = "GEOGRAPHIC";
    1020               7 :     const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
    1021                 : 
    1022               7 :     GDALPDFDictionaryRW* poProjectionDict = new GDALPDFDictionaryRW();
    1023               7 :     poProjectionDict->Add("Type", GDALPDFObjectRW::CreateName("Projection"));
    1024               7 :     poProjectionDict->Add("Datum", GDALPDFBuildOGC_BP_Datum(poSRS));
    1025                 : 
    1026               7 :     if( pszProjection == NULL )
    1027                 :     {
    1028               1 :         if( poSRS->IsGeographic() )
    1029               1 :             pszProjectionOGCBP = "GEOGRAPHIC";
    1030               0 :         else if( poSRS->IsLocal() )
    1031               0 :             pszProjectionOGCBP = "LOCAL CARTESIAN";
    1032                 :         else
    1033                 :         {
    1034               0 :             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported SRS type");
    1035               0 :             delete poProjectionDict;
    1036               0 :             return NULL;
    1037                 :         }
    1038                 :     }
    1039               6 :     else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) )
    1040                 :     {
    1041                 :         int bNorth;
    1042               6 :         int nZone = poSRS->GetUTMZone( &bNorth );
    1043                 : 
    1044               6 :         if( nZone != 0 )
    1045                 :         {
    1046               6 :             pszProjectionOGCBP = "UT";
    1047               6 :             poProjectionDict->Add("Hemisphere", (bNorth) ? "N" : "S");
    1048               6 :             poProjectionDict->Add("Zone", nZone);
    1049                 :         }
    1050                 :         else
    1051                 :         {
    1052               0 :             double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.L);
    1053               0 :             double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1054               0 :             double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1055               0 :             double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1056               0 :             double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1057                 : 
    1058                 :             /* OGC_BP supports representing numbers as strings for better precision */
    1059                 :             /* so use it */
    1060                 : 
    1061               0 :             pszProjectionOGCBP = "TC";
    1062               0 :             poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1063               0 :             poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1064               0 :             poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1065               0 :             poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1066               0 :             poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1067                 :         }
    1068                 :     }
    1069               0 :     else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) )
    1070                 :     {
    1071               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1072               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1073               0 :         double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1074               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1075               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1076                 : 
    1077               0 :         if( fabs(dfCenterLat) == 90.0 && dfCenterLong == 0.0 &&
    1078                 :             dfScale == 0.994 && dfFalseEasting == 200000.0 && dfFalseNorthing == 200000.0)
    1079                 :         {
    1080               0 :             pszProjectionOGCBP = "UP";
    1081               0 :             poProjectionDict->Add("Hemisphere", (dfCenterLat > 0) ? "N" : "S");
    1082                 :         }
    1083                 :         else
    1084                 :         {
    1085               0 :             pszProjectionOGCBP = "PG";
    1086               0 :             poProjectionDict->Add("LatitudeTrueScale", dfCenterLat, TRUE);
    1087               0 :             poProjectionDict->Add("LongitudeDownFromPole", dfCenterLong, TRUE);
    1088               0 :             poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1089               0 :             poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1090               0 :             poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1091                 :         }
    1092                 :     }
    1093                 : 
    1094               0 :     else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    1095                 :     {
    1096               0 :         double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0);
    1097               0 :         double dfStdP2 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0);
    1098               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1099               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1100               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1101               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1102                 : 
    1103               0 :         pszProjectionOGCBP = "LE";
    1104               0 :         poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE);
    1105               0 :         poProjectionDict->Add("StandardParallelTwo", dfStdP2, TRUE);
    1106               0 :         poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1107               0 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1108               0 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1109               0 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1110                 :     }
    1111                 : 
    1112               0 :     else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) )
    1113                 :     {
    1114               0 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1115               0 :         double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0);
    1116               0 :         double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0);
    1117               0 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1118               0 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1119                 : 
    1120               0 :         pszProjectionOGCBP = "MC";
    1121               0 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1122               0 :         poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE);
    1123               0 :         poProjectionDict->Add("ScaleFactor", dfScale, TRUE);
    1124               0 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1125               0 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1126                 :     }
    1127                 : 
    1128                 : #ifdef not_supported
    1129                 :     else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) )
    1130                 :     {
    1131                 :         double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0);
    1132                 :         double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0);
    1133                 :         double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0);
    1134                 :         double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0);
    1135                 : 
    1136                 :         pszProjectionOGCBP = "MC";
    1137                 :         poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE);
    1138                 :         poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE);
    1139                 :         poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE);
    1140                 :         poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE);
    1141                 :     }
    1142                 : #endif
    1143                 : 
    1144                 :     else
    1145                 :     {
    1146                 :         CPLError(CE_Warning, CPLE_NotSupported,
    1147               0 :                  "Unhandled projection type (%s) for now", pszProjection);
    1148                 :     }
    1149                 : 
    1150               7 :     poProjectionDict->Add("ProjectionType", pszProjectionOGCBP);
    1151                 : 
    1152               7 :     if( poSRS->IsProjected() )
    1153                 :     {
    1154               6 :         char* pszUnitName = NULL;
    1155               6 :         double dfLinearUnits = poSRS->GetLinearUnits(&pszUnitName);
    1156               6 :         if (dfLinearUnits == 1.0)
    1157               6 :             poProjectionDict->Add("Units", "M");
    1158               0 :         else if (dfLinearUnits == 0.3048)
    1159               0 :             poProjectionDict->Add("Units", "FT");
    1160                 :     }
    1161                 : 
    1162               7 :     return poProjectionDict;
    1163                 : }
    1164                 : 
    1165                 : /************************************************************************/
    1166                 : /*                           WriteSRS_OGC_BP()                          */
    1167                 : /************************************************************************/
    1168                 : 
    1169               7 : int GDALPDFWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS,
    1170                 :                                    double dfUserUnit,
    1171                 :                                    const char* pszNEATLINE,
    1172                 :                                    PDFMargins* psMargins)
    1173                 : {
    1174               7 :     int  nWidth = poSrcDS->GetRasterXSize();
    1175               7 :     int  nHeight = poSrcDS->GetRasterYSize();
    1176               7 :     const char* pszWKT = poSrcDS->GetProjectionRef();
    1177                 :     double adfGeoTransform[6];
    1178                 : 
    1179               7 :     int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None);
    1180               7 :     int nGCPCount = poSrcDS->GetGCPCount();
    1181               7 :     const GDAL_GCP* pasGCPList = (nGCPCount >= 4) ? poSrcDS->GetGCPs() : NULL;
    1182               7 :     if (pasGCPList != NULL)
    1183               2 :         pszWKT = poSrcDS->GetGCPProjection();
    1184                 : 
    1185               7 :     if( !bHasGT && pasGCPList == NULL )
    1186               0 :         return 0;
    1187                 : 
    1188               7 :     if( pszWKT == NULL || EQUAL(pszWKT, "") )
    1189               0 :         return 0;
    1190                 :     
    1191               7 :     if( !bHasGT )
    1192                 :     {
    1193               2 :         if (!GDALGCPsToGeoTransform( nGCPCount, pasGCPList,
    1194                 :                                      adfGeoTransform, FALSE ))
    1195                 :         {
    1196               1 :             CPLDebug("PDF", "Could not compute GT with exact match. Writing Registration then");
    1197                 :         }
    1198                 :         else
    1199                 :         {
    1200               1 :             bHasGT = TRUE;
    1201                 :         }
    1202                 :     }
    1203                 : 
    1204               7 :     OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
    1205               7 :     if( hSRS == NULL )
    1206               0 :         return 0;
    1207                 : 
    1208               7 :     const OGRSpatialReference* poSRS = (const OGRSpatialReference*)hSRS;
    1209               7 :     GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS);
    1210               7 :     if (poProjectionDict == NULL)
    1211                 :     {
    1212               0 :         OSRDestroySpatialReference(hSRS);
    1213               0 :         return 0;
    1214                 :     }
    1215                 : 
    1216               7 :     GDALPDFArrayRW* poNeatLineArray = NULL;
    1217                 : 
    1218               7 :     if (pszNEATLINE == NULL)
    1219               6 :         pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE");
    1220               7 :     if( bHasGT && pszNEATLINE != NULL && !EQUAL(pszNEATLINE, "NO") && pszNEATLINE[0] != '\0' )
    1221                 :     {
    1222               1 :         OGRGeometry* poGeom = NULL;
    1223               1 :         OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom );
    1224               1 :         if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
    1225                 :         {
    1226               1 :             OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing();
    1227                 :             double adfGeoTransformInv[6];
    1228               1 :             if( poLS != NULL && poLS->getNumPoints() >= 5 &&
    1229                 :                 GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) )
    1230                 :             {
    1231               1 :                 poNeatLineArray = new GDALPDFArrayRW();
    1232                 : 
    1233                 :                  // FIXME : ensure that they are in clockwise order ?
    1234               6 :                 for(int i=0;i<poLS->getNumPoints() - 1;i++)
    1235                 :                 {
    1236               5 :                     double X = poLS->getX(i);
    1237               5 :                     double Y = poLS->getY(i);
    1238               5 :                     double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2];
    1239               5 :                     double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5];
    1240               5 :                     poNeatLineArray->Add(x / dfUserUnit + psMargins->nLeft, TRUE);
    1241               5 :                     poNeatLineArray->Add((nHeight - y) / dfUserUnit + psMargins->nBottom, TRUE);
    1242                 :                 }
    1243                 :             }
    1244                 :         }
    1245               1 :         delete poGeom;
    1246                 :     }
    1247                 : 
    1248               7 :     if( pszNEATLINE != NULL && EQUAL(pszNEATLINE, "NO") )
    1249                 :     {
    1250                 :         // Do nothing
    1251                 :     }
    1252               9 :     else if( pasGCPList && poNeatLineArray == NULL)
    1253                 :     {
    1254               2 :         if (nGCPCount == 4)
    1255                 :         {
    1256               1 :             int iUL = 0, iUR = 0, iLR = 0, iLL = 0;
    1257                 :             GDALPDFFind4Corners(pasGCPList,
    1258               1 :                                 iUL,iUR, iLR, iLL);
    1259                 : 
    1260                 :             double adfNL[8];
    1261               1 :             adfNL[0] = pasGCPList[iUL].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1262               1 :             adfNL[1] = (nHeight - pasGCPList[iUL].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1263               1 :             adfNL[2] = pasGCPList[iLL].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1264               1 :             adfNL[3] = (nHeight - pasGCPList[iLL].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1265               1 :             adfNL[4] = pasGCPList[iLR].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1266               1 :             adfNL[5] = (nHeight - pasGCPList[iLR].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1267               1 :             adfNL[6] = pasGCPList[iUR].dfGCPPixel / dfUserUnit + psMargins->nLeft;
    1268               1 :             adfNL[7] = (nHeight - pasGCPList[iUR].dfGCPLine) / dfUserUnit + psMargins->nBottom;
    1269                 : 
    1270               1 :             poNeatLineArray = new GDALPDFArrayRW();
    1271               1 :             poNeatLineArray->Add(adfNL, 8, TRUE);
    1272                 :         }
    1273                 :         else
    1274                 :         {
    1275               1 :             poNeatLineArray = new GDALPDFArrayRW();
    1276                 : 
    1277                 :             // FIXME : ensure that they are in clockwise order ?
    1278                 :             int i;
    1279               6 :             for(i = 0; i < nGCPCount; i++)
    1280                 :             {
    1281               5 :                 poNeatLineArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE);
    1282               5 :                 poNeatLineArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE);
    1283                 :             }
    1284                 :         }
    1285                 :     }
    1286               5 :     else if (poNeatLineArray == NULL)
    1287                 :     {
    1288               4 :         poNeatLineArray = new GDALPDFArrayRW();
    1289                 : 
    1290               4 :         poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE);
    1291               4 :         poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE);
    1292                 : 
    1293               4 :         poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE);
    1294               4 :         poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE);
    1295                 : 
    1296               4 :         poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE);
    1297               4 :         poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE);
    1298                 : 
    1299               4 :         poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE);
    1300               4 :         poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE);
    1301                 :     }
    1302                 : 
    1303               7 :     int nLGIDictId = AllocNewObject();
    1304               7 :     StartObj(nLGIDictId);
    1305               7 :     GDALPDFDictionaryRW oLGIDict;
    1306                 :     oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict"))
    1307               7 :             .Add("Version", "2.1");
    1308               7 :     if( bHasGT )
    1309                 :     {
    1310                 :         double adfCTM[6];
    1311               6 :         double dfX1 = psMargins->nLeft;
    1312               6 :         double dfY2 = nHeight / dfUserUnit + psMargins->nBottom ;
    1313                 : 
    1314               6 :         adfCTM[0] = adfGeoTransform[1] * dfUserUnit;
    1315               6 :         adfCTM[1] = adfGeoTransform[2] * dfUserUnit;
    1316               6 :         adfCTM[2] = - adfGeoTransform[4] * dfUserUnit;
    1317               6 :         adfCTM[3] = - adfGeoTransform[5] * dfUserUnit;
    1318               6 :         adfCTM[4] = adfGeoTransform[0] - (adfCTM[0] * dfX1 + adfCTM[2] * dfY2);
    1319               6 :         adfCTM[5] = adfGeoTransform[3] - (adfCTM[1] * dfX1 + adfCTM[3] * dfY2);
    1320                 : 
    1321               6 :         oLGIDict.Add("CTM", &((new GDALPDFArrayRW())->Add(adfCTM, 6, TRUE)));
    1322                 :     }
    1323                 :     else
    1324                 :     {
    1325               1 :         GDALPDFArrayRW* poRegistrationArray = new GDALPDFArrayRW();
    1326                 :         int i;
    1327               6 :         for(i = 0; i < nGCPCount; i++)
    1328                 :         {
    1329               5 :             GDALPDFArrayRW* poPTArray = new GDALPDFArrayRW();
    1330               5 :             poPTArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE);
    1331               5 :             poPTArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE);
    1332               5 :             poPTArray->Add(pasGCPList[i].dfGCPX, TRUE);
    1333               5 :             poPTArray->Add(pasGCPList[i].dfGCPY, TRUE);
    1334               5 :             poRegistrationArray->Add(poPTArray);
    1335                 :         }
    1336               1 :         oLGIDict.Add("Registration", poRegistrationArray);
    1337                 :     }
    1338               7 :     if( poNeatLineArray )
    1339                 :     {
    1340               7 :         oLGIDict.Add("Neatline", poNeatLineArray);
    1341                 :     }
    1342                 : 
    1343               7 :     const OGR_SRSNode* poNode = poSRS->GetRoot();
    1344               7 :     if( poNode != NULL )
    1345               7 :         poNode = poNode->GetChild(0);
    1346               7 :     const char* pszDescription = NULL;
    1347               7 :     if( poNode != NULL )
    1348               7 :         pszDescription = poNode->GetValue();
    1349               7 :     if( pszDescription )
    1350                 :     {
    1351               7 :         oLGIDict.Add("Description", pszDescription);
    1352                 :     }
    1353                 : 
    1354               7 :     oLGIDict.Add("Projection", poProjectionDict);
    1355                 : 
    1356                 :     /* GDAL extension */
    1357               7 :     if( CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) )
    1358               3 :         poProjectionDict->Add("WKT", pszWKT);
    1359                 : 
    1360               7 :     VSIFPrintfL(fp, "%s\n", oLGIDict.Serialize().c_str());
    1361               7 :     EndObj();
    1362                 : 
    1363               7 :     OSRDestroySpatialReference(hSRS);
    1364                 :     
    1365               7 :     return nLGIDictId;
    1366                 : }
    1367                 : 
    1368                 : /************************************************************************/
    1369                 : /*                     GDALPDFGetValueFromDSOrOption()                  */
    1370                 : /************************************************************************/
    1371                 : 
    1372             406 : static const char* GDALPDFGetValueFromDSOrOption(GDALDataset* poSrcDS,
    1373                 :                                                  char** papszOptions,
    1374                 :                                                  const char* pszKey)
    1375                 : {
    1376             406 :     const char* pszValue = CSLFetchNameValue(papszOptions, pszKey);
    1377             406 :     if (pszValue == NULL)
    1378             400 :         pszValue = poSrcDS->GetMetadataItem(pszKey);
    1379             406 :     if (pszValue != NULL && pszValue[0] == '\0')
    1380               2 :         return NULL;
    1381                 :     else
    1382             404 :         return pszValue;
    1383                 : }
    1384                 : 
    1385                 : /************************************************************************/
    1386                 : /*                             SetInfo()                                */
    1387                 : /************************************************************************/
    1388                 : 
    1389              58 : int GDALPDFWriter::SetInfo(GDALDataset* poSrcDS,
    1390                 :                            char** papszOptions)
    1391                 : {
    1392              58 :     const char* pszAUTHOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "AUTHOR");
    1393              58 :     const char* pszPRODUCER = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "PRODUCER");
    1394              58 :     const char* pszCREATOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATOR");
    1395              58 :     const char* pszCREATION_DATE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATION_DATE");
    1396              58 :     const char* pszSUBJECT = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "SUBJECT");
    1397              58 :     const char* pszTITLE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "TITLE");
    1398              58 :     const char* pszKEYWORDS = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "KEYWORDS");
    1399                 : 
    1400              58 :     if (pszAUTHOR == NULL && pszPRODUCER == NULL && pszCREATOR == NULL && pszCREATION_DATE == NULL &&
    1401                 :         pszSUBJECT == NULL && pszTITLE == NULL && pszKEYWORDS == NULL)
    1402              52 :         return 0;
    1403                 : 
    1404               6 :     if (nInfoId == 0)
    1405               4 :         nInfoId = AllocNewObject();
    1406               6 :     StartObj(nInfoId, nInfoGen);
    1407               6 :     GDALPDFDictionaryRW oDict;
    1408               6 :     if (pszAUTHOR != NULL)
    1409               6 :         oDict.Add("Author", pszAUTHOR);
    1410               6 :     if (pszPRODUCER != NULL)
    1411               2 :         oDict.Add("Producer", pszPRODUCER);
    1412               6 :     if (pszCREATOR != NULL)
    1413               2 :         oDict.Add("Creator", pszCREATOR);
    1414               6 :     if (pszCREATION_DATE != NULL)
    1415               0 :         oDict.Add("CreationDate", pszCREATION_DATE);
    1416               6 :     if (pszSUBJECT != NULL)
    1417               2 :         oDict.Add("Subject", pszSUBJECT);
    1418               6 :     if (pszTITLE != NULL)
    1419               2 :         oDict.Add("Title", pszTITLE);
    1420               6 :     if (pszKEYWORDS != NULL)
    1421               2 :         oDict.Add("Keywords", pszKEYWORDS);
    1422               6 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1423               6 :     EndObj();
    1424                 : 
    1425               6 :     return nInfoId;
    1426                 : }
    1427                 : 
    1428                 : /************************************************************************/
    1429                 : /*                             SetXMP()                                 */
    1430                 : /************************************************************************/
    1431                 : 
    1432              57 : int  GDALPDFWriter::SetXMP(GDALDataset* poSrcDS,
    1433                 :                            const char* pszXMP)
    1434                 : {
    1435              57 :     if (pszXMP != NULL && EQUALN(pszXMP, "NO", 2))
    1436               1 :         return 0;
    1437              56 :     if (pszXMP != NULL && pszXMP[0] == '\0')
    1438               0 :         return 0;
    1439                 : 
    1440              56 :     char** papszXMP = poSrcDS->GetMetadata("xml:XMP");
    1441              56 :     if (pszXMP == NULL && papszXMP != NULL && papszXMP[0] != NULL)
    1442               5 :         pszXMP = papszXMP[0];
    1443                 : 
    1444              56 :     if (pszXMP == NULL)
    1445              51 :         return 0;
    1446                 : 
    1447               5 :     CPLXMLNode* psNode = CPLParseXMLString(pszXMP);
    1448               5 :     if (psNode == NULL)
    1449               0 :         return 0;
    1450               5 :     CPLDestroyXMLNode(psNode);
    1451                 : 
    1452               5 :     if(nXMPId == 0)
    1453               3 :         nXMPId = AllocNewObject();
    1454               5 :     StartObj(nXMPId, nXMPGen);
    1455               5 :     GDALPDFDictionaryRW oDict;
    1456                 :     oDict.Add("Type", GDALPDFObjectRW::CreateName("Metadata"))
    1457                 :          .Add("Subtype", GDALPDFObjectRW::CreateName("XML"))
    1458               5 :          .Add("Length", (int)strlen(pszXMP));
    1459               5 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1460               5 :     VSIFPrintfL(fp, "stream\n");
    1461               5 :     VSIFPrintfL(fp, "%s\n", pszXMP);
    1462               5 :     VSIFPrintfL(fp, "endstream\n");
    1463               5 :     EndObj();
    1464               5 :     return nXMPId;
    1465                 : }
    1466                 : 
    1467                 : /************************************************************************/
    1468                 : /*                              WriteOCG()                              */
    1469                 : /************************************************************************/
    1470                 : 
    1471             112 : int GDALPDFWriter::WriteOCG(const char* pszLayerName, int nParentId)
    1472                 : {
    1473             112 :     if (pszLayerName == NULL || pszLayerName[0] == '\0')
    1474             101 :         return 0;
    1475                 : 
    1476              11 :     int nOGCId = AllocNewObject();
    1477                 : 
    1478              11 :     GDALPDFOCGDesc oOCGDesc;
    1479              11 :     oOCGDesc.nId = nOGCId;
    1480              11 :     oOCGDesc.nParentId = nParentId;
    1481              11 :     oOCGDesc.osLayerName = pszLayerName;
    1482                 : 
    1483              11 :     asOCGs.push_back(oOCGDesc);
    1484                 : 
    1485              11 :     StartObj(nOGCId);
    1486                 :     {
    1487              11 :         GDALPDFDictionaryRW oDict;
    1488              11 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("OCG"));
    1489              11 :         oDict.Add("Name", pszLayerName);
    1490              11 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1491                 :     }
    1492              11 :     EndObj();
    1493                 : 
    1494              11 :     return nOGCId;
    1495                 : }
    1496                 : 
    1497                 : /************************************************************************/
    1498                 : /*                              StartPage()                             */
    1499                 : /************************************************************************/
    1500                 : 
    1501              53 : int GDALPDFWriter::StartPage(GDALDataset* poClippingDS,
    1502                 :                              double dfDPI,
    1503                 :                              const char* pszGEO_ENCODING,
    1504                 :                              const char* pszNEATLINE,
    1505                 :                              PDFMargins* psMargins,
    1506                 :                              PDFCompressMethod eStreamCompressMethod,
    1507                 :                              int bHasOGRData)
    1508                 : {
    1509              53 :     int  nWidth = poClippingDS->GetRasterXSize();
    1510              53 :     int  nHeight = poClippingDS->GetRasterYSize();
    1511              53 :     int  nBands = poClippingDS->GetRasterCount();
    1512                 : 
    1513              53 :     double dfUserUnit = dfDPI / 72.0;
    1514              53 :     double dfWidthInUserUnit = nWidth / dfUserUnit + psMargins->nLeft + psMargins->nRight;
    1515              53 :     double dfHeightInUserUnit = nHeight / dfUserUnit + psMargins->nBottom + psMargins->nTop;
    1516                 : 
    1517              53 :     int nPageId = AllocNewObject();
    1518              53 :     asPageId.push_back(nPageId);
    1519                 : 
    1520              53 :     int nContentId = AllocNewObject();
    1521              53 :     int nResourcesId = AllocNewObject();
    1522                 : 
    1523              53 :     int nAnnotsId = AllocNewObject();
    1524                 : 
    1525                 :     int bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") ||
    1526              53 :                     EQUAL(pszGEO_ENCODING, "BOTH");
    1527                 :     int bOGC_BP   = EQUAL(pszGEO_ENCODING, "OGC_BP") ||
    1528              53 :                     EQUAL(pszGEO_ENCODING, "BOTH");
    1529                 : 
    1530              53 :     int nViewportId = 0;
    1531              53 :     if( bISO32000 )
    1532              43 :         nViewportId = WriteSRS_ISO32000(poClippingDS, dfUserUnit, pszNEATLINE, psMargins, TRUE);
    1533                 : 
    1534              53 :     int nLGIDictId = 0;
    1535              53 :     if( bOGC_BP )
    1536               6 :         nLGIDictId = WriteSRS_OGC_BP(poClippingDS, dfUserUnit, pszNEATLINE, psMargins);
    1537                 : 
    1538              53 :     StartObj(nPageId);
    1539              53 :     GDALPDFDictionaryRW oDictPage;
    1540                 :     oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page"))
    1541                 :              .Add("Parent", nPageResourceId, 0)
    1542                 :              .Add("MediaBox", &((new GDALPDFArrayRW())
    1543                 :                                ->Add(0).Add(0).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit)))
    1544                 :              .Add("UserUnit", dfUserUnit)
    1545                 :              .Add("Contents", nContentId, 0)
    1546                 :              .Add("Resources", nResourcesId, 0)
    1547              53 :              .Add("Annots", nAnnotsId, 0);
    1548                 : 
    1549              53 :     if (nBands == 4)
    1550                 :     {
    1551                 :         oDictPage.Add("Group",
    1552                 :                       &((new GDALPDFDictionaryRW())
    1553                 :                         ->Add("Type", GDALPDFObjectRW::CreateName("Group"))
    1554                 :                          .Add("S", GDALPDFObjectRW::CreateName("Transparency"))
    1555               5 :                          .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB"))));
    1556                 :     }
    1557              53 :     if (nViewportId)
    1558                 :     {
    1559                 :         oDictPage.Add("VP", &((new GDALPDFArrayRW())
    1560              37 :                                ->Add(nViewportId, 0)));
    1561                 :     }
    1562              53 :     if (nLGIDictId)
    1563                 :     {
    1564               6 :         oDictPage.Add("LGIDict", nLGIDictId, 0);
    1565                 :     }
    1566                 : 
    1567              53 :     if (bHasOGRData)
    1568               3 :         oDictPage.Add("StructParents", 0);
    1569                 : 
    1570              53 :     VSIFPrintfL(fp, "%s\n", oDictPage.Serialize().c_str());
    1571              53 :     EndObj();
    1572                 : 
    1573              53 :     oPageContext.poClippingDS = poClippingDS;
    1574              53 :     oPageContext.nPageId = nPageId;
    1575              53 :     oPageContext.nContentId = nContentId;
    1576              53 :     oPageContext.nResourcesId = nResourcesId;
    1577              53 :     oPageContext.nAnnotsId = nAnnotsId;
    1578              53 :     oPageContext.dfDPI = dfDPI;
    1579              53 :     oPageContext.sMargins = *psMargins;
    1580              53 :     oPageContext.eStreamCompressMethod = eStreamCompressMethod;
    1581                 : 
    1582              53 :     return TRUE;
    1583                 : }
    1584                 : 
    1585                 : /************************************************************************/
    1586                 : /*                             WriteColorTable()                        */
    1587                 : /************************************************************************/
    1588                 : 
    1589             183 : int GDALPDFWriter::WriteColorTable(GDALDataset* poSrcDS)
    1590                 : {
    1591                 :     /* Does the source image has a color table ? */
    1592             183 :     GDALColorTable* poCT = NULL;
    1593             183 :     if (poSrcDS->GetRasterCount() > 0)
    1594             183 :         poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    1595             183 :     int nColorTableId = 0;
    1596             183 :     if (poCT != NULL && poCT->GetColorEntryCount() <= 256)
    1597                 :     {
    1598               1 :         int nColors = poCT->GetColorEntryCount();
    1599               1 :         nColorTableId = AllocNewObject();
    1600                 : 
    1601               1 :         int nLookupTableId = AllocNewObject();
    1602                 : 
    1603                 :         /* Index object */
    1604               1 :         StartObj(nColorTableId);
    1605                 :         {
    1606               1 :             GDALPDFArrayRW oArray;
    1607                 :             oArray.Add(GDALPDFObjectRW::CreateName("Indexed"))
    1608                 :                   .Add(&((new GDALPDFArrayRW())->Add(GDALPDFObjectRW::CreateName("DeviceRGB"))))
    1609                 :                   .Add(nColors-1)
    1610               1 :                   .Add(nLookupTableId, 0);
    1611               1 :             VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str());
    1612                 :         }
    1613               1 :         EndObj();
    1614                 : 
    1615                 :         /* Lookup table object */
    1616               1 :         StartObj(nLookupTableId);
    1617                 :         {
    1618               1 :             GDALPDFDictionaryRW oDict;
    1619               1 :             oDict.Add("Length", nColors * 3);
    1620               1 :             VSIFPrintfL(fp, "%s %% Lookup table\n", oDict.Serialize().c_str());
    1621                 :         }
    1622               1 :         VSIFPrintfL(fp, "stream\n");
    1623                 :         GByte pabyLookup[768];
    1624              17 :         for(int i=0;i<nColors;i++)
    1625                 :         {
    1626              16 :             const GDALColorEntry* poEntry = poCT->GetColorEntry(i);
    1627              16 :             pabyLookup[3 * i + 0] = (GByte)poEntry->c1;
    1628              16 :             pabyLookup[3 * i + 1] = (GByte)poEntry->c2;
    1629              16 :             pabyLookup[3 * i + 2] = (GByte)poEntry->c3;
    1630                 :         }
    1631               1 :         VSIFWriteL(pabyLookup, 3 * nColors, 1, fp);
    1632               1 :         VSIFPrintfL(fp, "\n");
    1633               1 :         VSIFPrintfL(fp, "endstream\n");
    1634               1 :         EndObj();
    1635                 :     }
    1636                 : 
    1637             183 :     return nColorTableId;
    1638                 : }
    1639                 : 
    1640                 : /************************************************************************/
    1641                 : /*                             WriteImagery()                           */
    1642                 : /************************************************************************/
    1643                 : 
    1644              50 : int GDALPDFWriter::WriteImagery(GDALDataset* poDS,
    1645                 :                                 const char* pszLayerName,
    1646                 :                                 PDFCompressMethod eCompressMethod,
    1647                 :                                 int nPredictor,
    1648                 :                                 int nJPEGQuality,
    1649                 :                                 const char* pszJPEG2000_DRIVER,
    1650                 :                                 int nBlockXSize, int nBlockYSize,
    1651                 :                                 GDALProgressFunc pfnProgress,
    1652                 :                                 void * pProgressData)
    1653                 : {
    1654              50 :     int  nWidth = poDS->GetRasterXSize();
    1655              50 :     int  nHeight = poDS->GetRasterYSize();
    1656              50 :     double dfUserUnit = oPageContext.dfDPI / 72.0;
    1657                 : 
    1658              50 :     GDALPDFRasterDesc oRasterDesc;
    1659                 : 
    1660              50 :     if( pfnProgress == NULL )
    1661               0 :         pfnProgress = GDALDummyProgress;
    1662                 : 
    1663              50 :     oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName);
    1664                 : 
    1665                 :     /* Does the source image has a color table ? */
    1666              50 :     int nColorTableId = WriteColorTable(poDS);
    1667                 : 
    1668              50 :     int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
    1669              50 :     int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
    1670              50 :     int nBlocks = nXBlocks * nYBlocks;
    1671                 :     int nBlockXOff, nBlockYOff;
    1672             112 :     for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
    1673                 :     {
    1674             188 :         for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++)
    1675                 :         {
    1676             126 :             int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize);
    1677             126 :             int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize);
    1678             126 :             int iImage = nBlockYOff * nXBlocks + nBlockXOff;
    1679                 : 
    1680                 :             void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks,
    1681                 :                                                           (iImage + 1) / (double)nBlocks,
    1682             126 :                                                           pfnProgress, pProgressData);
    1683             126 :             int nX = nBlockXOff * nBlockXSize;
    1684             126 :             int nY = nBlockYOff * nBlockYSize;
    1685                 : 
    1686                 :             int nImageId = WriteBlock(poDS,
    1687                 :                                     nX,
    1688                 :                                     nY,
    1689                 :                                     nReqWidth, nReqHeight,
    1690                 :                                     nColorTableId,
    1691                 :                                     eCompressMethod,
    1692                 :                                     nPredictor,
    1693                 :                                     nJPEGQuality,
    1694                 :                                     pszJPEG2000_DRIVER,
    1695                 :                                     GDALScaledProgress,
    1696             126 :                                     pScaledData);
    1697                 : 
    1698             126 :             GDALDestroyScaledProgress(pScaledData);
    1699                 : 
    1700             126 :             if (nImageId == 0)
    1701               0 :                 return FALSE;
    1702                 : 
    1703                 :             GDALPDFImageDesc oImageDesc;
    1704             126 :             oImageDesc.nImageId = nImageId;
    1705             126 :             oImageDesc.dfXOff = nX / dfUserUnit + oPageContext.sMargins.nLeft;
    1706             126 :             oImageDesc.dfYOff = (nHeight - nY - nReqHeight) / dfUserUnit + oPageContext.sMargins.nBottom;
    1707             126 :             oImageDesc.dfXSize = nReqWidth / dfUserUnit;
    1708             126 :             oImageDesc.dfYSize = nReqHeight / dfUserUnit;
    1709                 : 
    1710             126 :             oRasterDesc.asImageDesc.push_back(oImageDesc);
    1711                 :         }
    1712                 :     }
    1713                 : 
    1714              50 :     oPageContext.asRasterDesc.push_back(oRasterDesc);
    1715                 : 
    1716              50 :     return TRUE;
    1717                 : }
    1718                 : 
    1719                 : /************************************************************************/
    1720                 : /*                        WriteClippedImagery()                         */
    1721                 : /************************************************************************/
    1722                 : 
    1723               2 : int GDALPDFWriter::WriteClippedImagery(
    1724                 :                                 GDALDataset* poDS,
    1725                 :                                 const char* pszLayerName,
    1726                 :                                 PDFCompressMethod eCompressMethod,
    1727                 :                                 int nPredictor,
    1728                 :                                 int nJPEGQuality,
    1729                 :                                 const char* pszJPEG2000_DRIVER,
    1730                 :                                 int nBlockXSize, int nBlockYSize,
    1731                 :                                 GDALProgressFunc pfnProgress,
    1732                 :                                 void * pProgressData)
    1733                 : {
    1734               2 :     double dfUserUnit = oPageContext.dfDPI / 72.0;
    1735                 : 
    1736               2 :     GDALPDFRasterDesc oRasterDesc;
    1737                 : 
    1738                 :     /* Get clipping dataset bounding-box */
    1739                 :     double adfClippingGeoTransform[6];
    1740               2 :     GDALDataset* poClippingDS = oPageContext.poClippingDS;
    1741               2 :     poClippingDS->GetGeoTransform(adfClippingGeoTransform);
    1742               2 :     int  nClippingWidth = poClippingDS->GetRasterXSize();
    1743               2 :     int  nClippingHeight = poClippingDS->GetRasterYSize();
    1744               2 :     double dfClippingMinX = adfClippingGeoTransform[0];
    1745               2 :     double dfClippingMaxX = dfClippingMinX + nClippingWidth * adfClippingGeoTransform[1];
    1746               2 :     double dfClippingMaxY = adfClippingGeoTransform[3];
    1747               2 :     double dfClippingMinY = dfClippingMaxY + nClippingHeight * adfClippingGeoTransform[5];
    1748                 : 
    1749               2 :     if( dfClippingMaxY < dfClippingMinY )
    1750                 :     {
    1751               0 :         double dfTmp = dfClippingMinY;
    1752               0 :         dfClippingMinY = dfClippingMaxY;
    1753               0 :         dfClippingMaxY = dfTmp;
    1754                 :     }
    1755                 : 
    1756                 :     /* Get current dataset dataset bounding-box */
    1757                 :     double adfGeoTransform[6];
    1758               2 :     poDS->GetGeoTransform(adfGeoTransform);
    1759               2 :     int  nWidth = poDS->GetRasterXSize();
    1760               2 :     int  nHeight = poDS->GetRasterYSize();
    1761               2 :     double dfRasterMinX = adfGeoTransform[0];
    1762                 :     //double dfRasterMaxX = dfRasterMinX + nWidth * adfGeoTransform[1];
    1763               2 :     double dfRasterMaxY = adfGeoTransform[3];
    1764               2 :     double dfRasterMinY = dfRasterMaxY + nHeight * adfGeoTransform[5];
    1765                 : 
    1766               2 :     if( dfRasterMaxY < dfRasterMinY )
    1767                 :     {
    1768               0 :         double dfTmp = dfRasterMinY;
    1769               0 :         dfRasterMinY = dfRasterMaxY;
    1770               0 :         dfRasterMaxY = dfTmp;
    1771                 :     }
    1772                 : 
    1773               2 :     if( pfnProgress == NULL )
    1774               1 :         pfnProgress = GDALDummyProgress;
    1775                 : 
    1776               2 :     oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName);
    1777                 : 
    1778                 :     /* Does the source image has a color table ? */
    1779               2 :     int nColorTableId = WriteColorTable(poDS);
    1780                 : 
    1781               2 :     int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize;
    1782               2 :     int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize;
    1783               2 :     int nBlocks = nXBlocks * nYBlocks;
    1784                 :     int nBlockXOff, nBlockYOff;
    1785               4 :     for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++)
    1786                 :     {
    1787               4 :         for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++)
    1788                 :         {
    1789               2 :             int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize);
    1790               2 :             int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize);
    1791               2 :             int iImage = nBlockYOff * nXBlocks + nBlockXOff;
    1792                 : 
    1793                 :             void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks,
    1794                 :                                                           (iImage + 1) / (double)nBlocks,
    1795               2 :                                                           pfnProgress, pProgressData);
    1796                 : 
    1797               2 :             int nX = nBlockXOff * nBlockXSize;
    1798               2 :             int nY = nBlockYOff * nBlockYSize;
    1799                 : 
    1800                 :             /* Compute extent of block to write */
    1801               2 :             double dfBlockMinX = adfGeoTransform[0] + nX * adfGeoTransform[1];
    1802               2 :             double dfBlockMaxX = adfGeoTransform[0] + (nX + nReqWidth) * adfGeoTransform[1];
    1803               2 :             double dfBlockMinY = adfGeoTransform[3] + (nY + nReqHeight) * adfGeoTransform[5];
    1804               2 :             double dfBlockMaxY = adfGeoTransform[3] + nY * adfGeoTransform[5];
    1805                 : 
    1806               2 :             if( dfBlockMaxY < dfBlockMinY )
    1807                 :             {
    1808               0 :                 double dfTmp = dfBlockMinY;
    1809               0 :                 dfBlockMinY = dfBlockMaxY;
    1810               0 :                 dfBlockMaxY = dfTmp;
    1811                 :             }
    1812                 : 
    1813                 :             /* Clip the extent of the block with the extent of the main raster */
    1814               2 :             double dfIntersectMinX = MAX(dfBlockMinX, dfClippingMinX);
    1815               2 :             double dfIntersectMinY = MAX(dfBlockMinY, dfClippingMinY);
    1816               2 :             double dfIntersectMaxX = MIN(dfBlockMaxX, dfClippingMaxX);
    1817               2 :             double dfIntersectMaxY = MIN(dfBlockMaxY, dfClippingMaxY);
    1818                 : 
    1819               2 :             if( dfIntersectMinX < dfIntersectMaxX &&
    1820                 :                 dfIntersectMinY < dfIntersectMaxY )
    1821                 :             {
    1822                 :                 /* Re-compute (x,y,width,height) subwindow of current raster from */
    1823                 :                 /* the extent of the clipped block */
    1824               2 :                 nX = (int)((dfIntersectMinX - dfRasterMinX) / adfGeoTransform[1] + 0.5);
    1825               2 :                 if( adfGeoTransform[5] < 0 )
    1826               2 :                     nY = (int)((dfRasterMaxY - dfIntersectMaxY) / (-adfGeoTransform[5]) + 0.5);
    1827                 :                 else
    1828               0 :                     nY = (int)((dfIntersectMinY - dfRasterMinY) / adfGeoTransform[5] + 0.5);
    1829               2 :                 nReqWidth = (int)((dfIntersectMaxX - dfRasterMinX) / adfGeoTransform[1] + 0.5) - nX;
    1830               2 :                 if( adfGeoTransform[5] < 0 )
    1831               2 :                     nReqHeight = (int)((dfRasterMaxY - dfIntersectMinY) / (-adfGeoTransform[5]) + 0.5) - nY;
    1832                 :                 else
    1833               0 :                     nReqHeight = (int)((dfIntersectMaxY - dfRasterMinY) / adfGeoTransform[5] + 0.5) - nY;
    1834                 : 
    1835               2 :                 if( nReqWidth > 0 && nReqHeight > 0)
    1836                 :                 {
    1837                 :                     int nImageId = WriteBlock(poDS,
    1838                 :                                             nX,
    1839                 :                                             nY,
    1840                 :                                             nReqWidth, nReqHeight,
    1841                 :                                             nColorTableId,
    1842                 :                                             eCompressMethod,
    1843                 :                                             nPredictor,
    1844                 :                                             nJPEGQuality,
    1845                 :                                             pszJPEG2000_DRIVER,
    1846                 :                                             GDALScaledProgress,
    1847               2 :                                             pScaledData);
    1848                 : 
    1849               2 :                     if (nImageId == 0)
    1850                 :                     {
    1851               0 :                         GDALDestroyScaledProgress(pScaledData);
    1852               0 :                         return FALSE;
    1853                 :                     }
    1854                 : 
    1855                 :                     /* Compute the subwindow in image coordinates of the main raster corresponding */
    1856                 :                     /* to the extent of the clipped block */
    1857                 :                     double dfXInClippingUnits, dfYInClippingUnits, dfReqWidthInClippingUnits, dfReqHeightInClippingUnits;
    1858                 : 
    1859               2 :                     dfXInClippingUnits = (dfIntersectMinX - dfClippingMinX) / adfClippingGeoTransform[1];
    1860               2 :                     if( adfClippingGeoTransform[5] < 0 )
    1861               2 :                         dfYInClippingUnits = (dfClippingMaxY - dfIntersectMaxY) / (-adfClippingGeoTransform[5]);
    1862                 :                     else
    1863               0 :                         dfYInClippingUnits = (dfIntersectMinY - dfClippingMinY) / adfClippingGeoTransform[5];
    1864               2 :                     dfReqWidthInClippingUnits = (dfIntersectMaxX - dfClippingMinX) / adfClippingGeoTransform[1] - dfXInClippingUnits;
    1865               2 :                     if( adfClippingGeoTransform[5] < 0 )
    1866               2 :                         dfReqHeightInClippingUnits = (dfClippingMaxY - dfIntersectMinY) / (-adfClippingGeoTransform[5]) - dfYInClippingUnits;
    1867                 :                     else
    1868               0 :                         dfReqHeightInClippingUnits = (dfIntersectMaxY - dfClippingMinY) / adfClippingGeoTransform[5] - dfYInClippingUnits;
    1869                 : 
    1870                 :                     GDALPDFImageDesc oImageDesc;
    1871               2 :                     oImageDesc.nImageId = nImageId;
    1872               2 :                     oImageDesc.dfXOff = dfXInClippingUnits / dfUserUnit + oPageContext.sMargins.nLeft;
    1873               2 :                     oImageDesc.dfYOff = (nClippingHeight - dfYInClippingUnits - dfReqHeightInClippingUnits) / dfUserUnit + oPageContext.sMargins.nBottom;
    1874               2 :                     oImageDesc.dfXSize = dfReqWidthInClippingUnits / dfUserUnit;
    1875               2 :                     oImageDesc.dfYSize = dfReqHeightInClippingUnits / dfUserUnit;
    1876                 : 
    1877               2 :                     oRasterDesc.asImageDesc.push_back(oImageDesc);
    1878                 :                 }
    1879                 :             }
    1880                 : 
    1881               2 :             GDALDestroyScaledProgress(pScaledData);
    1882                 :         }
    1883                 :     }
    1884                 : 
    1885               2 :     oPageContext.asRasterDesc.push_back(oRasterDesc);
    1886                 : 
    1887               2 :     return TRUE;
    1888                 : }
    1889                 : 
    1890                 : #ifdef OGR_ENABLED
    1891                 : 
    1892                 : /************************************************************************/
    1893                 : /*                          WriteOGRDataSource()                        */
    1894                 : /************************************************************************/
    1895                 : 
    1896               2 : int GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource,
    1897                 :                                       const char* pszOGRDisplayField,
    1898                 :                                       const char* pszOGRDisplayLayerNames,
    1899                 :                                       const char* pszOGRLinkField,
    1900                 :                                       int bWriteOGRAttributes)
    1901                 : {
    1902               2 :     if (OGRGetDriverCount() == 0)
    1903               0 :         OGRRegisterAll();
    1904                 : 
    1905               2 :     OGRDataSourceH hDS = OGROpen(pszOGRDataSource, 0, NULL);
    1906               2 :     if (hDS == NULL)
    1907               0 :         return FALSE;
    1908                 : 
    1909               2 :     int iObj = 0;
    1910                 : 
    1911               2 :     int nLayers = OGR_DS_GetLayerCount(hDS);
    1912                 : 
    1913               2 :     char** papszLayerNames = CSLTokenizeString2(pszOGRDisplayLayerNames,",",0);
    1914                 : 
    1915               4 :     for(int iLayer = 0; iLayer < nLayers; iLayer ++)
    1916                 :     {
    1917               2 :         CPLString osLayerName;
    1918               2 :         if (CSLCount(papszLayerNames) < nLayers)
    1919               0 :             osLayerName = OGR_L_GetName(OGR_DS_GetLayer(hDS, iLayer));
    1920                 :         else
    1921               2 :             osLayerName = papszLayerNames[iLayer];
    1922                 : 
    1923                 :         WriteOGRLayer(hDS, iLayer,
    1924                 :                       pszOGRDisplayField,
    1925                 :                       pszOGRLinkField,
    1926                 :                       osLayerName,
    1927                 :                       bWriteOGRAttributes,
    1928               2 :                       iObj);
    1929                 :     }
    1930                 : 
    1931               2 :     OGRReleaseDataSource(hDS);
    1932                 : 
    1933               2 :     CSLDestroy(papszLayerNames);
    1934                 : 
    1935               2 :     return TRUE;
    1936                 : }
    1937                 : 
    1938                 : /************************************************************************/
    1939                 : /*                           StartOGRLayer()                            */
    1940                 : /************************************************************************/
    1941                 : 
    1942               4 : GDALPDFLayerDesc GDALPDFWriter::StartOGRLayer(CPLString osLayerName,
    1943                 :                                               int bWriteOGRAttributes)
    1944                 : {
    1945               4 :     GDALPDFLayerDesc osVectorDesc;
    1946               4 :     osVectorDesc.osLayerName = osLayerName;
    1947               4 :     osVectorDesc.bWriteOGRAttributes = bWriteOGRAttributes;
    1948               4 :     osVectorDesc.nOGCId = WriteOCG(osLayerName);
    1949               4 :     osVectorDesc.nFeatureLayerId = (bWriteOGRAttributes) ? AllocNewObject() : 0;
    1950               4 :     osVectorDesc.nOCGTextId = 0;
    1951                 : 
    1952               0 :     return osVectorDesc;
    1953                 : }
    1954                 : 
    1955                 : /************************************************************************/
    1956                 : /*                           EndOGRLayer()                              */
    1957                 : /************************************************************************/
    1958                 : 
    1959               4 : void GDALPDFWriter::EndOGRLayer(GDALPDFLayerDesc& osVectorDesc)
    1960                 : {
    1961               4 :     if (osVectorDesc.bWriteOGRAttributes)
    1962                 :     {
    1963               3 :         StartObj(osVectorDesc.nFeatureLayerId);
    1964                 : 
    1965               3 :         GDALPDFDictionaryRW oDict;
    1966                 :         oDict.Add("A", &(new GDALPDFDictionaryRW())->Add("O",
    1967               3 :                 GDALPDFObjectRW::CreateName("UserProperties")));
    1968                 : 
    1969               3 :         GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
    1970               3 :         oDict.Add("K", poArray);
    1971                 : 
    1972              26 :         for(int i = 0; i < (int)osVectorDesc.aUserPropertiesIds.size(); i++)
    1973                 :         {
    1974              23 :             poArray->Add(osVectorDesc.aUserPropertiesIds[i], 0);
    1975                 :         }
    1976                 : 
    1977               3 :         if (nStructTreeRootId == 0)
    1978               3 :             nStructTreeRootId = AllocNewObject();
    1979                 : 
    1980               3 :         oDict.Add("P", nStructTreeRootId, 0);
    1981               3 :         oDict.Add("S", GDALPDFObjectRW::CreateName("Feature"));
    1982               3 :         oDict.Add("T", osVectorDesc.osLayerName);
    1983                 : 
    1984               3 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    1985                 : 
    1986               3 :         EndObj();
    1987                 :     }
    1988                 : 
    1989               4 :     oPageContext.asVectorDesc.push_back(osVectorDesc);
    1990               4 : }
    1991                 : 
    1992                 : /************************************************************************/
    1993                 : /*                           WriteOGRLayer()                            */
    1994                 : /************************************************************************/
    1995                 : 
    1996               4 : int GDALPDFWriter::WriteOGRLayer(OGRDataSourceH hDS,
    1997                 :                                  int iLayer,
    1998                 :                                  const char* pszOGRDisplayField,
    1999                 :                                  const char* pszOGRLinkField,
    2000                 :                                  CPLString osLayerName,
    2001                 :                                  int bWriteOGRAttributes,
    2002                 :                                  int& iObj)
    2003                 : {
    2004               4 :     GDALDataset* poClippingDS = oPageContext.poClippingDS;
    2005                 :     double adfGeoTransform[6];
    2006               4 :     if (poClippingDS->GetGeoTransform(adfGeoTransform) != CE_None)
    2007               0 :         return FALSE;
    2008                 : 
    2009                 :     GDALPDFLayerDesc osVectorDesc = StartOGRLayer(osLayerName,
    2010               4 :                                                   bWriteOGRAttributes);
    2011               4 :     OGRLayerH hLyr = OGR_DS_GetLayer(hDS, iLayer);
    2012                 : 
    2013               4 :     const char* pszWKT = poClippingDS->GetProjectionRef();
    2014               4 :     OGRSpatialReferenceH hGDAL_SRS = NULL;
    2015               4 :     if( pszWKT && pszWKT[0] != '\0' )
    2016               4 :         hGDAL_SRS = OSRNewSpatialReference(pszWKT);
    2017               4 :     OGRSpatialReferenceH hOGR_SRS = OGR_L_GetSpatialRef(hLyr);
    2018               4 :     OGRCoordinateTransformationH hCT = NULL;
    2019                 : 
    2020               4 :     if( hGDAL_SRS == NULL && hOGR_SRS != NULL )
    2021                 :     {
    2022                 :         CPLError(CE_Warning, CPLE_AppDefined,
    2023               0 :                  "Vector layer has a SRS set, but Raster layer has no SRS set. Assuming they are the same.");
    2024                 :     }
    2025               4 :     else if( hGDAL_SRS != NULL && hOGR_SRS == NULL )
    2026                 :     {
    2027                 :         CPLError(CE_Warning, CPLE_AppDefined,
    2028               0 :                  "Vector layer has no SRS set, but Raster layer has a SRS set. Assuming they are the same.");
    2029                 :     }
    2030               4 :     else if( hGDAL_SRS != NULL && hOGR_SRS != NULL )
    2031                 :     {
    2032               4 :         if (!OSRIsSame(hGDAL_SRS, hOGR_SRS))
    2033                 :         {
    2034               1 :             hCT = OCTNewCoordinateTransformation( hOGR_SRS, hGDAL_SRS );
    2035               1 :             if( hCT == NULL )
    2036                 :             {
    2037                 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2038               0 :                          "Cannot compute coordinate transformation from vector SRS to raster SRS");
    2039                 :             }
    2040                 :         }
    2041                 :     }
    2042                 : 
    2043               4 :     if( hCT == NULL )
    2044                 :     {
    2045               3 :         double dfXMin = adfGeoTransform[0];
    2046               3 :         double dfYMin = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5];
    2047               3 :         double dfXMax = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1];
    2048               3 :         double dfYMax = adfGeoTransform[3];
    2049               3 :         OGR_L_SetSpatialFilterRect(hLyr, dfXMin, dfYMin, dfXMax, dfYMax);
    2050                 :     }
    2051                 : 
    2052                 :     OGRFeatureH hFeat;
    2053               4 :     int iObjLayer = 0;
    2054                 : 
    2055              47 :     while( (hFeat = OGR_L_GetNextFeature(hLyr)) != NULL)
    2056                 :     {
    2057                 :         WriteOGRFeature(osVectorDesc,
    2058                 :                         hFeat,
    2059                 :                         hCT,
    2060                 :                         pszOGRDisplayField,
    2061                 :                         pszOGRLinkField,
    2062                 :                         bWriteOGRAttributes,
    2063                 :                         iObj,
    2064              39 :                         iObjLayer);
    2065                 : 
    2066              39 :         OGR_F_Destroy(hFeat);
    2067                 :     }
    2068                 : 
    2069               4 :     EndOGRLayer(osVectorDesc);
    2070                 : 
    2071               4 :     if( hCT != NULL )
    2072               1 :         OCTDestroyCoordinateTransformation(hCT);
    2073               4 :     if( hGDAL_SRS != NULL )
    2074               4 :         OSRDestroySpatialReference(hGDAL_SRS);
    2075                 : 
    2076               4 :     return TRUE;
    2077                 : }
    2078                 : 
    2079                 : /************************************************************************/
    2080                 : /*                             DrawGeometry()                           */
    2081                 : /************************************************************************/
    2082                 : 
    2083              19 : static void DrawGeometry(VSILFILE* fp, OGRGeometryH hGeom, double adfMatrix[4], int bPaint = TRUE)
    2084                 : {
    2085              19 :     switch(wkbFlatten(OGR_G_GetGeometryType(hGeom)))
    2086                 :     {
    2087                 :         case wkbLineString:
    2088                 :         {
    2089              12 :             int nPoints = OGR_G_GetPointCount(hGeom);
    2090              63 :             for(int i=0;i<nPoints;i++)
    2091                 :             {
    2092              51 :                 double dfX = OGR_G_GetX(hGeom, i) * adfMatrix[1] + adfMatrix[0];
    2093              51 :                 double dfY = OGR_G_GetY(hGeom, i) * adfMatrix[3] + adfMatrix[2];
    2094              51 :                 VSIFPrintfL(fp, "%f %f %c\n", dfX, dfY, (i == 0) ? 'm' : 'l');
    2095                 :             }
    2096              12 :             if (bPaint)
    2097               3 :                 VSIFPrintfL(fp, "S\n");
    2098              12 :             break;
    2099                 :         }
    2100                 : 
    2101                 :         case wkbPolygon:
    2102                 :         {
    2103               6 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    2104              15 :             for(int i=0;i<nParts;i++)
    2105                 :             {
    2106               9 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    2107               9 :                 VSIFPrintfL(fp, "h\n");
    2108                 :             }
    2109               6 :             if (bPaint)
    2110               4 :                 VSIFPrintfL(fp, "b*\n");
    2111               6 :             break;
    2112                 :         }
    2113                 : 
    2114                 :         case wkbMultiLineString:
    2115                 :         {
    2116               0 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    2117               0 :             for(int i=0;i<nParts;i++)
    2118                 :             {
    2119               0 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    2120                 :             }
    2121               0 :             if (bPaint)
    2122               0 :                 VSIFPrintfL(fp, "S\n");
    2123               0 :             break;
    2124                 :         }
    2125                 : 
    2126                 :         case wkbMultiPolygon:
    2127                 :         {
    2128               1 :             int nParts = OGR_G_GetGeometryCount(hGeom);
    2129               3 :             for(int i=0;i<nParts;i++)
    2130                 :             {
    2131               2 :                 DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE);
    2132                 :             }
    2133               1 :             if (bPaint)
    2134               1 :                 VSIFPrintfL(fp, "b*\n");
    2135                 :             break;
    2136                 :         }
    2137                 : 
    2138                 :         default:
    2139                 :             break;
    2140                 :     }
    2141              19 : }
    2142                 : 
    2143                 : /************************************************************************/
    2144                 : /*                          WriteOGRFeature()                           */
    2145                 : /************************************************************************/
    2146                 : 
    2147              39 : int GDALPDFWriter::WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc,
    2148                 :                                    OGRFeatureH hFeat,
    2149                 :                                    OGRCoordinateTransformationH hCT,
    2150                 :                                    const char* pszOGRDisplayField,
    2151                 :                                    const char* pszOGRLinkField,
    2152                 :                                    int bWriteOGRAttributes,
    2153                 :                                    int& iObj,
    2154                 :                                    int& iObjLayer)
    2155                 : {
    2156              39 :     GDALDataset* poClippingDS = oPageContext.poClippingDS;
    2157              39 :     int  nHeight = poClippingDS->GetRasterYSize();
    2158              39 :     double dfUserUnit = oPageContext.dfDPI / 72.0;
    2159                 :     double adfGeoTransform[6];
    2160              39 :     poClippingDS->GetGeoTransform(adfGeoTransform);
    2161                 : 
    2162                 :     double adfMatrix[4];
    2163              39 :     adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft;
    2164              39 :     adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit);
    2165              39 :     adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom;
    2166              39 :     adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit);
    2167                 : 
    2168              39 :     OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
    2169              39 :     if (hGeom == NULL)
    2170                 :     {
    2171               0 :         return TRUE;
    2172                 :     }
    2173                 : 
    2174              39 :     OGREnvelope sEnvelope;
    2175                 : 
    2176              39 :     if( hCT != NULL )
    2177                 :     {
    2178                 :         /* Reproject */
    2179               6 :         if( OGR_G_Transform(hGeom, hCT) != OGRERR_NONE )
    2180                 :         {
    2181               0 :             return TRUE;
    2182                 :         }
    2183                 : 
    2184               6 :         OGREnvelope sRasterEnvelope;
    2185               6 :         sRasterEnvelope.MinX = adfGeoTransform[0];
    2186               6 :         sRasterEnvelope.MinY = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5];
    2187               6 :         sRasterEnvelope.MaxX = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1];
    2188               6 :         sRasterEnvelope.MaxY = adfGeoTransform[3];
    2189                 : 
    2190                 :         /* Check that the reprojected geometry interescts the raster envelope */
    2191               6 :         OGR_G_GetEnvelope(hGeom, &sEnvelope);
    2192               6 :         if( !(sRasterEnvelope.Intersects(sEnvelope)) )
    2193                 :         {
    2194               1 :             return TRUE;
    2195                 :         }
    2196                 :     }
    2197                 :     else
    2198                 :     {
    2199              33 :         OGR_G_GetEnvelope(hGeom, &sEnvelope);
    2200                 :     }
    2201                 : 
    2202                 :     /* -------------------------------------------------------------- */
    2203                 :     /*  Get style                                                     */
    2204                 :     /* -------------------------------------------------------------- */
    2205              38 :     int nPenR = 0, nPenG = 0, nPenB = 0, nPenA = 255;
    2206              38 :     int nBrushR = 127, nBrushG = 127, nBrushB = 127, nBrushA = 127;
    2207              38 :     int nTextR = 0, nTextG = 0, nTextB = 0, nTextA = 255;
    2208              38 :     int bSymbolColorDefined = FALSE;
    2209              38 :     int nSymbolR = 0, nSymbolG = 0, nSymbolB = 0, nSymbolA = 255;
    2210              38 :     double dfTextSize = 12, dfTextAngle = 0, dfTextDx = 0, dfTextDy = 0;
    2211              38 :     double dfPenWidth = 1;
    2212              38 :     double dfSymbolSize = 5;
    2213              38 :     CPLString osDashArray;
    2214              38 :     CPLString osLabelText;
    2215              38 :     CPLString osSymbolId;
    2216              38 :     int nImageSymbolId = 0, nImageWidth = 0, nImageHeight = 0;
    2217                 : 
    2218              38 :     OGRStyleMgrH hSM = OGR_SM_Create(NULL);
    2219              38 :     OGR_SM_InitFromFeature(hSM, hFeat);
    2220              38 :     int nCount = OGR_SM_GetPartCount(hSM, NULL);
    2221              67 :     for(int iPart = 0; iPart < nCount; iPart++)
    2222                 :     {
    2223              29 :         OGRStyleToolH hTool = OGR_SM_GetPart(hSM, iPart, NULL);
    2224              29 :         if (hTool)
    2225                 :         {
    2226              28 :             if (OGR_ST_GetType(hTool) == OGRSTCPen)
    2227                 :             {
    2228               2 :                 int bIsNull = TRUE;
    2229               2 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTPenColor, &bIsNull);
    2230               2 :                 if (pszColor && !bIsNull)
    2231                 :                 {
    2232               2 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    2233               2 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    2234               2 :                     if (nVals >= 3)
    2235                 :                     {
    2236               2 :                         nPenR = nRed;
    2237               2 :                         nPenG = nGreen;
    2238               2 :                         nPenB = nBlue;
    2239               2 :                         if (nVals == 4)
    2240               0 :                             nPenA = nAlpha;
    2241                 :                     }
    2242                 :                 }
    2243                 : 
    2244               2 :                 const char* pszDash = OGR_ST_GetParamStr(hTool, OGRSTPenPattern, &bIsNull);
    2245               2 :                 if (pszDash && !bIsNull)
    2246                 :                 {
    2247               1 :                     char** papszTokens = CSLTokenizeString2(pszDash, " ", 0);
    2248               1 :                     int nTokens = CSLCount(papszTokens);
    2249               1 :                     if ((nTokens % 2) == 0)
    2250                 :                     {
    2251               3 :                         for(int i=0;i<nTokens;i++)
    2252                 :                         {
    2253               2 :                             osDashArray += CPLSPrintf("%d ", atoi(papszTokens[i]));
    2254                 :                         }
    2255                 :                     }
    2256               1 :                     CSLDestroy(papszTokens);
    2257                 :                 }
    2258                 : 
    2259                 :                 //OGRSTUnitId eUnit = OGR_ST_GetUnit(hTool);
    2260               2 :                 double dfWidth = OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bIsNull);
    2261               2 :                 if (!bIsNull)
    2262               2 :                     dfPenWidth = dfWidth;
    2263                 :             }
    2264              26 :             else if (OGR_ST_GetType(hTool) == OGRSTCBrush)
    2265                 :             {
    2266                 :                 int bIsNull;
    2267               1 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTBrushFColor, &bIsNull);
    2268               1 :                 if (pszColor)
    2269                 :                 {
    2270               1 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    2271               1 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    2272               1 :                     if (nVals >= 3)
    2273                 :                     {
    2274               1 :                         nBrushR = nRed;
    2275               1 :                         nBrushG = nGreen;
    2276               1 :                         nBrushB = nBlue;
    2277               1 :                         if (nVals == 4)
    2278               0 :                             nBrushA = nAlpha;
    2279                 :                     }
    2280                 :                 }
    2281                 :             }
    2282              25 :             else if (OGR_ST_GetType(hTool) == OGRSTCLabel)
    2283                 :             {
    2284                 :                 int bIsNull;
    2285               3 :                 const char* pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelTextString, &bIsNull);
    2286               3 :                 if (pszStr)
    2287                 :                 {
    2288               3 :                     osLabelText = pszStr;
    2289                 : 
    2290                 :                     /* If the text is of the form {stuff}, then it means we want to fetch */
    2291                 :                     /* the value of the field "stuff" in the feature */
    2292               3 :                     if( osLabelText.size() && osLabelText[0] == '{' &&
    2293                 :                         osLabelText[osLabelText.size() - 1] == '}' )
    2294                 :                     {
    2295               2 :                         osLabelText = pszStr + 1;
    2296               2 :                         osLabelText.resize(osLabelText.size() - 1);
    2297                 : 
    2298               2 :                         int nIdxField = OGR_F_GetFieldIndex(hFeat, osLabelText);
    2299               2 :                         if( nIdxField >= 0 )
    2300               2 :                             osLabelText = OGR_F_GetFieldAsString(hFeat, nIdxField);
    2301                 :                         else
    2302               0 :                             osLabelText = "";
    2303                 :                     }
    2304                 :                 }
    2305                 : 
    2306               3 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTLabelFColor, &bIsNull);
    2307               3 :                 if (pszColor && !bIsNull)
    2308                 :                 {
    2309               1 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    2310               1 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    2311               1 :                     if (nVals >= 3)
    2312                 :                     {
    2313               1 :                         nTextR = nRed;
    2314               1 :                         nTextG = nGreen;
    2315               1 :                         nTextB = nBlue;
    2316               1 :                         if (nVals == 4)
    2317               1 :                             nTextA = nAlpha;
    2318                 :                     }
    2319                 :                 }
    2320                 : 
    2321               3 :                 double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelSize, &bIsNull);
    2322               3 :                 if (!bIsNull)
    2323                 :                 {
    2324               1 :                     dfTextSize = dfVal;
    2325                 :                 }
    2326                 : 
    2327               3 :                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelAngle, &bIsNull);
    2328               3 :                 if (!bIsNull)
    2329                 :                 {
    2330               1 :                     dfTextAngle = dfVal;
    2331                 :                 }
    2332                 : 
    2333               3 :                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDx, &bIsNull);
    2334               3 :                 if (!bIsNull)
    2335                 :                 {
    2336               2 :                     dfTextDx = dfVal;
    2337                 :                 }
    2338                 : 
    2339               3 :                 dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDy, &bIsNull);
    2340               3 :                 if (!bIsNull)
    2341                 :                 {
    2342               2 :                     dfTextDy = dfVal;
    2343                 :                 }
    2344                 : 
    2345                 :             }
    2346              22 :             else if (OGR_ST_GetType(hTool) == OGRSTCSymbol)
    2347                 :             {
    2348                 :                 int bIsNull;
    2349              22 :                 const char* pszSymbolId = OGR_ST_GetParamStr(hTool, OGRSTSymbolId, &bIsNull);
    2350              22 :                 if (pszSymbolId && !bIsNull)
    2351                 :                 {
    2352              22 :                     osSymbolId = pszSymbolId;
    2353                 : 
    2354              22 :                     if (strstr(pszSymbolId, "ogr-sym-") == NULL)
    2355                 :                     {
    2356               2 :                         if (oMapSymbolFilenameToDesc.find(osSymbolId) == oMapSymbolFilenameToDesc.end())
    2357                 :                         {
    2358               2 :                             CPLPushErrorHandler(CPLQuietErrorHandler);
    2359               2 :                             GDALDatasetH hImageDS = GDALOpen(osSymbolId, GA_ReadOnly);
    2360               2 :                             CPLPopErrorHandler();
    2361               2 :                             if (hImageDS != NULL)
    2362                 :                             {
    2363               2 :                                 nImageWidth = GDALGetRasterXSize(hImageDS);
    2364               2 :                                 nImageHeight = GDALGetRasterYSize(hImageDS);
    2365                 : 
    2366                 :                                 nImageSymbolId = WriteBlock((GDALDataset*) hImageDS,
    2367                 :                                                         0, 0,
    2368                 :                                                         nImageWidth,
    2369                 :                                                         nImageHeight,
    2370                 :                                                         0,
    2371                 :                                                         COMPRESS_DEFAULT,
    2372                 :                                                         0,
    2373                 :                                                         -1,
    2374                 :                                                         NULL,
    2375                 :                                                         NULL,
    2376               2 :                                                         NULL);
    2377               2 :                                 GDALClose(hImageDS);
    2378                 :                             }
    2379                 : 
    2380                 :                             GDALPDFImageDesc oDesc;
    2381               2 :                             oDesc.nImageId = nImageSymbolId;
    2382               2 :                             oDesc.dfXOff = 0;
    2383               2 :                             oDesc.dfYOff = 0;
    2384               2 :                             oDesc.dfXSize = nImageWidth;
    2385               2 :                             oDesc.dfYSize = nImageHeight;
    2386               2 :                             oMapSymbolFilenameToDesc[osSymbolId] = oDesc;
    2387                 :                         }
    2388                 :                         else
    2389                 :                         {
    2390               0 :                             GDALPDFImageDesc& oDesc = oMapSymbolFilenameToDesc[osSymbolId];
    2391               0 :                             nImageSymbolId = oDesc.nImageId;
    2392               0 :                             nImageWidth = (int)oDesc.dfXSize;
    2393               0 :                             nImageHeight = (int)oDesc.dfYSize;
    2394                 :                         }
    2395                 :                     }
    2396                 :                 }
    2397                 : 
    2398              22 :                 double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTSymbolSize, &bIsNull);
    2399              22 :                 if (!bIsNull)
    2400                 :                 {
    2401              20 :                     dfSymbolSize = dfVal;
    2402                 :                 }
    2403                 : 
    2404              22 :                 const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTSymbolColor, &bIsNull);
    2405              22 :                 if (pszColor && !bIsNull)
    2406                 :                 {
    2407              22 :                     int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255;
    2408              22 :                     int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha);
    2409              22 :                     if (nVals >= 3)
    2410                 :                     {
    2411              22 :                         bSymbolColorDefined = TRUE;
    2412              22 :                         nSymbolR = nRed;
    2413              22 :                         nSymbolG = nGreen;
    2414              22 :                         nSymbolB = nBlue;
    2415              22 :                         if (nVals == 4)
    2416               0 :                             nSymbolA = nAlpha;
    2417                 :                     }
    2418                 :                 }
    2419                 :             }
    2420                 : 
    2421              28 :             OGR_ST_Destroy(hTool);
    2422                 :         }
    2423                 :     }
    2424              38 :     OGR_SM_Destroy(hSM);
    2425                 : 
    2426              38 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && bSymbolColorDefined)
    2427                 :     {
    2428              22 :         nPenR = nSymbolR;
    2429              22 :         nPenG = nSymbolG;
    2430              22 :         nPenB = nSymbolB;
    2431              22 :         nPenA = nSymbolA;
    2432              22 :         nBrushR = nSymbolR;
    2433              22 :         nBrushG = nSymbolG;
    2434              22 :         nBrushB = nSymbolB;
    2435              22 :         nBrushA = nSymbolA;
    2436                 :     }
    2437                 : 
    2438              38 :     double dfRadius = dfSymbolSize * dfUserUnit;
    2439                 : 
    2440                 :     /* -------------------------------------------------------------- */
    2441                 :     /*  Write object dictionary                                       */
    2442                 :     /* -------------------------------------------------------------- */
    2443              38 :     int nObjectId = AllocNewObject();
    2444              38 :     int nObjectLengthId = AllocNewObject();
    2445                 : 
    2446              38 :     osVectorDesc.aIds.push_back(nObjectId);
    2447                 : 
    2448                 :     int bboxXMin, bboxYMin, bboxXMax, bboxYMax;
    2449              38 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && nImageSymbolId != 0)
    2450                 :     {
    2451               2 :         bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - nImageWidth / 2);
    2452               2 :         bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - nImageHeight / 2);
    2453               2 :         bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + nImageWidth / 2);
    2454               2 :         bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + nImageHeight / 2);
    2455                 :     }
    2456                 :     else
    2457                 :     {
    2458              36 :         double dfMargin = dfPenWidth;
    2459              36 :         if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint )
    2460                 :         {
    2461              28 :             if (osSymbolId == "ogr-sym-6" ||
    2462                 :                 osSymbolId == "ogr-sym-7")
    2463                 :             {
    2464               4 :                 const double dfSqrt3 = 1.73205080757;
    2465               4 :                 dfMargin += dfRadius * 2 * dfSqrt3 / 3;
    2466                 :             }
    2467                 :             else
    2468              24 :                 dfMargin += dfRadius;
    2469                 :         }
    2470              36 :         bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin);
    2471              36 :         bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin);
    2472              36 :         bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin);
    2473              36 :         bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin);
    2474                 :     }
    2475                 : 
    2476              38 :     int iField = -1;
    2477              38 :     const char* pszLinkVal = NULL;
    2478              38 :     if (pszOGRLinkField != NULL &&
    2479                 :         (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRLinkField)) >= 0 &&
    2480                 :         OGR_F_IsFieldSet(hFeat, iField) &&
    2481                 :         strcmp((pszLinkVal = OGR_F_GetFieldAsString(hFeat, iField)), "") != 0)
    2482                 :     {
    2483               4 :         int nAnnotId = AllocNewObject();
    2484               4 :         oPageContext.anAnnotationsId.push_back(nAnnotId);
    2485               4 :         StartObj(nAnnotId);
    2486                 :         {
    2487               4 :             GDALPDFDictionaryRW oDict;
    2488               4 :             oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot"));
    2489               4 :             oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link"));
    2490               4 :             oDict.Add("Rect", &(new GDALPDFArrayRW())->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax).Add(bboxYMax));
    2491                 :             oDict.Add("A", &(new GDALPDFDictionaryRW())->
    2492                 :                 Add("S", GDALPDFObjectRW::CreateName("URI")).
    2493               4 :                 Add("URI", pszLinkVal));
    2494                 :             oDict.Add("BS", &(new GDALPDFDictionaryRW())->
    2495                 :                 Add("Type", GDALPDFObjectRW::CreateName("Border")).
    2496                 :                 Add("S", GDALPDFObjectRW::CreateName("S")).
    2497               4 :                 Add("W", 0));
    2498               4 :             oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0));
    2499               4 :             oDict.Add("H", GDALPDFObjectRW::CreateName("I"));
    2500                 : 
    2501               4 :             if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon && 
    2502                 :                 OGR_G_GetGeometryCount(hGeom) == 1 )
    2503                 :             {
    2504               2 :                 OGRGeometryH hSubGeom = OGR_G_GetGeometryRef(hGeom, 0);
    2505               2 :                 int nPoints = OGR_G_GetPointCount(hSubGeom);
    2506               2 :                 if( nPoints == 4 || nPoints == 5 )
    2507                 :                 {
    2508               2 :                     std::vector<double> adfX, adfY;
    2509              12 :                     for(int i=0;i<nPoints;i++)
    2510                 :                     {
    2511              10 :                         double dfX = OGR_G_GetX(hSubGeom, i) * adfMatrix[1] + adfMatrix[0];
    2512              10 :                         double dfY = OGR_G_GetY(hSubGeom, i) * adfMatrix[3] + adfMatrix[2];
    2513              10 :                         adfX.push_back(dfX);
    2514              10 :                         adfY.push_back(dfY);
    2515                 :                     }
    2516               2 :                     if( nPoints == 4 )
    2517                 :                     {
    2518                 :                         oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
    2519                 :                             Add(adfX[0]).Add(adfY[0]).
    2520                 :                             Add(adfX[1]).Add(adfY[1]).
    2521                 :                             Add(adfX[2]).Add(adfY[2]).
    2522               0 :                             Add(adfX[0]).Add(adfY[0]));
    2523                 :                     }
    2524               2 :                     else if( nPoints == 5 )
    2525                 :                     {
    2526                 :                         oDict.Add("QuadPoints", &(new GDALPDFArrayRW())->
    2527                 :                             Add(adfX[0]).Add(adfY[0]).
    2528                 :                             Add(adfX[1]).Add(adfY[1]).
    2529                 :                             Add(adfX[2]).Add(adfY[2]).
    2530               2 :                             Add(adfX[3]).Add(adfY[3]));
    2531               2 :                     }
    2532                 :                 }
    2533                 :             }
    2534                 : 
    2535               4 :             VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2536                 :         }
    2537               4 :         EndObj();
    2538                 :     }
    2539                 : 
    2540              38 :     StartObj(nObjectId);
    2541                 :     {
    2542              38 :         GDALPDFDictionaryRW oDict;
    2543              38 :         GDALPDFArrayRW* poBBOX = new GDALPDFArrayRW();
    2544              76 :         poBBOX->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax). Add(bboxYMax);
    2545                 :         oDict.Add("Length", nObjectLengthId, 0)
    2546                 :             .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    2547                 :             .Add("BBox", poBBOX)
    2548              38 :             .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
    2549              38 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2550                 :         {
    2551              38 :             oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2552                 :         }
    2553                 : 
    2554              38 :         GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
    2555              76 :         poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
    2556              38 :         if (nPenA != 255)
    2557               0 :             poGS1->Add("CA", (nPenA == 127 || nPenA == 128) ? 0.5 : nPenA / 255.0);
    2558              38 :         if (nBrushA != 255)
    2559              16 :             poGS1->Add("ca", (nBrushA == 127 || nBrushA == 128) ? 0.5 : nBrushA / 255.0 );
    2560                 : 
    2561              38 :         GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
    2562              38 :         poExtGState->Add("GS1", poGS1);
    2563                 : 
    2564              38 :         GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
    2565              38 :         poResources->Add("ExtGState", poExtGState);
    2566                 : 
    2567              38 :         if( nImageSymbolId != 0 )
    2568                 :         {
    2569               2 :             GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW();
    2570               2 :             poResources->Add("XObject", poDictXObject);
    2571                 : 
    2572               2 :             poDictXObject->Add(CPLSPrintf("SymImage%d", nImageSymbolId), nImageSymbolId, 0);
    2573                 :         }
    2574                 : 
    2575              38 :         oDict.Add("Resources", poResources);
    2576                 : 
    2577              38 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2578                 :     }
    2579                 : 
    2580                 :     /* -------------------------------------------------------------- */
    2581                 :     /*  Write object stream                                           */
    2582                 :     /* -------------------------------------------------------------- */
    2583              38 :     VSIFPrintfL(fp, "stream\n");
    2584                 : 
    2585              38 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    2586                 : 
    2587              38 :     VSILFILE* fpGZip = NULL;
    2588              38 :     VSILFILE* fpBack = fp;
    2589              38 :     if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2590                 :     {
    2591              38 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2592              38 :         fp = fpGZip;
    2593                 :     }
    2594                 : 
    2595              38 :     VSIFPrintfL(fp, "q\n");
    2596                 : 
    2597              38 :     VSIFPrintfL(fp, "/GS1 gs\n");
    2598                 : 
    2599              38 :     if (nImageSymbolId == 0)
    2600                 :     {
    2601                 :         VSIFPrintfL(fp, "%f w\n"
    2602                 :                         "0 J\n"
    2603                 :                         "0 j\n"
    2604                 :                         "10 M\n"
    2605                 :                         "[%s]0 d\n",
    2606                 :                         dfPenWidth,
    2607              36 :                         osDashArray.c_str());
    2608                 : 
    2609              36 :         VSIFPrintfL(fp, "%f %f %f RG\n", nPenR / 255.0, nPenG / 255.0, nPenB / 255.0);
    2610              36 :         VSIFPrintfL(fp, "%f %f %f rg\n", nBrushR / 255.0, nBrushG / 255.0, nBrushB / 255.0);
    2611                 :     }
    2612                 : 
    2613              38 :     if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
    2614                 :     {
    2615              30 :         double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0];
    2616              30 :         double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2];
    2617                 : 
    2618              30 :         if (nImageSymbolId != 0)
    2619                 :         {
    2620                 :             VSIFPrintfL(fp, "%d 0 0 %d %f %f cm\n",
    2621                 :                         nImageWidth, nImageHeight,
    2622               2 :                         dfX - nImageWidth / 2, dfY - nImageHeight / 2);
    2623               2 :             VSIFPrintfL(fp, "/SymImage%d Do\n", nImageSymbolId);
    2624                 :         }
    2625              28 :         else if (osSymbolId == "")
    2626               8 :             osSymbolId = "ogr-sym-3"; /* symbol by default */
    2627              20 :         else if ( !(osSymbolId == "ogr-sym-0" ||
    2628                 :                     osSymbolId == "ogr-sym-1" ||
    2629                 :                     osSymbolId == "ogr-sym-2" ||
    2630                 :                     osSymbolId == "ogr-sym-3" ||
    2631                 :                     osSymbolId == "ogr-sym-4" ||
    2632                 :                     osSymbolId == "ogr-sym-5" ||
    2633                 :                     osSymbolId == "ogr-sym-6" ||
    2634                 :                     osSymbolId == "ogr-sym-7" ||
    2635                 :                     osSymbolId == "ogr-sym-8" ||
    2636                 :                     osSymbolId == "ogr-sym-9") )
    2637                 :         {
    2638               0 :             CPLDebug("PDF", "Unhandled symbol id : %s. Using ogr-sym-3 instead", osSymbolId.c_str());
    2639               0 :             osSymbolId = "ogr-sym-3";
    2640                 :         }
    2641                 : 
    2642              30 :         if (osSymbolId == "ogr-sym-0") /* cross (+)  */
    2643                 :         {
    2644               2 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY);
    2645               2 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY);
    2646               2 :             VSIFPrintfL(fp, "%f %f m\n", dfX, dfY - dfRadius);
    2647               2 :             VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + dfRadius);
    2648               2 :             VSIFPrintfL(fp, "S\n");
    2649                 :         }
    2650              28 :         else if (osSymbolId == "ogr-sym-1") /* diagcross (X) */
    2651                 :         {
    2652               2 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius);
    2653               2 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius);
    2654               2 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius);
    2655               2 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius);
    2656               2 :             VSIFPrintfL(fp, "S\n");
    2657                 :         }
    2658              26 :         else if (osSymbolId == "ogr-sym-2" ||
    2659                 :                  osSymbolId == "ogr-sym-3") /* circle */
    2660                 :         {
    2661                 :             /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */
    2662              12 :             const double dfKappa = 0.5522847498;
    2663                 : 
    2664              12 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY);
    2665                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2666                 :                         dfX - dfRadius, dfY - dfRadius * dfKappa,
    2667                 :                         dfX - dfRadius * dfKappa, dfY - dfRadius,
    2668              12 :                         dfX, dfY - dfRadius);
    2669                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2670                 :                         dfX + dfRadius * dfKappa, dfY - dfRadius,
    2671                 :                         dfX + dfRadius, dfY - dfRadius * dfKappa,
    2672              12 :                         dfX + dfRadius, dfY);
    2673                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2674                 :                         dfX + dfRadius, dfY + dfRadius * dfKappa,
    2675                 :                         dfX + dfRadius * dfKappa, dfY + dfRadius,
    2676              12 :                         dfX, dfY + dfRadius);
    2677                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f c\n",
    2678                 :                         dfX - dfRadius * dfKappa, dfY + dfRadius,
    2679                 :                         dfX - dfRadius, dfY + dfRadius * dfKappa,
    2680              12 :                         dfX - dfRadius, dfY);
    2681              12 :             if (osSymbolId == "ogr-sym-2") 
    2682               2 :                 VSIFPrintfL(fp, "s\n"); /* not filled */
    2683                 :             else
    2684              10 :                 VSIFPrintfL(fp, "b*\n"); /* filled */
    2685                 :         }
    2686              14 :         else if (osSymbolId == "ogr-sym-4" ||
    2687                 :                  osSymbolId == "ogr-sym-5") /* square */
    2688                 :         {
    2689               4 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius);
    2690               4 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius);
    2691               4 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius);
    2692               4 :             VSIFPrintfL(fp, "%f %f l\n", dfX - dfRadius, dfY - dfRadius);
    2693               4 :             if (osSymbolId == "ogr-sym-4")
    2694               2 :                 VSIFPrintfL(fp, "s\n"); /* not filled */
    2695                 :             else
    2696               2 :                 VSIFPrintfL(fp, "b*\n"); /* filled */
    2697                 :         }
    2698              10 :         else if (osSymbolId == "ogr-sym-6" ||
    2699                 :                  osSymbolId == "ogr-sym-7") /* triangle */
    2700                 :         {
    2701               4 :             const double dfSqrt3 = 1.73205080757;
    2702               4 :             VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius * dfSqrt3 / 3);
    2703               4 :             VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + 2 * dfRadius * dfSqrt3 / 3);
    2704               4 :             VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius * dfSqrt3 / 3);
    2705               4 :             if (osSymbolId == "ogr-sym-6")
    2706               2 :                 VSIFPrintfL(fp, "s\n"); /* not filled */
    2707                 :             else
    2708               2 :                 VSIFPrintfL(fp, "b*\n"); /* filled */
    2709                 :         }
    2710               6 :         else if (osSymbolId == "ogr-sym-8" ||
    2711                 :                  osSymbolId == "ogr-sym-9") /* star */
    2712                 :         {
    2713               4 :             const double dfSin18divSin126 = 0.38196601125;
    2714               4 :             VSIFPrintfL(fp, "%f %f m\n", dfX, dfY + dfRadius);
    2715              40 :             for(int i=1; i<10;i++)
    2716                 :             {
    2717              36 :                 double dfFactor = ((i % 2) == 1) ? dfSin18divSin126 : 1.0;
    2718                 :                 VSIFPrintfL(fp, "%f %f l\n",
    2719                 :                             dfX + cos(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor,
    2720              36 :                             dfY + sin(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor);
    2721                 :             }
    2722               4 :             if (osSymbolId == "ogr-sym-8")
    2723               2 :                 VSIFPrintfL(fp, "s\n"); /* not filled */
    2724                 :             else
    2725               2 :                 VSIFPrintfL(fp, "b*\n"); /* filled */
    2726                 :         }
    2727                 :     }
    2728                 :     else
    2729                 :     {
    2730               8 :         DrawGeometry(fp, hGeom, adfMatrix);
    2731                 :     }
    2732                 : 
    2733              38 :     VSIFPrintfL(fp, "Q");
    2734                 : 
    2735              38 :     if (fpGZip)
    2736              38 :         VSIFCloseL(fpGZip);
    2737              38 :     fp = fpBack;
    2738                 : 
    2739              38 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2740              38 :     VSIFPrintfL(fp, "\n");
    2741              38 :     VSIFPrintfL(fp, "endstream\n");
    2742              38 :     EndObj();
    2743                 : 
    2744              38 :     StartObj(nObjectLengthId);
    2745                 :     VSIFPrintfL(fp,
    2746                 :                 "   %ld\n",
    2747              38 :                 (long)(nStreamEnd - nStreamStart));
    2748              38 :     EndObj();
    2749                 : 
    2750                 :     /* -------------------------------------------------------------- */
    2751                 :     /*  Write label                                                   */
    2752                 :     /* -------------------------------------------------------------- */
    2753              38 :     if (osLabelText.size() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint)
    2754                 :     {
    2755               3 :         if (osVectorDesc.nOCGTextId == 0)
    2756               3 :             osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOGCId);
    2757                 : 
    2758                 :         /* -------------------------------------------------------------- */
    2759                 :         /*  Write object dictionary                                       */
    2760                 :         /* -------------------------------------------------------------- */
    2761               3 :         nObjectId = AllocNewObject();
    2762               3 :         nObjectLengthId = AllocNewObject();
    2763                 : 
    2764               3 :         osVectorDesc.aIdsText.push_back(nObjectId);
    2765                 : 
    2766               3 :         StartObj(nObjectId);
    2767                 :         {
    2768               3 :             GDALPDFDictionaryRW oDict;
    2769                 : 
    2770               3 :             GDALDataset* poClippingDS = oPageContext.poClippingDS;
    2771               3 :             int  nWidth = poClippingDS->GetRasterXSize();
    2772               3 :             int  nHeight = poClippingDS->GetRasterYSize();
    2773               3 :             double dfUserUnit = oPageContext.dfDPI / 72.0;
    2774               3 :             double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight;
    2775               3 :             double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop;
    2776                 : 
    2777                 :             oDict.Add("Length", nObjectLengthId, 0)
    2778                 :                 .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    2779                 :                 .Add("BBox", &((new GDALPDFArrayRW())
    2780                 :                                 ->Add(0).Add(0)).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit))
    2781               3 :                 .Add("Subtype", GDALPDFObjectRW::CreateName("Form"));
    2782               3 :             if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2783                 :             {
    2784               3 :                 oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    2785                 :             }
    2786                 : 
    2787               3 :             GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW();
    2788                 : 
    2789               3 :             if (nTextA != 255)
    2790                 :             {
    2791               1 :                 GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW();
    2792               2 :                 poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState"));
    2793               1 :                 poGS1->Add("ca", (nTextA == 127 || nTextA == 128) ? 0.5 : nTextA / 255.0);
    2794                 : 
    2795               1 :                 GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW();
    2796               1 :                 poExtGState->Add("GS1", poGS1);
    2797                 : 
    2798               1 :                 poResources->Add("ExtGState", poExtGState);
    2799                 :             }
    2800                 : 
    2801               3 :             GDALPDFDictionaryRW* poDictFTimesRoman = NULL;
    2802               3 :             poDictFTimesRoman = new GDALPDFDictionaryRW();
    2803               6 :             poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    2804               3 :             poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman"));
    2805               3 :             poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    2806               3 :             poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    2807                 : 
    2808               3 :             GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
    2809               3 :             if (poDictFTimesRoman)
    2810               3 :                 poDictFont->Add("FTimesRoman", poDictFTimesRoman);
    2811               3 :             poResources->Add("Font", poDictFont);
    2812                 : 
    2813               3 :             oDict.Add("Resources", poResources);
    2814                 : 
    2815               3 :             VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2816                 :         }
    2817                 : 
    2818                 :         /* -------------------------------------------------------------- */
    2819                 :         /*  Write object stream                                           */
    2820                 :         /* -------------------------------------------------------------- */
    2821               3 :         VSIFPrintfL(fp, "stream\n");
    2822                 : 
    2823               3 :         vsi_l_offset nStreamStart = VSIFTellL(fp);
    2824                 : 
    2825               3 :         VSILFILE* fpGZip = NULL;
    2826               3 :         VSILFILE* fpBack = fp;
    2827               3 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    2828                 :         {
    2829               3 :             fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    2830               3 :             fp = fpGZip;
    2831                 :         }
    2832                 : 
    2833               3 :         double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0] + dfTextDx;
    2834               3 :         double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2] + dfTextDy;
    2835                 : 
    2836               3 :         VSIFPrintfL(fp, "q\n");
    2837               3 :         VSIFPrintfL(fp, "BT\n");
    2838               3 :         if (nTextA != 255)
    2839                 :         {
    2840               1 :             VSIFPrintfL(fp, "/GS1 gs\n");
    2841                 :         }
    2842               3 :         if (dfTextAngle == 0)
    2843                 :         {
    2844               2 :             VSIFPrintfL(fp, "%f %f Td\n", dfX, dfY);
    2845                 :         }
    2846                 :         else
    2847                 :         {
    2848               1 :             dfTextAngle = - dfTextAngle * M_PI / 180.0;
    2849                 :             VSIFPrintfL(fp, "%f %f %f %f %f %f Tm\n",
    2850                 :                         cos(dfTextAngle), -sin(dfTextAngle),
    2851                 :                         sin(dfTextAngle), cos(dfTextAngle),
    2852               1 :                         dfX, dfY);
    2853                 :         }
    2854               3 :         VSIFPrintfL(fp, "%f %f %f rg\n", nTextR / 255.0, nTextG / 255.0, nTextB / 255.0);
    2855               3 :         VSIFPrintfL(fp, "/FTimesRoman %f Tf\n", dfTextSize);
    2856               3 :         VSIFPrintfL(fp, "(");
    2857              37 :         for(size_t i=0;i<osLabelText.size();i++)
    2858                 :         {
    2859                 :             /*if (osLabelText[i] == '\n')
    2860                 :                 VSIFPrintfL(fp, ") Tj T* (");
    2861              34 :             else */if (osLabelText[i] >= 32 && osLabelText[i] <= 127)
    2862              34 :                 VSIFPrintfL(fp, "%c", osLabelText[i]);
    2863                 :             else
    2864               0 :                 VSIFPrintfL(fp, "_");
    2865                 :         }
    2866               3 :         VSIFPrintfL(fp, ") Tj\n");
    2867               3 :         VSIFPrintfL(fp, "ET\n");
    2868               3 :         VSIFPrintfL(fp, "Q");
    2869                 : 
    2870               3 :         if (fpGZip)
    2871               3 :             VSIFCloseL(fpGZip);
    2872               3 :         fp = fpBack;
    2873                 : 
    2874               3 :         vsi_l_offset nStreamEnd = VSIFTellL(fp);
    2875               3 :         VSIFPrintfL(fp, "\n");
    2876               3 :         VSIFPrintfL(fp, "endstream\n");
    2877               3 :         EndObj();
    2878                 : 
    2879               3 :         StartObj(nObjectLengthId);
    2880                 :         VSIFPrintfL(fp,
    2881                 :                     "   %ld\n",
    2882               3 :                     (long)(nStreamEnd - nStreamStart));
    2883               3 :         EndObj();
    2884                 :     }
    2885                 :     else
    2886                 :     {
    2887              35 :         osVectorDesc.aIdsText.push_back(0);
    2888                 :     }
    2889                 : 
    2890                 :     /* -------------------------------------------------------------- */
    2891                 :     /*  Write feature attributes                                      */
    2892                 :     /* -------------------------------------------------------------- */
    2893              38 :     int nFeatureUserProperties = 0;
    2894                 : 
    2895              38 :     CPLString osFeatureName;
    2896                 : 
    2897              38 :     if (bWriteOGRAttributes)
    2898                 :     {
    2899              23 :         int iField = -1;
    2900              23 :         if (pszOGRDisplayField &&
    2901                 :             (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField)) >= 0)
    2902               3 :             osFeatureName = OGR_F_GetFieldAsString(hFeat, iField);
    2903                 :         else
    2904              20 :             osFeatureName = CPLSPrintf("feature%d", iObjLayer + 1);
    2905                 : 
    2906              23 :         nFeatureUserProperties = AllocNewObject();
    2907              23 :         StartObj(nFeatureUserProperties);
    2908                 : 
    2909              23 :         GDALPDFDictionaryRW oDict;
    2910              23 :         GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW();
    2911              23 :         oDict.Add("A", poDictA);
    2912              23 :         poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties"));
    2913                 : 
    2914              23 :         int nFields = OGR_F_GetFieldCount(hFeat);
    2915              23 :         GDALPDFArrayRW* poArray = new GDALPDFArrayRW();
    2916              97 :         for(int i = 0; i < nFields; i++)
    2917                 :         {
    2918              74 :             if (OGR_F_IsFieldSet(hFeat, i))
    2919                 :             {
    2920              20 :                 OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i );
    2921              20 :                 GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW();
    2922              40 :                 poKV->Add("N", OGR_Fld_GetNameRef(hFDefn));
    2923              20 :                 if (OGR_Fld_GetType(hFDefn) == OFTInteger)
    2924               9 :                     poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i));
    2925              11 :                 else if (OGR_Fld_GetType(hFDefn) == OFTReal)
    2926               1 :                     poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i));
    2927                 :                 else
    2928              10 :                     poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i));
    2929              20 :                 poArray->Add(poKV);
    2930                 :             }
    2931                 :         }
    2932                 : 
    2933              23 :         poDictA->Add("P", poArray);
    2934                 : 
    2935              23 :         oDict.Add("K", iObj);
    2936              23 :         oDict.Add("P", osVectorDesc.nFeatureLayerId, 0);
    2937              23 :         oDict.Add("Pg", oPageContext.nPageId, 0);
    2938              23 :         oDict.Add("S", GDALPDFObjectRW::CreateName("feature"));
    2939              23 :         oDict.Add("T", osFeatureName);
    2940                 : 
    2941              23 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    2942                 : 
    2943              23 :         EndObj();
    2944                 :     }
    2945                 : 
    2946              38 :     iObj ++;
    2947              38 :     iObjLayer ++;
    2948                 : 
    2949              38 :     osVectorDesc.aUserPropertiesIds.push_back(nFeatureUserProperties);
    2950              38 :     osVectorDesc.aFeatureNames.push_back(osFeatureName);
    2951                 : 
    2952              38 :     return TRUE;
    2953                 : }
    2954                 : 
    2955                 : #endif
    2956                 : 
    2957                 : /************************************************************************/
    2958                 : /*                               EndPage()                              */
    2959                 : /************************************************************************/
    2960                 : 
    2961              53 : int GDALPDFWriter::EndPage(const char* pszExtraImages,
    2962                 :                            const char* pszExtraStream,
    2963                 :                            const char* pszExtraLayerName,
    2964                 :                            const char* pszOffLayers,
    2965                 :                            const char* pszExclusiveLayers)
    2966                 : {
    2967              53 :     int nLayerExtraId = WriteOCG(pszExtraLayerName);
    2968              53 :     if( pszOffLayers )
    2969               1 :         osOffLayers = pszOffLayers;
    2970              53 :     if( pszExclusiveLayers )
    2971               1 :         osExclusiveLayers = pszExclusiveLayers;
    2972                 : 
    2973              53 :     int bHasTimesRoman = pszExtraStream && strstr(pszExtraStream, "/FTimesRoman");
    2974              53 :     int bHasTimesBold = pszExtraStream && strstr(pszExtraStream, "/FTimesBold");
    2975                 : 
    2976                 :     /* -------------------------------------------------------------- */
    2977                 :     /*  Write extra images                                            */
    2978                 :     /* -------------------------------------------------------------- */
    2979              53 :     std::vector<GDALPDFImageDesc> asExtraImageDesc;
    2980              53 :     if (pszExtraImages)
    2981                 :     {
    2982               1 :         if( GDALGetDriverCount() == 0 )
    2983               0 :             GDALAllRegister();
    2984                 : 
    2985               1 :         char** papszExtraImagesTokens = CSLTokenizeString2(pszExtraImages, ",", 0);
    2986               1 :         double dfUserUnit = oPageContext.dfDPI / 72.0;
    2987               1 :         int nCount = CSLCount(papszExtraImagesTokens);
    2988               4 :         for(int i=0;i+4<=nCount; /* */)
    2989                 :         {
    2990               2 :             const char* pszImageFilename = papszExtraImagesTokens[i+0];
    2991               2 :             double dfX = atof(papszExtraImagesTokens[i+1]);
    2992               2 :             double dfY = atof(papszExtraImagesTokens[i+2]);
    2993               2 :             double dfScale = atof(papszExtraImagesTokens[i+3]);
    2994               2 :             const char* pszLinkVal = NULL;
    2995               2 :             i += 4;
    2996               2 :             if( i < nCount && EQUALN(papszExtraImagesTokens[i],"link=",5) )
    2997                 :             {
    2998               1 :                 pszLinkVal = papszExtraImagesTokens[i] + 5;
    2999               1 :                 i++;
    3000                 :             }
    3001               2 :             GDALDataset* poImageDS = (GDALDataset* )GDALOpen(pszImageFilename, GA_ReadOnly);
    3002               2 :             if (poImageDS)
    3003                 :             {
    3004                 :                 int nImageId = WriteBlock( poImageDS,
    3005                 :                                             0, 0,
    3006                 :                                             poImageDS->GetRasterXSize(),
    3007                 :                                             poImageDS->GetRasterYSize(),
    3008                 :                                             0,
    3009                 :                                             COMPRESS_DEFAULT,
    3010                 :                                             0,
    3011                 :                                             -1,
    3012                 :                                             NULL,
    3013                 :                                             NULL,
    3014               2 :                                             NULL );
    3015                 : 
    3016               2 :                 if (nImageId)
    3017                 :                 {
    3018                 :                     GDALPDFImageDesc oImageDesc;
    3019               2 :                     oImageDesc.nImageId = nImageId;
    3020               2 :                     oImageDesc.dfXSize = poImageDS->GetRasterXSize() / dfUserUnit * dfScale;
    3021               2 :                     oImageDesc.dfYSize = poImageDS->GetRasterYSize() / dfUserUnit * dfScale;
    3022               2 :                     oImageDesc.dfXOff = dfX;
    3023               2 :                     oImageDesc.dfYOff = dfY;
    3024                 : 
    3025               2 :                     asExtraImageDesc.push_back(oImageDesc);
    3026                 : 
    3027               2 :                     if( pszLinkVal != NULL )
    3028                 :                     {
    3029               1 :                         int nAnnotId = AllocNewObject();
    3030               1 :                         oPageContext.anAnnotationsId.push_back(nAnnotId);
    3031               1 :                         StartObj(nAnnotId);
    3032                 :                         {
    3033               1 :                             GDALPDFDictionaryRW oDict;
    3034               1 :                             oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot"));
    3035               1 :                             oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link"));
    3036                 :                             oDict.Add("Rect", &(new GDALPDFArrayRW())->
    3037                 :                                 Add(oImageDesc.dfXOff).
    3038                 :                                 Add(oImageDesc.dfYOff).
    3039                 :                                 Add(oImageDesc.dfXOff + oImageDesc.dfXSize).
    3040               1 :                                 Add(oImageDesc.dfYOff + oImageDesc.dfYSize));
    3041                 :                             oDict.Add("A", &(new GDALPDFDictionaryRW())->
    3042                 :                                 Add("S", GDALPDFObjectRW::CreateName("URI")).
    3043               1 :                                 Add("URI", pszLinkVal));
    3044                 :                             oDict.Add("BS", &(new GDALPDFDictionaryRW())->
    3045                 :                                 Add("Type", GDALPDFObjectRW::CreateName("Border")).
    3046                 :                                 Add("S", GDALPDFObjectRW::CreateName("S")).
    3047               1 :                                 Add("W", 0));
    3048               1 :                             oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0));
    3049               1 :                             oDict.Add("H", GDALPDFObjectRW::CreateName("I"));
    3050                 : 
    3051               1 :                             VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3052                 :                         }
    3053               1 :                         EndObj();
    3054                 :                     }
    3055                 :                 }
    3056                 : 
    3057               2 :                 GDALClose(poImageDS);
    3058                 :             }
    3059                 :         }
    3060               1 :         CSLDestroy(papszExtraImagesTokens);
    3061                 :     }
    3062                 : 
    3063                 :     /* -------------------------------------------------------------- */
    3064                 :     /*  Write content dictionary                                      */
    3065                 :     /* -------------------------------------------------------------- */
    3066              53 :     int nContentLengthId = AllocNewObject();
    3067                 : 
    3068              53 :     StartObj(oPageContext.nContentId);
    3069                 :     {
    3070              53 :         GDALPDFDictionaryRW oDict;
    3071              53 :         oDict.Add("Length", nContentLengthId, 0);
    3072              53 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    3073                 :         {
    3074              51 :             oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    3075                 :         }
    3076              53 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3077                 :     }
    3078                 : 
    3079                 :     /* -------------------------------------------------------------- */
    3080                 :     /*  Write content stream                                          */
    3081                 :     /* -------------------------------------------------------------- */
    3082              53 :     VSIFPrintfL(fp, "stream\n");
    3083              53 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    3084                 : 
    3085              53 :     VSILFILE* fpGZip = NULL;
    3086              53 :     VSILFILE* fpBack = fp;
    3087              53 :     if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    3088                 :     {
    3089              51 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    3090              51 :         fp = fpGZip;
    3091                 :     }
    3092                 : 
    3093                 :     /* -------------------------------------------------------------- */
    3094                 :     /*  Write drawing instructions for raster blocks                  */
    3095                 :     /* -------------------------------------------------------------- */
    3096             105 :     for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++)
    3097                 :     {
    3098              52 :         const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster];
    3099              52 :         if (oDesc.nOCGRasterId)
    3100               3 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oDesc.nOCGRasterId);
    3101                 : 
    3102             180 :         for(size_t iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++)
    3103                 :         {
    3104             128 :             VSIFPrintfL(fp, "q\n");
    3105             128 :             GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXSize);
    3106             128 :             GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYSize);
    3107             128 :             GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXOff);
    3108             128 :             GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYOff);
    3109                 :             VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
    3110                 :                         poXSize->Serialize().c_str(),
    3111                 :                         poYSize->Serialize().c_str(),
    3112                 :                         poXOff->Serialize().c_str(),
    3113             128 :                         poYOff->Serialize().c_str());
    3114             128 :             delete poXSize;
    3115             128 :             delete poYSize;
    3116             128 :             delete poXOff;
    3117             128 :             delete poYOff;
    3118                 :             VSIFPrintfL(fp, "/Image%d Do\n",
    3119             128 :                         oDesc.asImageDesc[iImage].nImageId);
    3120             128 :             VSIFPrintfL(fp, "Q\n");
    3121                 :         }
    3122                 : 
    3123              52 :         if (oDesc.nOCGRasterId)
    3124               3 :             VSIFPrintfL(fp, "EMC\n");
    3125                 :     }
    3126                 : 
    3127                 :     /* -------------------------------------------------------------- */
    3128                 :     /*  Write drawing instructions for vector features                */
    3129                 :     /* -------------------------------------------------------------- */
    3130              53 :     int iObj = 0;
    3131              57 :     for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    3132                 :     {
    3133               4 :         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    3134                 : 
    3135               4 :         VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
    3136                 : 
    3137              42 :         for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    3138                 :         {
    3139              38 :             CPLString osName = oLayerDesc.aFeatureNames[iVector];
    3140              38 :             if (osName.size())
    3141                 :             {
    3142                 :                 VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
    3143              23 :                             iObj);
    3144                 :             }
    3145                 : 
    3146              38 :             iObj ++;
    3147                 : 
    3148              38 :             VSIFPrintfL(fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector]);
    3149                 : 
    3150              38 :             if (osName.size())
    3151                 :             {
    3152              23 :                 VSIFPrintfL(fp, "EMC\n");
    3153                 :             }
    3154                 :         }
    3155                 : 
    3156               4 :         VSIFPrintfL(fp, "EMC\n");
    3157                 :     }
    3158                 : 
    3159                 :     /* -------------------------------------------------------------- */
    3160                 :     /*  Write drawing instructions for labels of vector features      */
    3161                 :     /* -------------------------------------------------------------- */
    3162              53 :     iObj = 0;
    3163              57 :     for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    3164                 :     {
    3165               4 :         GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    3166               4 :         if (oLayerDesc.nOCGTextId)
    3167                 :         {
    3168               3 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId);
    3169               3 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId);
    3170                 : 
    3171              36 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    3172                 :             {
    3173              33 :                 if (oLayerDesc.aIdsText[iVector])
    3174                 :                 {
    3175               3 :                     CPLString osName = oLayerDesc.aFeatureNames[iVector];
    3176               3 :                     if (osName.size())
    3177                 :                     {
    3178                 :                         VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n",
    3179               2 :                                     iObj);
    3180                 :                     }
    3181                 : 
    3182               3 :                     VSIFPrintfL(fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector]);
    3183                 : 
    3184               3 :                     if (osName.size())
    3185                 :                     {
    3186               2 :                         VSIFPrintfL(fp, "EMC\n");
    3187               3 :                     }
    3188                 :                 }
    3189                 : 
    3190              33 :                 iObj ++;
    3191                 :             }
    3192                 : 
    3193               3 :             VSIFPrintfL(fp, "EMC\n");
    3194               3 :             VSIFPrintfL(fp, "EMC\n");
    3195                 :         }
    3196                 :         else
    3197               1 :             iObj += oLayerDesc.aIds.size();
    3198                 :     }
    3199                 : 
    3200                 :     /* -------------------------------------------------------------- */
    3201                 :     /*  Write drawing instructions for extra content.                 */
    3202                 :     /* -------------------------------------------------------------- */
    3203              53 :     if (pszExtraStream || asExtraImageDesc.size())
    3204                 :     {
    3205               1 :         if (nLayerExtraId)
    3206               1 :             VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", nLayerExtraId);
    3207                 : 
    3208                 :         /* -------------------------------------------------------------- */
    3209                 :         /*  Write drawing instructions for extra images.                  */
    3210                 :         /* -------------------------------------------------------------- */
    3211               3 :         for(size_t iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
    3212                 :         {
    3213               2 :             VSIFPrintfL(fp, "q\n");
    3214               2 :             GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXSize);
    3215               2 :             GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYSize);
    3216               2 :             GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXOff);
    3217               2 :             GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYOff);
    3218                 :             VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n",
    3219                 :                         poXSize->Serialize().c_str(),
    3220                 :                         poYSize->Serialize().c_str(),
    3221                 :                         poXOff->Serialize().c_str(),
    3222               2 :                         poYOff->Serialize().c_str());
    3223               2 :             delete poXSize;
    3224               2 :             delete poYSize;
    3225               2 :             delete poXOff;
    3226               2 :             delete poYOff;
    3227                 :             VSIFPrintfL(fp, "/Image%d Do\n",
    3228               2 :                         asExtraImageDesc[iImage].nImageId);
    3229               2 :             VSIFPrintfL(fp, "Q\n");
    3230                 :         }
    3231                 : 
    3232               1 :         if (pszExtraStream)
    3233               1 :             VSIFPrintfL(fp, "%s\n", pszExtraStream);
    3234                 : 
    3235               1 :         if (nLayerExtraId)
    3236               1 :             VSIFPrintfL(fp, "EMC\n");
    3237                 :     }
    3238                 : 
    3239              53 :     if (fpGZip)
    3240              51 :         VSIFCloseL(fpGZip);
    3241              53 :     fp = fpBack;
    3242                 : 
    3243              53 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    3244              53 :     if (fpGZip)
    3245              51 :         VSIFPrintfL(fp, "\n");
    3246              53 :     VSIFPrintfL(fp, "endstream\n");
    3247              53 :     EndObj();
    3248                 : 
    3249              53 :     StartObj(nContentLengthId);
    3250                 :     VSIFPrintfL(fp,
    3251                 :                 "   %ld\n",
    3252              53 :                 (long)(nStreamEnd - nStreamStart));
    3253              53 :     EndObj();
    3254                 : 
    3255                 :     /* -------------------------------------------------------------- */
    3256                 :     /*  Write objects for feature tree.                               */
    3257                 :     /* -------------------------------------------------------------- */
    3258              53 :     if (nStructTreeRootId)
    3259                 :     {
    3260               3 :         int nParentTreeId = AllocNewObject();
    3261               3 :         StartObj(nParentTreeId);
    3262               3 :         VSIFPrintfL(fp, "<< /Nums [ 0 ");
    3263               3 :         VSIFPrintfL(fp, "[ ");
    3264               6 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    3265                 :         {
    3266               3 :             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    3267              26 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    3268                 :             {
    3269              23 :                 int nId = oLayerDesc.aUserPropertiesIds[iVector];
    3270              23 :                 if (nId)
    3271              23 :                     VSIFPrintfL(fp, "%d 0 R ", nId);
    3272                 :             }
    3273                 :         }
    3274               3 :         VSIFPrintfL(fp, " ]\n");
    3275               3 :         VSIFPrintfL(fp, " ] >> \n");
    3276               3 :         EndObj();
    3277                 : 
    3278               3 :         StartObj(nStructTreeRootId);
    3279                 :         VSIFPrintfL(fp,
    3280                 :                     "<< "
    3281                 :                     "/Type /StructTreeRoot "
    3282                 :                     "/ParentTree %d 0 R "
    3283               3 :                     "/K [ ", nParentTreeId);
    3284               6 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    3285                 :         {
    3286               3 :             VSIFPrintfL(fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId);
    3287                 :         }
    3288               3 :         VSIFPrintfL(fp,"] >>\n");
    3289               3 :         EndObj();
    3290                 :     }
    3291                 : 
    3292                 :     /* -------------------------------------------------------------- */
    3293                 :     /*  Write page resource dictionary.                               */
    3294                 :     /* -------------------------------------------------------------- */
    3295              53 :     StartObj(oPageContext.nResourcesId);
    3296                 :     {
    3297              53 :         GDALPDFDictionaryRW oDict;
    3298              53 :         GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW();
    3299              53 :         oDict.Add("XObject", poDictXObject);
    3300                 :         size_t iImage;
    3301             105 :         for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++)
    3302                 :         {
    3303              52 :             const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster];
    3304             180 :             for(iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++)
    3305                 :             {
    3306                 :                 poDictXObject->Add(CPLSPrintf("Image%d", oDesc.asImageDesc[iImage].nImageId),
    3307             128 :                                 oDesc.asImageDesc[iImage].nImageId, 0);
    3308                 :             }
    3309                 :         }
    3310              55 :         for(iImage = 0; iImage < asExtraImageDesc.size(); iImage ++)
    3311                 :         {
    3312                 :             poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId),
    3313               2 :                                asExtraImageDesc[iImage].nImageId, 0);
    3314                 :         }
    3315              57 :         for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++)
    3316                 :         {
    3317               4 :             GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer];
    3318              42 :             for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++)
    3319                 :             {
    3320                 :                 poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector]),
    3321              38 :                                 oLayerDesc.aIds[iVector], 0);
    3322              38 :                 if (oLayerDesc.aIdsText[iVector])
    3323                 :                     poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector]),
    3324               3 :                                 oLayerDesc.aIdsText[iVector], 0);
    3325                 :             }
    3326                 :         }
    3327                 : 
    3328              53 :         GDALPDFDictionaryRW* poDictFTimesRoman = NULL;
    3329              53 :         if (bHasTimesRoman)
    3330                 :         {
    3331               1 :             poDictFTimesRoman = new GDALPDFDictionaryRW();
    3332               2 :             poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    3333               1 :             poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman"));
    3334               1 :             poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    3335               1 :             poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    3336                 :         }
    3337                 : 
    3338              53 :         GDALPDFDictionaryRW* poDictFTimesBold = NULL;
    3339              53 :         if (bHasTimesBold)
    3340                 :         {
    3341               0 :             poDictFTimesBold = new GDALPDFDictionaryRW();
    3342               0 :             poDictFTimesBold->Add("Type", GDALPDFObjectRW::CreateName("Font"));
    3343               0 :             poDictFTimesBold->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Bold"));
    3344               0 :             poDictFTimesBold->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding"));
    3345               0 :             poDictFTimesBold->Add("Subtype", GDALPDFObjectRW::CreateName("Type1"));
    3346                 :         }
    3347                 : 
    3348              53 :         if (poDictFTimesRoman != NULL || poDictFTimesBold != NULL)
    3349                 :         {
    3350               1 :             GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW();
    3351               1 :             if (poDictFTimesRoman)
    3352               1 :                 poDictFont->Add("FTimesRoman", poDictFTimesRoman);
    3353               1 :             if (poDictFTimesBold)
    3354               0 :                 poDictFont->Add("FTimesBold", poDictFTimesBold);
    3355               1 :             oDict.Add("Font", poDictFont);
    3356                 :         }
    3357                 : 
    3358              53 :         if (asOCGs.size())
    3359                 :         {
    3360               6 :             GDALPDFDictionaryRW* poDictProperties = new GDALPDFDictionaryRW();
    3361              17 :             for(size_t i=0; i<asOCGs.size(); i++)
    3362                 :                 poDictProperties->Add(CPLSPrintf("Lyr%d", asOCGs[i].nId),
    3363              11 :                                       asOCGs[i].nId, 0);
    3364               6 :             oDict.Add("Properties", poDictProperties);
    3365                 :         }
    3366                 : 
    3367              53 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3368                 :     }
    3369              53 :     EndObj();
    3370                 : 
    3371                 :     /* -------------------------------------------------------------- */
    3372                 :     /*  Write annotation arrays.                                      */
    3373                 :     /* -------------------------------------------------------------- */
    3374              53 :     StartObj(oPageContext.nAnnotsId);
    3375                 :     {
    3376              53 :         GDALPDFArrayRW oArray;
    3377              58 :         for(size_t i = 0; i < oPageContext.anAnnotationsId.size(); i++)
    3378                 :         {
    3379               5 :             oArray.Add(oPageContext.anAnnotationsId[i], 0);
    3380                 :         }
    3381              53 :         VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str());
    3382                 :     }
    3383              53 :     EndObj();
    3384                 : 
    3385              53 :     return TRUE;
    3386                 : }
    3387                 : 
    3388                 : /************************************************************************/
    3389                 : /*                             WriteMask()                              */
    3390                 : /************************************************************************/
    3391                 : 
    3392              63 : int GDALPDFWriter::WriteMask(GDALDataset* poSrcDS,
    3393                 :                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
    3394                 :                              PDFCompressMethod eCompressMethod)
    3395                 : {
    3396              63 :     int nMaskSize = nReqXSize * nReqYSize;
    3397              63 :     GByte* pabyMask = (GByte*)VSIMalloc(nMaskSize);
    3398              63 :     if (pabyMask == NULL)
    3399               0 :         return 0;
    3400                 : 
    3401                 :     CPLErr eErr;
    3402                 :     eErr = poSrcDS->GetRasterBand(4)->RasterIO(
    3403                 :             GF_Read,
    3404                 :             nXOff, nYOff,
    3405                 :             nReqXSize, nReqYSize,
    3406                 :             pabyMask, nReqXSize, nReqYSize, GDT_Byte,
    3407              63 :             0, 0);
    3408              63 :     if (eErr != CE_None)
    3409                 :     {
    3410               0 :         VSIFree(pabyMask);
    3411               0 :         return 0;
    3412                 :     }
    3413                 : 
    3414              63 :     int bOnly0or255 = TRUE;
    3415              63 :     int bOnly255 = TRUE;
    3416              63 :     int bOnly0 = TRUE;
    3417                 :     int i;
    3418           16349 :     for(i=0;i<nReqXSize * nReqYSize;i++)
    3419                 :     {
    3420           16332 :         if (pabyMask[i] == 0)
    3421           12188 :             bOnly255 = FALSE;
    3422            4144 :         else if (pabyMask[i] == 255)
    3423            4098 :             bOnly0 = FALSE;
    3424                 :         else
    3425                 :         {
    3426              46 :             bOnly0 = FALSE;
    3427              46 :             bOnly255 = FALSE;
    3428              46 :             bOnly0or255 = FALSE;
    3429              46 :             break;
    3430                 :         }
    3431                 :     }
    3432                 : 
    3433              63 :     if (bOnly255)
    3434                 :     {
    3435               0 :         CPLFree(pabyMask);
    3436               0 :         return 0;
    3437                 :     }
    3438                 : 
    3439              63 :     if (bOnly0or255)
    3440                 :     {
    3441                 :         /* Translate to 1 bit */
    3442              17 :         int nReqXSize1 = (nReqXSize + 7) / 8;
    3443              17 :         GByte* pabyMask1 = (GByte*)VSICalloc(nReqXSize1, nReqYSize);
    3444              17 :         if (pabyMask1 == NULL)
    3445                 :         {
    3446               0 :             CPLFree(pabyMask);
    3447               0 :             return 0;
    3448                 :         }
    3449             499 :         for(int y=0;y<nReqYSize;y++)
    3450                 :         {
    3451            6686 :             for(int x=0;x<nReqXSize;x++)
    3452                 :             {
    3453            6204 :                 if (pabyMask[y * nReqXSize + x])
    3454            1792 :                     pabyMask1[y * nReqXSize1 + x / 8] |= 1 << (7 - (x % 8));
    3455                 :             }
    3456                 :         }
    3457              17 :         VSIFree(pabyMask);
    3458              17 :         pabyMask = pabyMask1;
    3459              17 :         nMaskSize = nReqXSize1 * nReqYSize;
    3460                 :     }
    3461                 : 
    3462              63 :     int nMaskId = AllocNewObject();
    3463              63 :     int nMaskLengthId = AllocNewObject();
    3464                 : 
    3465              63 :     StartObj(nMaskId);
    3466              63 :     GDALPDFDictionaryRW oDict;
    3467                 :     oDict.Add("Length", nMaskLengthId, 0)
    3468              63 :          .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
    3469              63 :     if( eCompressMethod != COMPRESS_NONE )
    3470                 :     {
    3471              63 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    3472                 :     }
    3473                 :     oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    3474                 :          .Add("Width", nReqXSize)
    3475                 :          .Add("Height", nReqYSize)
    3476                 :          .Add("ColorSpace", GDALPDFObjectRW::CreateName("DeviceGray"))
    3477              63 :          .Add("BitsPerComponent", (bOnly0or255) ? 1 : 8);
    3478              63 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3479              63 :     VSIFPrintfL(fp, "stream\n");
    3480              63 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    3481                 : 
    3482              63 :     VSILFILE* fpGZip = NULL;
    3483              63 :     VSILFILE* fpBack = fp;
    3484              63 :     if( eCompressMethod != COMPRESS_NONE )
    3485                 :     {
    3486              63 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    3487              63 :         fp = fpGZip;
    3488                 :     }
    3489                 : 
    3490              63 :     VSIFWriteL(pabyMask, nMaskSize, 1, fp);
    3491              63 :     CPLFree(pabyMask);
    3492                 : 
    3493              63 :     if (fpGZip)
    3494              63 :         VSIFCloseL(fpGZip);
    3495              63 :     fp = fpBack;
    3496                 : 
    3497              63 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    3498                 :     VSIFPrintfL(fp,
    3499                 :                 "\n"
    3500              63 :                 "endstream\n");
    3501              63 :     EndObj();
    3502                 : 
    3503              63 :     StartObj(nMaskLengthId);
    3504                 :     VSIFPrintfL(fp,
    3505                 :                 "   %ld\n",
    3506              63 :                 (long)(nStreamEnd - nStreamStart));
    3507              63 :     EndObj();
    3508                 : 
    3509              63 :     return nMaskId;
    3510                 : }
    3511                 : 
    3512                 : /************************************************************************/
    3513                 : /*                             WriteBlock()                             */
    3514                 : /************************************************************************/
    3515                 : 
    3516             132 : int GDALPDFWriter::WriteBlock(GDALDataset* poSrcDS,
    3517                 :                              int nXOff, int nYOff, int nReqXSize, int nReqYSize,
    3518                 :                              int nColorTableId,
    3519                 :                              PDFCompressMethod eCompressMethod,
    3520                 :                              int nPredictor,
    3521                 :                              int nJPEGQuality,
    3522                 :                              const char* pszJPEG2000_DRIVER,
    3523                 :                              GDALProgressFunc pfnProgress,
    3524                 :                              void * pProgressData)
    3525                 : {
    3526             132 :     int  nBands = poSrcDS->GetRasterCount();
    3527             132 :     if (nBands == 0)
    3528               0 :         return 0;
    3529                 : 
    3530             132 :     if (nColorTableId == 0)
    3531             131 :         nColorTableId = WriteColorTable(poSrcDS);
    3532                 : 
    3533             132 :     CPLErr eErr = CE_None;
    3534             132 :     GDALDataset* poBlockSrcDS = NULL;
    3535             132 :     GDALDatasetH hMemDS = NULL;
    3536             132 :     GByte* pabyMEMDSBuffer = NULL;
    3537                 : 
    3538             132 :     if (eCompressMethod == COMPRESS_DEFAULT)
    3539                 :     {
    3540             121 :         GDALDataset* poSrcDSToTest = poSrcDS;
    3541                 : 
    3542                 :         /* Test if we can directly copy original JPEG content */
    3543                 :         /* if available */
    3544             242 :         if (poSrcDS->GetDriver() != NULL &&
    3545             121 :             poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
    3546                 :         {
    3547               5 :             VRTDataset* poVRTDS = (VRTDataset* )poSrcDS;
    3548               5 :             poSrcDSToTest = poVRTDS->GetSingleSimpleSource();
    3549                 :         }
    3550                 : 
    3551             361 :         if (poSrcDSToTest != NULL &&
    3552             120 :             poSrcDSToTest->GetDriver() != NULL &&
    3553             120 :             poSrcDSToTest->GetDriver() == GDALGetDriverByName("JPEG") &&
    3554                 :             nXOff == 0 && nYOff == 0 &&
    3555                 :             nReqXSize == poSrcDSToTest->GetRasterXSize() &&
    3556                 :             nReqYSize == poSrcDSToTest->GetRasterYSize() &&
    3557                 :             nJPEGQuality < 0)
    3558                 :         {
    3559               2 :             VSILFILE* fpSrc = VSIFOpenL(poSrcDSToTest->GetDescription(), "rb");
    3560               2 :             if (fpSrc != NULL)
    3561                 :             {
    3562               2 :                 CPLDebug("PDF", "Copying directly original JPEG file");
    3563                 : 
    3564               2 :                 VSIFSeekL(fpSrc, 0, SEEK_END);
    3565               2 :                 int nLength = (int)VSIFTellL(fpSrc);
    3566               2 :                 VSIFSeekL(fpSrc, 0, SEEK_SET);
    3567                 : 
    3568               2 :                 int nImageId = AllocNewObject();
    3569                 : 
    3570               2 :                 StartObj(nImageId);
    3571                 : 
    3572               2 :                 GDALPDFDictionaryRW oDict;
    3573                 :                 oDict.Add("Length", nLength)
    3574                 :                      .Add("Type", GDALPDFObjectRW::CreateName("XObject"))
    3575                 :                      .Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode"))
    3576                 :                      .Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    3577                 :                      .Add("Width", nReqXSize)
    3578                 :                      .Add("Height", nReqYSize)
    3579                 :                      .Add("ColorSpace",
    3580                 :                         (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
    3581                 :                                                 GDALPDFObjectRW::CreateName("DeviceRGB"))
    3582               2 :                      .Add("BitsPerComponent", 8);
    3583               2 :                 VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3584               2 :                 VSIFPrintfL(fp, "stream\n");
    3585                 : 
    3586                 :                 GByte abyBuffer[1024];
    3587              12 :                 for(int i=0;i<nLength;i += 1024)
    3588                 :                 {
    3589              10 :                     int nRead = (int) VSIFReadL(abyBuffer, 1, 1024, fpSrc);
    3590              10 :                     if ((int)VSIFWriteL(abyBuffer, 1, nRead, fp) != nRead)
    3591                 :                     {
    3592               0 :                         eErr = CE_Failure;
    3593               0 :                         break;
    3594                 :                     }
    3595                 : 
    3596              10 :                     if( eErr == CE_None && pfnProgress != NULL
    3597                 :                         && !pfnProgress( (i + nRead) / (double)nLength,
    3598                 :                                         NULL, pProgressData ) )
    3599                 :                     {
    3600                 :                         CPLError( CE_Failure, CPLE_UserInterrupt,
    3601               0 :                                 "User terminated CreateCopy()" );
    3602               0 :                         eErr = CE_Failure;
    3603               0 :                         break;
    3604                 :                     }
    3605                 :                 }
    3606                 : 
    3607               2 :                 VSIFPrintfL(fp, "\nendstream\n");
    3608                 : 
    3609               2 :                 EndObj();
    3610                 : 
    3611               2 :                 VSIFCloseL(fpSrc);
    3612                 : 
    3613               2 :                 return eErr == CE_None ? nImageId : 0;
    3614                 :             }
    3615                 :         }
    3616                 : 
    3617             119 :         eCompressMethod = COMPRESS_DEFLATE;
    3618                 :     }
    3619                 : 
    3620             130 :     int nMaskId = 0;
    3621             130 :     if (nBands == 4)
    3622                 :     {
    3623                 :         nMaskId = WriteMask(poSrcDS,
    3624                 :                             nXOff, nYOff, nReqXSize, nReqYSize,
    3625              63 :                             eCompressMethod);
    3626                 :     }
    3627                 : 
    3628             130 :     if( nReqXSize == poSrcDS->GetRasterXSize() &&
    3629                 :         nReqYSize == poSrcDS->GetRasterYSize() &&
    3630                 :         nBands != 4)
    3631                 :     {
    3632              46 :         poBlockSrcDS = poSrcDS;
    3633                 :     }
    3634                 :     else
    3635                 :     {
    3636              84 :         if (nBands == 4)
    3637              63 :             nBands = 3;
    3638                 : 
    3639              84 :         GDALDriverH hMemDriver = GDALGetDriverByName("MEM");
    3640              84 :         if( hMemDriver == NULL )
    3641               0 :             return 0;
    3642                 : 
    3643                 :         hMemDS = GDALCreate(hMemDriver, "MEM:::",
    3644                 :                             nReqXSize, nReqYSize, 0,
    3645              84 :                             GDT_Byte, NULL);
    3646              84 :         if (hMemDS == NULL)
    3647               0 :             return 0;
    3648                 : 
    3649                 :         pabyMEMDSBuffer =
    3650              84 :             (GByte*)VSIMalloc3(nReqXSize, nReqYSize, nBands);
    3651              84 :         if (pabyMEMDSBuffer == NULL)
    3652                 :         {
    3653               0 :             GDALClose(hMemDS);
    3654               0 :             return 0;
    3655                 :         }
    3656                 : 
    3657                 :         eErr = poSrcDS->RasterIO(GF_Read,
    3658                 :                                 nXOff, nYOff,
    3659                 :                                 nReqXSize, nReqYSize,
    3660                 :                                 pabyMEMDSBuffer, nReqXSize, nReqYSize,
    3661                 :                                 GDT_Byte, nBands, NULL,
    3662              84 :                                 0, 0, 0);
    3663                 : 
    3664              84 :         if( eErr != CE_None )
    3665                 :         {
    3666               0 :             CPLFree(pabyMEMDSBuffer);
    3667               0 :             GDALClose(hMemDS);
    3668               0 :             return 0;
    3669                 :         }
    3670                 : 
    3671                 :         int iBand;
    3672             294 :         for(iBand = 0; iBand < nBands; iBand ++)
    3673                 :         {
    3674             210 :             char** papszMEMDSOptions = NULL;
    3675                 :             char szTmp[64];
    3676             210 :             memset(szTmp, 0, sizeof(szTmp));
    3677                 :             CPLPrintPointer(szTmp,
    3678             210 :                             pabyMEMDSBuffer + iBand * nReqXSize * nReqYSize, sizeof(szTmp));
    3679             210 :             papszMEMDSOptions = CSLSetNameValue(papszMEMDSOptions, "DATAPOINTER", szTmp);
    3680             210 :             GDALAddBand(hMemDS, GDT_Byte, papszMEMDSOptions);
    3681             210 :             CSLDestroy(papszMEMDSOptions);
    3682                 :         }
    3683                 : 
    3684              84 :         poBlockSrcDS = (GDALDataset*) hMemDS;
    3685                 :     }
    3686                 : 
    3687             130 :     int nImageId = AllocNewObject();
    3688             130 :     int nImageLengthId = AllocNewObject();
    3689                 : 
    3690             130 :     int nMeasureId = 0;
    3691             130 :     if( CSLTestBoolean(CPLGetConfigOption("GDAL_PDF_WRITE_GEOREF_ON_IMAGE", "FALSE")) &&
    3692                 :         nReqXSize == poSrcDS->GetRasterXSize() &&
    3693                 :         nReqYSize == poSrcDS->GetRasterYSize() )
    3694                 :     {
    3695               2 :         PDFMargins sMargins = {0, 0, 0, 0};
    3696               2 :         nMeasureId = WriteSRS_ISO32000(poSrcDS, 1, NULL, &sMargins, FALSE);
    3697                 :     }
    3698                 : 
    3699             130 :     StartObj(nImageId);
    3700                 : 
    3701             130 :     GDALPDFDictionaryRW oDict;
    3702                 :     oDict.Add("Length", nImageLengthId, 0)
    3703             130 :          .Add("Type", GDALPDFObjectRW::CreateName("XObject"));
    3704                 : 
    3705             130 :     if( eCompressMethod == COMPRESS_DEFLATE )
    3706                 :     {
    3707             121 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    3708             121 :         if( nPredictor == 2 )
    3709                 :             oDict.Add("DecodeParms", &((new GDALPDFDictionaryRW())
    3710                 :                                   ->Add("Predictor", 2)
    3711                 :                                    .Add("Colors", nBands)
    3712               2 :                                    .Add("Columns", nReqXSize)));
    3713                 :     }
    3714               9 :     else if( eCompressMethod == COMPRESS_JPEG )
    3715                 :     {
    3716               3 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode"));
    3717                 :     }
    3718               6 :     else if( eCompressMethod == COMPRESS_JPEG2000 )
    3719                 :     {
    3720               5 :         oDict.Add("Filter", GDALPDFObjectRW::CreateName("JPXDecode"));
    3721                 :     }
    3722                 : 
    3723                 :     oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image"))
    3724                 :          .Add("Width", nReqXSize)
    3725                 :          .Add("Height", nReqYSize)
    3726                 :          .Add("ColorSpace",
    3727                 :               (nColorTableId != 0) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) :
    3728                 :               (nBands == 1) ?        GDALPDFObjectRW::CreateName("DeviceGray") :
    3729                 :                                      GDALPDFObjectRW::CreateName("DeviceRGB"))
    3730             130 :          .Add("BitsPerComponent", 8);
    3731             130 :     if( nMaskId )
    3732                 :     {
    3733              63 :         oDict.Add("SMask", nMaskId, 0);
    3734                 :     }
    3735             130 :     if( nMeasureId )
    3736                 :     {
    3737               2 :         oDict.Add("Measure", nMeasureId, 0);
    3738                 :     }
    3739                 : 
    3740             130 :     VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3741             130 :     VSIFPrintfL(fp, "stream\n");
    3742                 : 
    3743             130 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    3744                 : 
    3745             138 :     if( eCompressMethod == COMPRESS_JPEG ||
    3746                 :         eCompressMethod == COMPRESS_JPEG2000 )
    3747                 :     {
    3748               8 :         GDALDriver* poJPEGDriver = NULL;
    3749                 :         char szTmp[64];
    3750               8 :         char** papszOptions = NULL;
    3751                 : 
    3752               8 :         if( eCompressMethod == COMPRESS_JPEG )
    3753                 :         {
    3754               3 :             poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG");
    3755               3 :             if (poJPEGDriver != NULL && nJPEGQuality > 0)
    3756               0 :                 papszOptions = CSLAddString(papszOptions, CPLSPrintf("QUALITY=%d", nJPEGQuality));
    3757               3 :             sprintf(szTmp, "/vsimem/pdftemp/%p.jpg", this);
    3758                 :         }
    3759                 :         else
    3760                 :         {
    3761               5 :             if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2KAK"))
    3762               1 :                 poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2KAK");
    3763               5 :             if (poJPEGDriver == NULL)
    3764                 :             {
    3765               5 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2ECW"))
    3766                 :                 {
    3767               3 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2ECW");
    3768               6 :                     if( poJPEGDriver &&
    3769               3 :                         poJPEGDriver->GetMetadataItem(GDAL_DMD_CREATIONDATATYPES) == NULL )
    3770                 :                     {
    3771               0 :                         poJPEGDriver = NULL;
    3772                 :                     }
    3773                 :                 }
    3774               5 :                 if (poJPEGDriver)
    3775                 :                 {
    3776               3 :                     papszOptions = CSLAddString(papszOptions, "PROFILE=NPJE");
    3777               3 :                     papszOptions = CSLAddString(papszOptions, "LAYERS=1");
    3778               3 :                     papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF");
    3779               3 :                     papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF");
    3780                 :                 }
    3781                 :             }
    3782               5 :             if (poJPEGDriver == NULL)
    3783                 :             {
    3784               2 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2OpenJPEG"))
    3785               1 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2OpenJPEG");
    3786               2 :                 if (poJPEGDriver)
    3787                 :                 {
    3788               1 :                     papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF");
    3789               1 :                     papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF");
    3790                 :                 }
    3791                 :             }
    3792               5 :             if (poJPEGDriver == NULL)
    3793                 :             {
    3794               1 :                 if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JPEG2000"))
    3795               1 :                     poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG2000");
    3796                 :             }
    3797               5 :             sprintf(szTmp, "/vsimem/pdftemp/%p.jp2", this);
    3798                 :         }
    3799                 : 
    3800               8 :         if( poJPEGDriver == NULL )
    3801                 :         {
    3802                 :             CPLError(CE_Failure, CPLE_NotSupported,
    3803                 :                      "No %s driver found",
    3804               0 :                      ( eCompressMethod == COMPRESS_JPEG ) ? "JPEG" : "JPEG2000");
    3805               0 :             eErr = CE_Failure;
    3806               0 :             goto end;
    3807                 :         }
    3808                 : 
    3809               8 :         GDALDataset* poJPEGDS = NULL;
    3810                 : 
    3811                 :         poJPEGDS = poJPEGDriver->CreateCopy(szTmp, poBlockSrcDS,
    3812                 :                                             FALSE, papszOptions,
    3813               8 :                                             pfnProgress, pProgressData);
    3814                 : 
    3815               8 :         CSLDestroy(papszOptions);
    3816               8 :         if( poJPEGDS == NULL )
    3817                 :         {
    3818               0 :             eErr = CE_Failure;
    3819               0 :             goto end;
    3820                 :         }
    3821                 : 
    3822               8 :         GDALClose(poJPEGDS);
    3823                 : 
    3824               8 :         vsi_l_offset nJPEGDataSize = 0;
    3825               8 :         GByte* pabyJPEGData = VSIGetMemFileBuffer(szTmp, &nJPEGDataSize, TRUE);
    3826               8 :         VSIFWriteL(pabyJPEGData, nJPEGDataSize, 1, fp);
    3827               8 :         CPLFree(pabyJPEGData);
    3828                 :     }
    3829                 :     else
    3830                 :     {
    3831             122 :         VSILFILE* fpGZip = NULL;
    3832             122 :         VSILFILE* fpBack = fp;
    3833             122 :         if( eCompressMethod == COMPRESS_DEFLATE )
    3834                 :         {
    3835             121 :             fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    3836             121 :             fp = fpGZip;
    3837                 :         }
    3838                 : 
    3839             122 :         GByte* pabyLine = (GByte*)CPLMalloc(nReqXSize * nBands);
    3840            8465 :         for(int iLine = 0; iLine < nReqYSize; iLine ++)
    3841                 :         {
    3842                 :             /* Get pixel interleaved data */
    3843                 :             eErr = poBlockSrcDS->RasterIO(GF_Read,
    3844                 :                                           0, iLine, nReqXSize, 1,
    3845                 :                                           pabyLine, nReqXSize, 1, GDT_Byte,
    3846            8343 :                                           nBands, NULL, nBands, 0, 1);
    3847            8343 :             if( eErr != CE_None )
    3848               0 :                 break;
    3849                 : 
    3850                 :             /* Apply predictor if needed */
    3851            8343 :             if( nPredictor == 2 )
    3852                 :             {
    3853             562 :                 if( nBands == 1 )
    3854                 :                 {
    3855             512 :                     int nPrevValue = pabyLine[0];
    3856          262144 :                     for(int iPixel = 1; iPixel < nReqXSize; iPixel ++)
    3857                 :                     {
    3858          261632 :                         int nCurValue = pabyLine[iPixel];
    3859          261632 :                         pabyLine[iPixel] = (GByte) (nCurValue - nPrevValue);
    3860          261632 :                         nPrevValue = nCurValue;
    3861                 :                     }
    3862                 :                 }
    3863              50 :                 else if( nBands == 3 )
    3864                 :                 {
    3865              50 :                     int nPrevValueR = pabyLine[0];
    3866              50 :                     int nPrevValueG = pabyLine[1];
    3867              50 :                     int nPrevValueB = pabyLine[2];
    3868            2500 :                     for(int iPixel = 1; iPixel < nReqXSize; iPixel ++)
    3869                 :                     {
    3870            2450 :                         int nCurValueR = pabyLine[3 * iPixel + 0];
    3871            2450 :                         int nCurValueG = pabyLine[3 * iPixel + 1];
    3872            2450 :                         int nCurValueB = pabyLine[3 * iPixel + 2];
    3873            2450 :                         pabyLine[3 * iPixel + 0] = (GByte) (nCurValueR - nPrevValueR);
    3874            2450 :                         pabyLine[3 * iPixel + 1] = (GByte) (nCurValueG - nPrevValueG);
    3875            2450 :                         pabyLine[3 * iPixel + 2] = (GByte) (nCurValueB - nPrevValueB);
    3876            2450 :                         nPrevValueR = nCurValueR;
    3877            2450 :                         nPrevValueG = nCurValueG;
    3878            2450 :                         nPrevValueB = nCurValueB;
    3879                 :                     }
    3880                 :                 }
    3881                 :             }
    3882                 : 
    3883            8343 :             if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, fp) != 1 )
    3884                 :             {
    3885               0 :                 eErr = CE_Failure;
    3886               0 :                 break;
    3887                 :             }
    3888                 : 
    3889            8343 :             if( eErr == CE_None && pfnProgress != NULL
    3890                 :                 && !pfnProgress( (iLine+1) / (double)nReqYSize,
    3891                 :                                 NULL, pProgressData ) )
    3892                 :             {
    3893                 :                 CPLError( CE_Failure, CPLE_UserInterrupt,
    3894               0 :                         "User terminated CreateCopy()" );
    3895               0 :                 eErr = CE_Failure;
    3896               0 :                 break;
    3897                 :             }
    3898                 :         }
    3899                 : 
    3900             122 :         CPLFree(pabyLine);
    3901                 : 
    3902             122 :         if (fpGZip)
    3903             121 :             VSIFCloseL(fpGZip);
    3904             122 :         fp = fpBack;
    3905                 :     }
    3906                 : 
    3907                 : end:
    3908             130 :     CPLFree(pabyMEMDSBuffer);
    3909             130 :     pabyMEMDSBuffer = NULL;
    3910             130 :     if( hMemDS != NULL )
    3911                 :     {
    3912              84 :         GDALClose(hMemDS);
    3913              84 :         hMemDS = NULL;
    3914                 :     }
    3915                 : 
    3916             130 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    3917                 :     VSIFPrintfL(fp,
    3918                 :                 "\n"
    3919             130 :                 "endstream\n");
    3920             130 :     EndObj();
    3921                 : 
    3922             130 :     StartObj(nImageLengthId);
    3923                 :     VSIFPrintfL(fp,
    3924                 :                 "   %ld\n",
    3925             130 :                 (long)(nStreamEnd - nStreamStart));
    3926             130 :     EndObj();
    3927                 : 
    3928             130 :     return eErr == CE_None ? nImageId : 0;
    3929                 : }
    3930                 : 
    3931                 : /************************************************************************/
    3932                 : /*                          WriteJavascript()                           */
    3933                 : /************************************************************************/
    3934                 : 
    3935               1 : int GDALPDFWriter::WriteJavascript(const char* pszJavascript)
    3936                 : {
    3937               1 :     int nJSId = AllocNewObject();
    3938               1 :     int nJSLengthId = AllocNewObject();
    3939               1 :     StartObj(nJSId);
    3940                 :     {
    3941               1 :         GDALPDFDictionaryRW oDict;
    3942               1 :         oDict.Add("Length", nJSLengthId, 0);
    3943               1 :         if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    3944                 :         {
    3945               1 :             oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode"));
    3946                 :         }
    3947               1 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3948                 :     }
    3949               1 :     VSIFPrintfL(fp, "stream\n");
    3950               1 :     vsi_l_offset nStreamStart = VSIFTellL(fp);
    3951                 : 
    3952               1 :     VSILFILE* fpGZip = NULL;
    3953               1 :     VSILFILE* fpBack = fp;
    3954               1 :     if( oPageContext.eStreamCompressMethod != COMPRESS_NONE )
    3955                 :     {
    3956               1 :         fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE );
    3957               1 :         fp = fpGZip;
    3958                 :     }
    3959                 : 
    3960               1 :     VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, fp);
    3961                 : 
    3962               1 :     if (fpGZip)
    3963               1 :         VSIFCloseL(fpGZip);
    3964               1 :     fp = fpBack;
    3965                 : 
    3966               1 :     vsi_l_offset nStreamEnd = VSIFTellL(fp);
    3967                 :     VSIFPrintfL(fp,
    3968                 :                 "\n"
    3969               1 :                 "endstream\n");
    3970               1 :     EndObj();
    3971                 : 
    3972               1 :     StartObj(nJSLengthId);
    3973                 :     VSIFPrintfL(fp,
    3974                 :                 "   %ld\n",
    3975               1 :                 (long)(nStreamEnd - nStreamStart));
    3976               1 :     EndObj();
    3977                 : 
    3978               1 :     nNamesId = AllocNewObject();
    3979               1 :     StartObj(nNamesId);
    3980                 :     {
    3981               1 :         GDALPDFDictionaryRW oDict;
    3982               1 :         GDALPDFDictionaryRW* poJavaScriptDict = new GDALPDFDictionaryRW();
    3983               1 :         oDict.Add("JavaScript", poJavaScriptDict);
    3984                 : 
    3985               1 :         GDALPDFArrayRW* poNamesArray = new GDALPDFArrayRW();
    3986               1 :         poJavaScriptDict->Add("Names", poNamesArray);
    3987                 : 
    3988               1 :         poNamesArray->Add("GDAL");
    3989                 : 
    3990               1 :         GDALPDFDictionaryRW* poJSDict = new GDALPDFDictionaryRW();
    3991               1 :         poNamesArray->Add(poJSDict);
    3992                 : 
    3993               1 :         poJSDict->Add("JS", nJSId, 0);
    3994               1 :         poJSDict->Add("S", GDALPDFObjectRW::CreateName("JavaScript"));
    3995                 : 
    3996               1 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    3997                 :     }
    3998               1 :     EndObj();
    3999                 : 
    4000               1 :     return nNamesId;
    4001                 : }
    4002                 : 
    4003                 : /************************************************************************/
    4004                 : /*                        WriteJavascriptFile()                         */
    4005                 : /************************************************************************/
    4006                 : 
    4007               0 : int GDALPDFWriter::WriteJavascriptFile(const char* pszJavascriptFile)
    4008                 : {
    4009               0 :     int nRet = 0;
    4010               0 :     char* pszJavascriptToFree = (char*)CPLMalloc(65536);
    4011               0 :     VSILFILE* fpJS = VSIFOpenL(pszJavascriptFile, "rb");
    4012               0 :     if( fpJS != NULL )
    4013                 :     {
    4014               0 :         int nRead = (int)VSIFReadL(pszJavascriptToFree, 1, 65536, fpJS);
    4015               0 :         if( nRead < 65536 )
    4016                 :         {
    4017               0 :             pszJavascriptToFree[nRead] = '\0';
    4018               0 :             nRet = WriteJavascript(pszJavascriptToFree);
    4019                 :         }
    4020               0 :         VSIFCloseL(fpJS);
    4021                 :     }
    4022               0 :     CPLFree(pszJavascriptToFree);
    4023               0 :     return nRet;
    4024                 : }
    4025                 : /************************************************************************/
    4026                 : /*                              WritePages()                            */
    4027                 : /************************************************************************/
    4028                 : 
    4029              53 : void GDALPDFWriter::WritePages()
    4030                 : {
    4031              53 :     StartObj(nPageResourceId);
    4032                 :     {
    4033              53 :         GDALPDFDictionaryRW oDict;
    4034              53 :         GDALPDFArrayRW* poKids = new GDALPDFArrayRW();
    4035                 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages"))
    4036                 :              .Add("Count", (int)asPageId.size())
    4037             106 :              .Add("Kids", poKids);
    4038                 : 
    4039             106 :         for(size_t i=0;i<asPageId.size();i++)
    4040              53 :             poKids->Add(asPageId[i], 0);
    4041                 : 
    4042              53 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    4043                 :     }
    4044              53 :     EndObj();
    4045                 : 
    4046              53 :     StartObj(nCatalogId);
    4047                 :     {
    4048              53 :         GDALPDFDictionaryRW oDict;
    4049                 :         oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog"))
    4050             106 :              .Add("Pages", nPageResourceId, 0);
    4051              53 :         if (nXMPId)
    4052               1 :             oDict.Add("Metadata", nXMPId, 0);
    4053              53 :         if (asOCGs.size())
    4054                 :         {
    4055               6 :             GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW();
    4056               6 :             oDict.Add("OCProperties", poDictOCProperties);
    4057                 : 
    4058               6 :             GDALPDFDictionaryRW* poDictD = new GDALPDFDictionaryRW();
    4059               6 :             poDictOCProperties->Add("D", poDictD);
    4060                 : 
    4061                 :             /* Build "Order" array of D dict */
    4062               6 :             GDALPDFArrayRW* poArrayOrder = new GDALPDFArrayRW();
    4063                 :             size_t i;
    4064              14 :             for(i=0;i<asOCGs.size();i++)
    4065                 :             {
    4066               8 :                 poArrayOrder->Add(asOCGs[i].nId, 0);
    4067               8 :                 if (i + 1 < asOCGs.size() && asOCGs[i+1].nParentId == asOCGs[i].nId)
    4068                 :                 {
    4069               3 :                     GDALPDFArrayRW* poSubArrayOrder = new GDALPDFArrayRW();
    4070               6 :                     poSubArrayOrder->Add(asOCGs[i+1].nId, 0);
    4071               3 :                     poArrayOrder->Add(poSubArrayOrder);
    4072               3 :                     i ++;
    4073                 :                 }
    4074                 :             }
    4075               6 :             poDictD->Add("Order", poArrayOrder);
    4076                 : 
    4077                 :             /* Build "OFF" array of D dict */
    4078               6 :             if( osOffLayers.size() )
    4079                 :             {
    4080               1 :                 GDALPDFArrayRW* poArrayOFF = new GDALPDFArrayRW();
    4081               2 :                 char** papszTokens = CSLTokenizeString2(osOffLayers, ",", 0);
    4082               2 :                 for(int i=0; papszTokens[i] != NULL; i++)
    4083                 :                 {
    4084                 :                     size_t j;
    4085               1 :                     int bFound = FALSE;
    4086               3 :                     for(j=0;j<asOCGs.size();j++)
    4087                 :                     {
    4088               2 :                         if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0)
    4089                 :                         {
    4090               1 :                             poArrayOFF->Add(asOCGs[j].nId, 0);
    4091               1 :                             bFound = TRUE;
    4092                 :                         }
    4093               2 :                         if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId)
    4094                 :                         {
    4095               0 :                             j ++;
    4096                 :                         }
    4097                 :                     }
    4098               1 :                     if( !bFound )
    4099                 :                     {
    4100                 :                         CPLError(CE_Warning, CPLE_AppDefined,
    4101                 :                                  "Unknown layer name (%s) specified in OFF_LAYERS",
    4102               0 :                                  papszTokens[i]);
    4103                 :                     }
    4104                 :                 }
    4105               1 :                 CSLDestroy(papszTokens);
    4106                 : 
    4107               1 :                 poDictD->Add("OFF", poArrayOFF);
    4108                 :             }
    4109                 : 
    4110                 :             /* Build "RBGroups" array of D dict */
    4111               6 :             if( osExclusiveLayers.size() )
    4112                 :             {
    4113               1 :                 GDALPDFArrayRW* poArrayRBGroups = new GDALPDFArrayRW();
    4114               2 :                 char** papszTokens = CSLTokenizeString2(osExclusiveLayers, ",", 0);
    4115               3 :                 for(int i=0; papszTokens[i] != NULL; i++)
    4116                 :                 {
    4117                 :                     size_t j;
    4118               2 :                     int bFound = FALSE;
    4119               6 :                     for(j=0;j<asOCGs.size();j++)
    4120                 :                     {
    4121               4 :                         if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0)
    4122                 :                         {
    4123               2 :                             poArrayRBGroups->Add(asOCGs[j].nId, 0);
    4124               2 :                             bFound = TRUE;
    4125                 :                         }
    4126               4 :                         if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId)
    4127                 :                         {
    4128               0 :                             j ++;
    4129                 :                         }
    4130                 :                     }
    4131               2 :                     if( !bFound )
    4132                 :                     {
    4133                 :                         CPLError(CE_Warning, CPLE_AppDefined,
    4134                 :                                     "Unknown layer name (%s) specified in EXCLUSIVE_LAYERS",
    4135               0 :                                     papszTokens[i]);
    4136                 :                     }
    4137                 :                 }
    4138               1 :                 CSLDestroy(papszTokens);
    4139                 : 
    4140               1 :                 if( poArrayRBGroups->GetLength() )
    4141                 :                 {
    4142               1 :                     GDALPDFArrayRW* poMainArrayRBGroups = new GDALPDFArrayRW();
    4143               1 :                     poMainArrayRBGroups->Add(poArrayRBGroups);
    4144               1 :                     poDictD->Add("RBGroups", poMainArrayRBGroups);
    4145                 :                 }
    4146                 :                 else
    4147               0 :                     delete poArrayRBGroups;
    4148                 :             }
    4149                 : 
    4150               6 :             GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW();
    4151              17 :             for(i=0;i<asOCGs.size();i++)
    4152              11 :                 poArrayOGCs->Add(asOCGs[i].nId, 0);
    4153               6 :             poDictOCProperties->Add("OCGs", poArrayOGCs);
    4154                 :         }
    4155                 : 
    4156              53 :         if (nStructTreeRootId)
    4157                 :         {
    4158               3 :             GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW();
    4159               3 :             oDict.Add("MarkInfo", poDictMarkInfo);
    4160               3 :             poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE));
    4161                 : 
    4162               3 :             oDict.Add("StructTreeRoot", nStructTreeRootId, 0);
    4163                 :         }
    4164                 : 
    4165              53 :         if (nNamesId)
    4166               1 :             oDict.Add("Names", nNamesId, 0);
    4167                 : 
    4168              53 :         VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str());
    4169                 :     }
    4170              53 :     EndObj();
    4171              53 : }
    4172                 : 
    4173                 : /************************************************************************/
    4174                 : /*                        GDALPDFGetJPEGQuality()                       */
    4175                 : /************************************************************************/
    4176                 : 
    4177              56 : static int GDALPDFGetJPEGQuality(char** papszOptions)
    4178                 : {
    4179              56 :     int nJpegQuality = -1;
    4180              56 :     const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" );
    4181              56 :     if( pszValue  != NULL )
    4182                 :     {
    4183               0 :         nJpegQuality = atoi( pszValue );
    4184               0 :         if (!(nJpegQuality >= 1 && nJpegQuality <= 100))
    4185                 :         {
    4186                 :             CPLError( CE_Warning, CPLE_IllegalArg,
    4187                 :                     "JPEG_QUALITY=%s value not recognised, ignoring.",
    4188               0 :                     pszValue );
    4189               0 :             nJpegQuality = -1;
    4190                 :         }
    4191                 :     }
    4192              56 :     return nJpegQuality;
    4193                 : }
    4194                 : 
    4195                 : /************************************************************************/
    4196                 : /*                         GDALPDFClippingDataset                       */
    4197                 : /************************************************************************/
    4198                 : 
    4199                 : class GDALPDFClippingDataset: public GDALDataset
    4200               1 : {
    4201                 :         GDALDataset* poSrcDS;
    4202                 :         double adfGeoTransform[6];
    4203                 : 
    4204                 :     public:
    4205               1 :         GDALPDFClippingDataset(GDALDataset* poSrcDS, double adfClippingExtent[4]) : poSrcDS(poSrcDS)
    4206                 :         {
    4207                 :             double adfSrcGeoTransform[6];
    4208               1 :             poSrcDS->GetGeoTransform(adfSrcGeoTransform);
    4209               1 :             adfGeoTransform[0] = adfClippingExtent[0];
    4210               1 :             adfGeoTransform[1] = adfSrcGeoTransform[1];
    4211               1 :             adfGeoTransform[2] = 0.0;
    4212               1 :             adfGeoTransform[3] = adfSrcGeoTransform[5] < 0 ? adfClippingExtent[3] : adfClippingExtent[1];
    4213               1 :             adfGeoTransform[4] = 0.0;
    4214               1 :             adfGeoTransform[5] = adfSrcGeoTransform[5];
    4215               1 :             nRasterXSize = (adfClippingExtent[2] - adfClippingExtent[0]) / adfSrcGeoTransform[1];
    4216               1 :             nRasterYSize = (adfClippingExtent[3] - adfClippingExtent[1]) / fabs(adfSrcGeoTransform[5]);
    4217               1 :         }
    4218                 : 
    4219               3 :         virtual CPLErr GetGeoTransform( double * padfGeoTransform )
    4220                 :         {
    4221               3 :             memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double));
    4222               3 :             return CE_None;
    4223                 :         }
    4224                 : 
    4225               1 :         virtual const char* GetProjectionRef()
    4226                 :         {
    4227               1 :             return poSrcDS->GetProjectionRef();
    4228                 :         }
    4229                 : };
    4230                 : 
    4231                 : /************************************************************************/
    4232                 : /*                          GDALPDFCreateCopy()                         */
    4233                 : /************************************************************************/
    4234                 : 
    4235              69 : GDALDataset *GDALPDFCreateCopy( const char * pszFilename,
    4236                 :                                 GDALDataset *poSrcDS,
    4237                 :                                 int bStrict,
    4238                 :                                 char **papszOptions,
    4239                 :                                 GDALProgressFunc pfnProgress,
    4240                 :                                 void * pProgressData )
    4241                 : {
    4242              69 :     int  nBands = poSrcDS->GetRasterCount();
    4243              69 :     int  nWidth = poSrcDS->GetRasterXSize();
    4244              69 :     int  nHeight = poSrcDS->GetRasterYSize();
    4245                 : 
    4246              69 :     if( !pfnProgress( 0.0, NULL, pProgressData ) )
    4247               0 :         return NULL;
    4248                 : 
    4249                 : /* -------------------------------------------------------------------- */
    4250                 : /*      Some some rudimentary checks                                    */
    4251                 : /* -------------------------------------------------------------------- */
    4252              69 :     if( nBands != 1 && nBands != 3 && nBands != 4 )
    4253                 :     {
    4254                 :         CPLError( CE_Failure, CPLE_NotSupported,
    4255                 :                   "PDF driver doesn't support %d bands.  Must be 1 (grey or with color table), "
    4256               3 :                   "3 (RGB) or 4 bands.\n", nBands );
    4257                 : 
    4258               3 :         return NULL;
    4259                 :     }
    4260                 : 
    4261              66 :     GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    4262              66 :     if( eDT != GDT_Byte )
    4263                 :     {
    4264                 :         CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4265                 :                   "PDF driver doesn't support data type %s. "
    4266                 :                   "Only eight bit byte bands supported.\n",
    4267                 :                   GDALGetDataTypeName(
    4268              10 :                       poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
    4269                 : 
    4270              10 :         if (bStrict)
    4271              10 :             return NULL;
    4272                 :     }
    4273                 : 
    4274                 : /* -------------------------------------------------------------------- */
    4275                 : /*     Read options.                                                    */
    4276                 : /* -------------------------------------------------------------------- */
    4277              56 :     PDFCompressMethod eCompressMethod = COMPRESS_DEFAULT;
    4278              56 :     const char* pszCompressMethod = CSLFetchNameValue(papszOptions, "COMPRESS");
    4279              56 :     if (pszCompressMethod)
    4280                 :     {
    4281               9 :         if( EQUAL(pszCompressMethod, "NONE") )
    4282               1 :             eCompressMethod = COMPRESS_NONE;
    4283               8 :         else if( EQUAL(pszCompressMethod, "DEFLATE") )
    4284               0 :             eCompressMethod = COMPRESS_DEFLATE;
    4285               8 :         else if( EQUAL(pszCompressMethod, "JPEG") )
    4286               3 :             eCompressMethod = COMPRESS_JPEG;
    4287               5 :         else if( EQUAL(pszCompressMethod, "JPEG2000") )
    4288               5 :             eCompressMethod = COMPRESS_JPEG2000;
    4289                 :         else
    4290                 :         {
    4291                 :             CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4292               0 :                     "Unsupported value for COMPRESS.");
    4293                 : 
    4294               0 :             if (bStrict)
    4295               0 :                 return NULL;
    4296                 :         }
    4297                 :     }
    4298                 : 
    4299              56 :     PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE;
    4300              56 :     const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS");
    4301              56 :     if (pszStreamCompressMethod)
    4302                 :     {
    4303               2 :         if( EQUAL(pszStreamCompressMethod, "NONE") )
    4304               2 :             eStreamCompressMethod = COMPRESS_NONE;
    4305               0 :         else if( EQUAL(pszStreamCompressMethod, "DEFLATE") )
    4306               0 :             eStreamCompressMethod = COMPRESS_DEFLATE;
    4307                 :         else
    4308                 :         {
    4309                 :             CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4310               0 :                     "Unsupported value for STREAM_COMPRESS.");
    4311                 : 
    4312               0 :             if (bStrict)
    4313               0 :                 return NULL;
    4314                 :         }
    4315                 :     }
    4316                 : 
    4317              98 :     if (nBands == 1 &&
    4318              42 :         poSrcDS->GetRasterBand(1)->GetColorTable() != NULL &&
    4319                 :         (eCompressMethod == COMPRESS_JPEG || eCompressMethod == COMPRESS_JPEG2000))
    4320                 :     {
    4321                 :         CPLError( CE_Warning, CPLE_AppDefined,
    4322                 :                   "The source raster band has a color table, which is not appropriate with JPEG or JPEG2000 compression.\n"
    4323               0 :                   "You should rather consider using color table expansion (-expand option in gdal_translate)");
    4324                 :     }
    4325                 : 
    4326                 : 
    4327              56 :     int nBlockXSize = nWidth;
    4328              56 :     int nBlockYSize = nHeight;
    4329                 :     const char* pszValue;
    4330                 : 
    4331              56 :     int bTiled = CSLFetchBoolean( papszOptions, "TILED", FALSE );
    4332              56 :     if( bTiled )
    4333               1 :         nBlockXSize = nBlockYSize = 256;
    4334                 : 
    4335              56 :     pszValue = CSLFetchNameValue(papszOptions, "BLOCKXSIZE");
    4336              56 :     if( pszValue != NULL )
    4337                 :     {
    4338               3 :         nBlockXSize = atoi( pszValue );
    4339               3 :         if (nBlockXSize < 0 || nBlockXSize >= nWidth)
    4340               0 :             nBlockXSize = nWidth;
    4341                 :     }
    4342                 : 
    4343              56 :     pszValue = CSLFetchNameValue(papszOptions, "BLOCKYSIZE");
    4344              56 :     if( pszValue != NULL )
    4345                 :     {
    4346               3 :         nBlockYSize = atoi( pszValue );
    4347               3 :         if (nBlockYSize < 0 || nBlockYSize >= nHeight)
    4348               0 :             nBlockYSize = nHeight;
    4349                 :     }
    4350                 : 
    4351              56 :     int nJPEGQuality = GDALPDFGetJPEGQuality(papszOptions);
    4352                 : 
    4353              56 :     const char* pszJPEG2000_DRIVER = CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER");
    4354                 : 
    4355                 :     const char* pszGEO_ENCODING =
    4356              56 :         CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000");
    4357                 : 
    4358              56 :     const char* pszXMP = CSLFetchNameValue(papszOptions, "XMP");
    4359                 : 
    4360              56 :     double dfDPI = atof(CSLFetchNameValueDef(papszOptions, "DPI", "72"));
    4361              56 :     if (dfDPI < 72.0)
    4362               0 :         dfDPI = 72.0;
    4363                 : 
    4364              56 :     const char* pszPredictor = CSLFetchNameValue(papszOptions, "PREDICTOR");
    4365              56 :     int nPredictor = 1;
    4366              56 :     if (pszPredictor)
    4367                 :     {
    4368               2 :         if (eCompressMethod == COMPRESS_DEFAULT)
    4369               2 :             eCompressMethod = COMPRESS_DEFLATE;
    4370                 : 
    4371               2 :         if (eCompressMethod != COMPRESS_DEFLATE)
    4372                 :         {
    4373                 :             CPLError(CE_Warning, CPLE_NotSupported,
    4374               0 :                      "PREDICTOR option is only taken into account for DEFLATE compression");
    4375                 :         }
    4376                 :         else
    4377                 :         {
    4378               2 :             nPredictor = atoi(pszPredictor);
    4379               2 :             if (nPredictor != 1 && nPredictor != 2)
    4380                 :             {
    4381                 :                 CPLError(CE_Warning, CPLE_NotSupported,
    4382               0 :                                     "Supported PREDICTOR values are 1 or 2");
    4383               0 :                 nPredictor = 1;
    4384                 :             }
    4385                 :         }
    4386                 :     }
    4387                 : 
    4388              56 :     const char* pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE");
    4389                 : 
    4390              56 :     int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0"));
    4391                 : 
    4392                 :     PDFMargins sMargins;
    4393              56 :     sMargins.nLeft = nMargin;
    4394              56 :     sMargins.nRight = nMargin;
    4395              56 :     sMargins.nTop = nMargin;
    4396              56 :     sMargins.nBottom = nMargin;
    4397                 : 
    4398              56 :     const char* pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN");
    4399              56 :     if (pszLeftMargin) sMargins.nLeft = atoi(pszLeftMargin);
    4400                 : 
    4401              56 :     const char* pszRightMargin = CSLFetchNameValue(papszOptions, "RIGHT_MARGIN");
    4402              56 :     if (pszRightMargin) sMargins.nRight = atoi(pszRightMargin);
    4403                 : 
    4404              56 :     const char* pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN");
    4405              56 :     if (pszTopMargin) sMargins.nTop = atoi(pszTopMargin);
    4406                 : 
    4407              56 :     const char* pszBottomMargin = CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN");
    4408              56 :     if (pszBottomMargin) sMargins.nBottom = atoi(pszBottomMargin);
    4409                 : 
    4410              56 :     const char* pszClippingExtent = CSLFetchNameValue(papszOptions, "CLIPPING_EXTENT");
    4411              56 :     int bUseClippingExtent = FALSE;
    4412              56 :     double adfClippingExtent[4] = { 0.0, 0.0, 0.0, 0.0 };
    4413              56 :     if( pszClippingExtent != NULL )
    4414                 :     {
    4415               1 :         char** papszTokens = CSLTokenizeString2(pszClippingExtent, ",", 0);
    4416               1 :         if( CSLCount(papszTokens) == 4 )
    4417                 :         {
    4418               1 :             bUseClippingExtent = TRUE;
    4419               1 :             adfClippingExtent[0] = CPLAtof(papszTokens[0]);
    4420               1 :             adfClippingExtent[1] = CPLAtof(papszTokens[1]);
    4421               1 :             adfClippingExtent[2] = CPLAtof(papszTokens[2]);
    4422               1 :             adfClippingExtent[3] = CPLAtof(papszTokens[3]);
    4423               2 :             if( adfClippingExtent[0] > adfClippingExtent[2] ||
    4424               1 :                 adfClippingExtent[1] > adfClippingExtent[3] )
    4425                 :             {
    4426                 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4427               0 :                          "Invalid value for CLIPPING_EXTENT. Should be xmin,ymin,xmax,ymax");
    4428               0 :                 bUseClippingExtent = TRUE;
    4429                 :             }
    4430                 : 
    4431               1 :             if( bUseClippingExtent )
    4432                 :             {
    4433                 :                 double adfGeoTransform[6];
    4434               1 :                 if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None )
    4435                 :                 {
    4436               1 :                     if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
    4437                 :                     {
    4438                 :                         CPLError(CE_Warning, CPLE_AppDefined,
    4439               0 :                                 "Cannot use CLIPPING_EXTENT because main raster has a rotated geotransform");
    4440               0 :                         bUseClippingExtent = TRUE;
    4441                 :                     }
    4442                 :                 }
    4443                 :                 else
    4444                 :                 {
    4445                 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4446               0 :                                 "Cannot use CLIPPING_EXTENT because main raster has no geotransform");
    4447               0 :                     bUseClippingExtent = TRUE;
    4448                 :                 }
    4449                 :             }
    4450                 :         }
    4451               1 :         CSLDestroy(papszTokens);
    4452                 :     }
    4453                 : 
    4454              56 :     const char* pszLayerName = CSLFetchNameValue(papszOptions, "LAYER_NAME");
    4455                 : 
    4456              56 :     const char* pszExtraImages = CSLFetchNameValue(papszOptions, "EXTRA_IMAGES");
    4457              56 :     const char* pszExtraStream = CSLFetchNameValue(papszOptions, "EXTRA_STREAM");
    4458              56 :     const char* pszExtraLayerName = CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME");
    4459                 : 
    4460              56 :     const char* pszOGRDataSource = CSLFetchNameValue(papszOptions, "OGR_DATASOURCE");
    4461              56 :     const char* pszOGRDisplayField = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD");
    4462              56 :     const char* pszOGRDisplayLayerNames = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES");
    4463              56 :     const char* pszOGRLinkField = CSLFetchNameValue(papszOptions, "OGR_LINK_FIELD");
    4464              56 :     int bWriteOGRAttributes = CSLFetchBoolean(papszOptions, "OGR_WRITE_ATTRIBUTES", TRUE);
    4465                 : 
    4466              56 :     const char* pszExtraRasters = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS");
    4467              56 :     const char* pszExtraRastersLayerName = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS_LAYER_NAME");
    4468                 :     
    4469              56 :     const char* pszOffLayers = CSLFetchNameValue(papszOptions, "OFF_LAYERS");
    4470              56 :     const char* pszExclusiveLayers = CSLFetchNameValue(papszOptions, "EXCLUSIVE_LAYERS");
    4471                 : 
    4472              56 :     const char* pszJavascript = CSLFetchNameValue(papszOptions, "JAVASCRIPT");
    4473              56 :     const char* pszJavascriptFile = CSLFetchNameValue(papszOptions, "JAVASCRIPT_FILE");
    4474                 : 
    4475                 : /* -------------------------------------------------------------------- */
    4476                 : /*      Create file.                                                    */
    4477                 : /* -------------------------------------------------------------------- */
    4478              56 :     VSILFILE* fp = VSIFOpenL(pszFilename, "wb");
    4479              56 :     if( fp == NULL )
    4480                 :     {
    4481                 :         CPLError( CE_Failure, CPLE_OpenFailed,
    4482                 :                   "Unable to create PDF file %s.\n",
    4483               5 :                   pszFilename );
    4484               5 :         return NULL;
    4485                 :     }
    4486                 : 
    4487                 : 
    4488              51 :     GDALPDFWriter oWriter(fp);
    4489                 : 
    4490              51 :     GDALDataset* poClippingDS = poSrcDS;
    4491              51 :     if( bUseClippingExtent )
    4492               1 :         poClippingDS = new GDALPDFClippingDataset(poSrcDS, adfClippingExtent);
    4493                 : 
    4494              51 :     if( CSLFetchBoolean(papszOptions, "WRITE_INFO", TRUE) )
    4495              50 :         oWriter.SetInfo(poSrcDS, papszOptions);
    4496              51 :     oWriter.SetXMP(poClippingDS, pszXMP);
    4497                 : 
    4498                 :     oWriter.StartPage(poClippingDS,
    4499                 :                       dfDPI,
    4500                 :                       pszGEO_ENCODING,
    4501                 :                       pszNEATLINE,
    4502                 :                       &sMargins,
    4503                 :                       eStreamCompressMethod,
    4504              51 :                       pszOGRDataSource != NULL && bWriteOGRAttributes);
    4505                 : 
    4506                 :     int bRet;
    4507                 : 
    4508              51 :     if( !bUseClippingExtent )
    4509                 :     {
    4510                 :         bRet = oWriter.WriteImagery(poSrcDS,
    4511                 :                                     pszLayerName,
    4512                 :                                     eCompressMethod,
    4513                 :                                     nPredictor,
    4514                 :                                     nJPEGQuality,
    4515                 :                                     pszJPEG2000_DRIVER,
    4516                 :                                     nBlockXSize, nBlockYSize,
    4517              50 :                                     pfnProgress, pProgressData);
    4518                 :     }
    4519                 :     else
    4520                 :     {
    4521                 :         bRet = oWriter.WriteClippedImagery(poSrcDS,
    4522                 :                                            pszLayerName,
    4523                 :                                            eCompressMethod,
    4524                 :                                            nPredictor,
    4525                 :                                            nJPEGQuality,
    4526                 :                                            pszJPEG2000_DRIVER,
    4527                 :                                            nBlockXSize, nBlockYSize,
    4528               1 :                                            pfnProgress, pProgressData);
    4529                 :     }
    4530                 : 
    4531                 :     char** papszExtraRasters = CSLTokenizeString2(
    4532              51 :         pszExtraRasters ? pszExtraRasters : "", ",", 0);
    4533                 :     char** papszExtraRastersLayerName = CSLTokenizeString2(
    4534              51 :         pszExtraRastersLayerName ? pszExtraRastersLayerName : "", ",", 0);
    4535                 :     int bUseExtraRastersLayerName = (CSLCount(papszExtraRasters) ==
    4536              51 :                                      CSLCount(papszExtraRastersLayerName));
    4537              51 :     int bUseExtraRasters = TRUE;
    4538                 : 
    4539              51 :     const char* pszClippingProjectionRef = poSrcDS->GetProjectionRef();
    4540              51 :     if( CSLCount(papszExtraRasters) != 0 )
    4541                 :     {
    4542                 :         double adfGeoTransform[6];
    4543               1 :         if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None )
    4544                 :         {
    4545               1 :             if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
    4546                 :             {
    4547                 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4548               0 :                          "Cannot use EXTRA_RASTERS because main raster has a rotated geotransform");
    4549               0 :                 bUseExtraRasters = FALSE;
    4550                 :             }
    4551                 :         }
    4552                 :         else
    4553                 :         {
    4554                 :             CPLError(CE_Warning, CPLE_AppDefined,
    4555               0 :                          "Cannot use EXTRA_RASTERS because main raster has no geotransform");
    4556               0 :             bUseExtraRasters = FALSE;
    4557                 :         }
    4558               2 :         if( bUseExtraRasters &&
    4559                 :             (pszClippingProjectionRef == NULL ||
    4560               1 :              pszClippingProjectionRef[0] == '\0') )
    4561                 :         {
    4562                 :             CPLError(CE_Warning, CPLE_AppDefined,
    4563               0 :                      "Cannot use EXTRA_RASTERS because main raster has no projection");
    4564               0 :             bUseExtraRasters = FALSE;
    4565                 :         }
    4566                 :     }
    4567                 : 
    4568              52 :     for(int i=0; bRet && bUseExtraRasters && papszExtraRasters[i] != NULL; i++)
    4569                 :     {
    4570               1 :         GDALDataset* poDS = (GDALDataset*)GDALOpen(papszExtraRasters[i], GA_ReadOnly);
    4571               1 :         if( poDS != NULL )
    4572                 :         {
    4573                 :             double adfGeoTransform[6];
    4574               1 :             int bUseRaster = TRUE;
    4575               1 :             if( poDS->GetGeoTransform(adfGeoTransform) == CE_None )
    4576                 :             {
    4577               1 :                 if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
    4578                 :                 {
    4579                 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4580                 :                             "Cannot use %s because it has a rotated geotransform",
    4581               0 :                              papszExtraRasters[i]);
    4582               0 :                     bUseRaster = FALSE;
    4583                 :                 }
    4584                 :             }
    4585                 :             else
    4586                 :             {
    4587                 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4588                 :                             "Cannot use %s because it has no geotransform",
    4589               0 :                          papszExtraRasters[i]);
    4590               0 :                 bUseRaster = FALSE;
    4591                 :             }
    4592               1 :             const char* pszProjectionRef = poDS->GetProjectionRef();
    4593               2 :             if( bUseRaster &&
    4594               1 :                 (pszProjectionRef == NULL || pszProjectionRef[0] == '\0')  )
    4595                 :             {
    4596                 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4597                 :                          "Cannot use %s because it has no projection",
    4598               0 :                          papszExtraRasters[i]);
    4599               0 :                 bUseRaster = FALSE;
    4600                 :             }
    4601               1 :             if( bUseRaster )
    4602                 :             {
    4603               1 :                 if( pszClippingProjectionRef != NULL &&
    4604                 :                     pszProjectionRef != NULL &&
    4605                 :                     !EQUAL(pszClippingProjectionRef, pszProjectionRef) )
    4606                 :                 {
    4607                 :                     OGRSpatialReferenceH hClippingSRS =
    4608               0 :                         OSRNewSpatialReference(pszClippingProjectionRef);
    4609                 :                     OGRSpatialReferenceH hSRS =
    4610               0 :                         OSRNewSpatialReference(pszProjectionRef);
    4611               0 :                     if (!OSRIsSame(hClippingSRS, hSRS))
    4612                 :                     {
    4613                 :                         CPLError(CE_Warning, CPLE_AppDefined,
    4614                 :                                 "Cannot use %s because it has a different projection than main dataset",
    4615               0 :                                 papszExtraRasters[i]);
    4616               0 :                         bUseRaster = FALSE;
    4617                 :                     }
    4618               0 :                     OSRDestroySpatialReference(hClippingSRS);
    4619               0 :                     OSRDestroySpatialReference(hSRS);
    4620                 :                 }
    4621                 :             }
    4622               1 :             if( bUseRaster )
    4623                 :             {
    4624                 :                 bRet = oWriter.WriteClippedImagery(poDS,
    4625                 :                                     bUseExtraRastersLayerName ?
    4626               1 :                                         papszExtraRastersLayerName[i] : NULL,
    4627                 :                                     eCompressMethod,
    4628                 :                                     nPredictor,
    4629                 :                                     nJPEGQuality,
    4630                 :                                     pszJPEG2000_DRIVER,
    4631                 :                                     nBlockXSize, nBlockYSize,
    4632               2 :                                     NULL, NULL);
    4633                 :             }
    4634                 : 
    4635               1 :             GDALClose(poDS);
    4636                 :         }
    4637                 :     }
    4638                 : 
    4639              51 :     CSLDestroy(papszExtraRasters);
    4640              51 :     CSLDestroy(papszExtraRastersLayerName);
    4641                 : 
    4642                 : #ifdef OGR_ENABLED
    4643              51 :     if (bRet && pszOGRDataSource != NULL)
    4644                 :         oWriter.WriteOGRDataSource(pszOGRDataSource,
    4645                 :                                    pszOGRDisplayField,
    4646                 :                                    pszOGRDisplayLayerNames,
    4647                 :                                    pszOGRLinkField,
    4648               2 :                                    bWriteOGRAttributes);
    4649                 : #endif
    4650                 : 
    4651              51 :     if (bRet)
    4652                 :         oWriter.EndPage(pszExtraImages,
    4653                 :                         pszExtraStream,
    4654                 :                         pszExtraLayerName,
    4655                 :                         pszOffLayers,
    4656              51 :                         pszExclusiveLayers);
    4657                 : 
    4658              51 :     if (pszJavascript)
    4659               1 :         oWriter.WriteJavascript(pszJavascript);
    4660              50 :     else if (pszJavascriptFile)
    4661               0 :         oWriter.WriteJavascriptFile(pszJavascriptFile);
    4662                 : 
    4663              51 :     oWriter.Close();
    4664                 :     
    4665              51 :     if (poClippingDS != poSrcDS)
    4666               1 :         delete poClippingDS;
    4667                 : 
    4668              51 :     if (!bRet)
    4669                 :     {
    4670               0 :         VSIUnlink(pszFilename);
    4671               0 :         return NULL;
    4672                 :     }
    4673                 :     else
    4674                 :     {
    4675                 : #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
    4676              51 :         return (GDALDataset*) GDALOpen(pszFilename, GA_ReadOnly);
    4677                 : #else
    4678                 :         return new GDALFakePDFDataset();
    4679                 : #endif
    4680               0 :     }
    4681            2139 : }

Generated by: LCOV version 1.7