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