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