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