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