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