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