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