1 : /******************************************************************************
2 : * $Id: pdfdataset.cpp 23033 2011-09-03 18:46:11Z 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) 2010, 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 "cpl_vsi_virtual.h"
35 : #include "cpl_string.h"
36 : #include "gdal_pam.h"
37 : #include "ogr_spatialref.h"
38 : #include "ogr_geometry.h"
39 :
40 : #ifdef USE_POPPLER
41 : #include "pdfio.h"
42 : #endif
43 :
44 : #include "pdfobject.h"
45 :
46 : /* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */
47 :
48 : CPL_CVSID("$Id: pdfdataset.cpp 23033 2011-09-03 18:46:11Z rouault $");
49 :
50 : CPL_C_START
51 : void GDALRegister_PDF(void);
52 : CPL_C_END
53 :
54 : #ifdef USE_POPPLER
55 :
56 : /************************************************************************/
57 : /* ObjectAutoFree */
58 : /************************************************************************/
59 :
60 : class ObjectAutoFree : public Object
61 : {
62 : public:
63 4 : ObjectAutoFree() {}
64 4 : ~ObjectAutoFree() { free(); }
65 : };
66 :
67 : #endif
68 :
69 : /************************************************************************/
70 : /* Dump routines */
71 : /************************************************************************/
72 :
73 : static void DumpObject(FILE* f, GDALPDFObject* poObj, int nDepth = 0, int nDepthLimit = -1);
74 : static void DumpDict(FILE* f, GDALPDFDictionary* poDict, int nDepth = 0, int nDepthLimit = -1);
75 : static void DumpArray(FILE* f, GDALPDFArray* poArray, int nDepth = 0, int nDepthLimit = -1);
76 : static void DumpObjectSimplified(FILE* f, GDALPDFObject* poObj);
77 :
78 0 : static void DumpArray(FILE* f, GDALPDFArray* poArray, int nDepth, int nDepthLimit)
79 : {
80 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
81 0 : return;
82 :
83 0 : int nLength = poArray->GetLength();
84 : int i;
85 0 : CPLString osIndent;
86 0 : for(i=0;i<nDepth;i++)
87 0 : osIndent += " ";
88 0 : for(i=0;i<nLength;i++)
89 : {
90 0 : fprintf(f, "%sItem[%d]:", osIndent.c_str(), i);
91 0 : GDALPDFObject* poObj = NULL;
92 0 : if ((poObj = poArray->Get(i)) != NULL)
93 : {
94 0 : if (poObj->GetType() == PDFObjectType_String ||
95 0 : poObj->GetType() == PDFObjectType_Int ||
96 0 : poObj->GetType() == PDFObjectType_Real ||
97 0 : poObj->GetType() == PDFObjectType_Name)
98 : {
99 0 : fprintf(f, " ");
100 0 : DumpObjectSimplified(f, poObj);
101 0 : fprintf(f, "\n");
102 : }
103 : else
104 : {
105 0 : fprintf(f, "\n");
106 0 : DumpObject(f, poObj, nDepth+1, nDepthLimit);
107 : }
108 : }
109 0 : }
110 : }
111 :
112 0 : static void DumpObjectSimplified(FILE* f, GDALPDFObject* poObj)
113 : {
114 0 : switch(poObj->GetType())
115 : {
116 : case PDFObjectType_String:
117 0 : fprintf(f, "%s (string)", poObj->GetString().c_str());
118 0 : break;
119 :
120 : case PDFObjectType_Int:
121 0 : fprintf(f, "%d (int)", poObj->GetInt());
122 0 : break;
123 :
124 : case PDFObjectType_Real:
125 0 : fprintf(f, "%f (real)", poObj->GetReal());
126 0 : break;
127 :
128 : case PDFObjectType_Name:
129 0 : fprintf(f, "%s (name)", poObj->GetName().c_str());
130 : break;
131 :
132 : default:
133 : break;
134 : }
135 0 : }
136 :
137 0 : static void DumpObject(FILE* f, GDALPDFObject* poObj, int nDepth, int nDepthLimit)
138 : {
139 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
140 0 : return;
141 :
142 : int i;
143 0 : CPLString osIndent;
144 0 : for(i=0;i<nDepth;i++)
145 0 : osIndent += " ";
146 0 : fprintf(f, "%sType = %s\n", osIndent.c_str(), poObj->GetTypeName());
147 0 : switch(poObj->GetType())
148 : {
149 : case PDFObjectType_Array:
150 0 : DumpArray(f, poObj->GetArray(), nDepth+1, nDepthLimit);
151 0 : break;
152 :
153 : case PDFObjectType_Dictionary:
154 0 : DumpDict(f, poObj->GetDictionary(), nDepth+1, nDepthLimit);
155 0 : break;
156 :
157 : case PDFObjectType_String:
158 : case PDFObjectType_Int:
159 : case PDFObjectType_Real:
160 : case PDFObjectType_Name:
161 0 : fprintf(f, "%s", osIndent.c_str());
162 0 : DumpObjectSimplified(f, poObj);
163 0 : fprintf(f, "\n");
164 : break;
165 :
166 : default:
167 : break;
168 0 : }
169 : }
170 :
171 0 : static void DumpDict(FILE* f, GDALPDFDictionary* poDict, int nDepth, int nDepthLimit)
172 : {
173 0 : if (nDepthLimit >= 0 && nDepth > nDepthLimit)
174 0 : return;
175 :
176 0 : std::map<CPLString, GDALPDFObject*>& oMap = poDict->GetValues();
177 0 : std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
178 0 : std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
179 : int i;
180 0 : CPLString osIndent;
181 0 : for(i=0;i<nDepth;i++)
182 0 : osIndent += " ";
183 0 : for(i=0;oIter != oEnd;++oIter, i++)
184 : {
185 0 : const char* pszKey = oIter->first.c_str();
186 0 : fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, pszKey);
187 0 : if (strcmp(pszKey, "Parent") == 0)
188 : {
189 0 : fprintf(f, "\n");
190 0 : continue;
191 : }
192 0 : GDALPDFObject* poObj = oIter->second;
193 0 : if (poObj != NULL)
194 : {
195 0 : if (poObj->GetType() == PDFObjectType_String ||
196 0 : poObj->GetType() == PDFObjectType_Int ||
197 0 : poObj->GetType() == PDFObjectType_Real ||
198 0 : poObj->GetType() == PDFObjectType_Name)
199 : {
200 0 : fprintf(f, " = ");
201 0 : DumpObjectSimplified(f, poObj);
202 0 : fprintf(f, "\n");
203 : }
204 : else
205 : {
206 0 : fprintf(f, "\n");
207 0 : DumpObject(f, poObj, nDepth+1, nDepthLimit);
208 : }
209 : }
210 0 : }
211 : }
212 :
213 :
214 : /************************************************************************/
215 : /* ==================================================================== */
216 : /* PDFDataset */
217 : /* ==================================================================== */
218 : /************************************************************************/
219 :
220 : class PDFRasterBand;
221 :
222 : class PDFDataset : public GDALPamDataset
223 : {
224 : friend class PDFRasterBand;
225 : CPLString osFilename;
226 : CPLString osUserPwd;
227 : char *pszWKT;
228 : double dfDPI;
229 : double adfCTM[6];
230 : double adfGeoTransform[6];
231 : int bGeoTransformValid;
232 : #ifdef USE_POPPLER
233 : PDFDoc* poDoc;
234 : #endif
235 : int iPage;
236 :
237 : double dfMaxArea;
238 : int ParseLGIDictObject(GDALPDFObject* poLGIDict);
239 : int ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict, int* pbIsLargestArea = NULL);
240 : int ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict);
241 : int ParseProjDict(GDALPDFDictionary* poProjDict);
242 : int ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight);
243 :
244 : int bTried;
245 : GByte *pabyData;
246 :
247 : OGRPolygon* poNeatLine;
248 :
249 : public:
250 : PDFDataset();
251 : virtual ~PDFDataset();
252 :
253 : virtual const char* GetProjectionRef();
254 : virtual CPLErr GetGeoTransform( double * );
255 :
256 : static GDALDataset *Open( GDALOpenInfo * );
257 : static int Identify( GDALOpenInfo * );
258 : };
259 :
260 : /************************************************************************/
261 : /* ==================================================================== */
262 : /* PDFRasterBand */
263 : /* ==================================================================== */
264 : /************************************************************************/
265 :
266 : class PDFRasterBand : public GDALPamRasterBand
267 12 : {
268 : friend class PDFDataset;
269 :
270 : public:
271 :
272 : PDFRasterBand( PDFDataset *, int );
273 :
274 : virtual CPLErr IReadBlock( int, int, void * );
275 : virtual GDALColorInterp GetColorInterpretation();
276 : };
277 :
278 :
279 : /************************************************************************/
280 : /* PDFRasterBand() */
281 : /************************************************************************/
282 :
283 12 : PDFRasterBand::PDFRasterBand( PDFDataset *poDS, int nBand )
284 :
285 : {
286 12 : this->poDS = poDS;
287 12 : this->nBand = nBand;
288 :
289 12 : eDataType = GDT_Byte;
290 :
291 12 : nBlockXSize = poDS->GetRasterXSize();
292 12 : nBlockYSize = 1;
293 12 : }
294 :
295 : /************************************************************************/
296 : /* GetColorInterpretation() */
297 : /************************************************************************/
298 :
299 0 : GDALColorInterp PDFRasterBand::GetColorInterpretation()
300 : {
301 0 : return (GDALColorInterp)(GCI_RedBand + (nBand - 1));
302 : }
303 :
304 : /************************************************************************/
305 : /* IReadBlock() */
306 : /************************************************************************/
307 :
308 2743 : CPLErr PDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
309 : void * pImage )
310 :
311 : {
312 2743 : PDFDataset *poGDS = (PDFDataset *) poDS;
313 2743 : if (poGDS->bTried == FALSE)
314 : {
315 2 : poGDS->bTried = TRUE;
316 2 : poGDS->pabyData = (GByte*)VSIMalloc3(3, nRasterXSize, nRasterYSize);
317 2 : if (poGDS->pabyData == NULL)
318 0 : return CE_Failure;
319 :
320 : #ifdef USE_POPPLER
321 :
322 : /* poppler global variable */
323 2 : if (globalParams == NULL)
324 1 : globalParams = new GlobalParams();
325 :
326 : SplashColor sColor;
327 2 : sColor[0] = 255;
328 2 : sColor[1] = 255;
329 2 : sColor[2] = 255;
330 : SplashOutputDev *poSplashOut;
331 2 : poSplashOut = new SplashOutputDev(splashModeRGB8, 4, gFalse, sColor);
332 2 : PDFDoc* poDoc = poGDS->poDoc;
333 2 : poSplashOut->startDoc(poDoc->getXRef());
334 2 : double dfDPI = poGDS->dfDPI;
335 :
336 : /* EVIL: we modify a private member... */
337 : /* poppler (at least 0.12 and 0.14 versions) don't render correctly */
338 : /* some PDFs and display an error message 'Could not find a OCG with Ref' */
339 : /* in those cases. This processing of optional content is an addition of */
340 : /* poppler in comparison to original xpdf, which hasn't the issue. All in */
341 : /* all, nullifying optContent removes the error message and improves the rendering */
342 : #ifdef POPPLER_HAS_OPTCONTENT
343 2 : Catalog* poCatalog = poDoc->getCatalog();
344 2 : OCGs* poOldOCGs = poCatalog->optContent;
345 2 : poCatalog->optContent = NULL;
346 : #endif
347 :
348 : poGDS->poDoc->displayPageSlice(poSplashOut,
349 : poGDS->iPage,
350 : dfDPI, dfDPI,
351 : 0,
352 : TRUE, gFalse, gFalse,
353 : 0, 0,
354 : nRasterXSize,
355 2 : nRasterYSize);
356 :
357 : /* Restore back */
358 : #ifdef POPPLER_HAS_OPTCONTENT
359 2 : poCatalog->optContent = poOldOCGs;
360 : #endif
361 :
362 2 : SplashBitmap* poBitmap = poSplashOut->getBitmap();
363 4 : if (poBitmap->getWidth() != nRasterXSize || poBitmap->getHeight() != nRasterYSize)
364 : {
365 : CPLError(CE_Failure, CPLE_AppDefined,
366 : "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)" ,
367 : poBitmap->getWidth(), poBitmap->getHeight(),
368 0 : nRasterXSize, nRasterYSize);
369 0 : VSIFree(poGDS->pabyData);
370 0 : poGDS->pabyData = NULL;
371 0 : delete poSplashOut;
372 0 : return CE_Failure;
373 : }
374 :
375 2 : int nRowSize = poBitmap->getRowSize();
376 2 : GByte* pabyBitmap = poBitmap->getDataPtr();
377 : int i, j;
378 2 : GByte* pabyDataR = poGDS->pabyData;
379 2 : GByte* pabyDataG = poGDS->pabyData + nRasterXSize * nRasterYSize;
380 2 : GByte* pabyDataB = poGDS->pabyData + 2 * nRasterXSize * nRasterYSize;
381 2745 : for(j=0;j<nRasterYSize;j++)
382 : {
383 4079403 : for(i=0;i<nRasterXSize;i++)
384 : {
385 4076660 : pabyDataR[j * nRasterXSize + i] = pabyBitmap[j * nRowSize + i * 3];
386 4076660 : pabyDataG[j * nRasterXSize + i] = pabyBitmap[j * nRowSize + i * 3 + 1];
387 4076660 : pabyDataB[j * nRasterXSize + i] = pabyBitmap[j * nRowSize + i * 3 + 2];
388 : }
389 : }
390 2 : delete poSplashOut;
391 :
392 : #else
393 : memset(poGDS->pabyData, 0, ((size_t)3) * nRasterXSize * nRasterYSize);
394 :
395 : CPLString osTmpFilenamePrefix = CPLGenerateTempFilename("pdf");
396 : CPLString osTmpFilename(CPLSPrintf("%s-%d.ppm",
397 : osTmpFilenamePrefix.c_str(),
398 : poGDS->iPage));
399 :
400 : CPLString osCmd = CPLSPrintf("pdftoppm -r %f -f %d -l %d \"%s\" \"%s\"",
401 : poGDS->dfDPI, poGDS->iPage, poGDS->iPage,
402 : poGDS->osFilename.c_str(), osTmpFilenamePrefix.c_str());
403 : if (poGDS->osUserPwd.size() != 0)
404 : {
405 : osCmd += " -upw \"";
406 : osCmd += poGDS->osUserPwd;
407 : osCmd += "\"";
408 : }
409 :
410 : CPLDebug("PDF", "Running '%s'", osCmd.c_str());
411 : int nRet = system(osCmd.c_str());
412 : if (nRet == 0)
413 : {
414 : GDALDataset* poDS = (GDALDataset*) GDALOpen(osTmpFilename, GA_ReadOnly);
415 : if (poDS)
416 : {
417 : if (poDS->GetRasterCount() == 3)
418 : {
419 : poDS->RasterIO(GF_Read, 0, 0,
420 : poDS->GetRasterXSize(),
421 : poDS->GetRasterYSize(),
422 : poGDS->pabyData, nRasterXSize, nRasterYSize,
423 : GDT_Byte, 3, NULL, 0, 0, 0);
424 : }
425 : delete poDS;
426 : }
427 : }
428 : else
429 : {
430 : CPLDebug("PDF", "Ret code = %d", nRet);
431 : }
432 : VSIUnlink(osTmpFilename);
433 : #endif
434 : }
435 2743 : if (poGDS->pabyData == NULL)
436 0 : return CE_Failure;
437 :
438 : memcpy(pImage,
439 : poGDS->pabyData + (nBand - 1) * nRasterXSize * nRasterYSize + nBlockYOff * nRasterXSize,
440 2743 : nRasterXSize);
441 :
442 2743 : return CE_None;
443 : }
444 :
445 : /************************************************************************/
446 : /* ~PDFDataset() */
447 : /************************************************************************/
448 :
449 4 : PDFDataset::PDFDataset()
450 : {
451 : #ifdef USE_POPPLER
452 4 : poDoc = NULL;
453 : #endif
454 4 : pszWKT = NULL;
455 4 : dfMaxArea = 0;
456 4 : adfGeoTransform[0] = 0;
457 4 : adfGeoTransform[1] = 1;
458 4 : adfGeoTransform[2] = 0;
459 4 : adfGeoTransform[3] = 0;
460 4 : adfGeoTransform[4] = 0;
461 4 : adfGeoTransform[5] = 1;
462 4 : bGeoTransformValid = FALSE;
463 4 : bTried = FALSE;
464 4 : pabyData = NULL;
465 4 : iPage = -1;
466 4 : poNeatLine = NULL;
467 4 : }
468 :
469 : /************************************************************************/
470 : /* PDFFreeDoc() */
471 : /************************************************************************/
472 :
473 : #ifdef USE_POPPLER
474 4 : static void PDFFreeDoc(PDFDoc* poDoc)
475 : {
476 4 : if (poDoc)
477 : {
478 : /* hack to avoid potential cross heap issues on Win32 */
479 : /* str is the VSIPDFFileStream object passed in the constructor of PDFDoc */
480 4 : delete poDoc->str;
481 4 : poDoc->str = NULL;
482 :
483 4 : delete poDoc;
484 : }
485 4 : }
486 : #endif
487 :
488 : /************************************************************************/
489 : /* ~PDFDataset() */
490 : /************************************************************************/
491 :
492 4 : PDFDataset::~PDFDataset()
493 : {
494 4 : CPLFree(pszWKT);
495 4 : CPLFree(pabyData);
496 :
497 4 : delete poNeatLine;
498 :
499 : #ifdef USE_POPPLER
500 4 : PDFFreeDoc(poDoc);
501 : #endif
502 4 : }
503 :
504 : /************************************************************************/
505 : /* Identify() */
506 : /************************************************************************/
507 :
508 10505 : int PDFDataset::Identify( GDALOpenInfo * poOpenInfo )
509 : {
510 10505 : if (strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0)
511 1 : return TRUE;
512 :
513 10504 : if (poOpenInfo->nHeaderBytes < 128)
514 10359 : return FALSE;
515 :
516 145 : return strncmp((const char*)poOpenInfo->pabyHeader, "%PDF", 4) == 0;
517 : }
518 :
519 : /************************************************************************/
520 : /* PDFDatasetErrorFunction() */
521 : /************************************************************************/
522 :
523 : #ifdef USE_POPPLER
524 0 : static void PDFDatasetErrorFunction(int nPos, char *pszMsg, va_list args)
525 : {
526 0 : CPLString osError;
527 :
528 0 : if (nPos >= 0)
529 0 : osError.Printf("Pos = %d, ", nPos);
530 0 : osError += CPLString().vPrintf(pszMsg, args);
531 :
532 0 : if (strcmp(osError.c_str(), "Incorrect password") == 0)
533 : return;
534 :
535 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str());
536 : }
537 : #endif
538 :
539 : /************************************************************************/
540 : /* Open() */
541 : /************************************************************************/
542 :
543 1267 : GDALDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo )
544 :
545 : {
546 1267 : if (!Identify(poOpenInfo))
547 1263 : return NULL;
548 :
549 4 : const char* pszUserPwd = CPLGetConfigOption("PDF_USER_PWD", NULL);
550 :
551 4 : int bOpenSubdataset = strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0;
552 4 : int iPage = -1;
553 4 : const char* pszFilename = poOpenInfo->pszFilename;
554 : char szPassword[81];
555 :
556 4 : if (bOpenSubdataset)
557 : {
558 1 : iPage = atoi(pszFilename + 4);
559 1 : if (iPage <= 0)
560 0 : return NULL;
561 1 : pszFilename = strchr(pszFilename + 4, ':');
562 1 : if (pszFilename == NULL)
563 0 : return NULL;
564 1 : pszFilename ++;
565 : }
566 : else
567 3 : iPage = 1;
568 :
569 : #ifdef USE_POPPLER
570 4 : GooString* poUserPwd = NULL;
571 :
572 : /* Set custom error handler for poppler errors */
573 4 : setErrorFunction(PDFDatasetErrorFunction);
574 :
575 4 : PDFDoc* poDoc = NULL;
576 4 : ObjectAutoFree oObj;
577 0 : while(TRUE)
578 : {
579 4 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
580 4 : if (fp == NULL)
581 0 : return NULL;
582 :
583 4 : fp = (VSILFILE*)VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp);
584 :
585 4 : if (pszUserPwd)
586 0 : poUserPwd = new GooString(pszUserPwd);
587 :
588 4 : oObj.initNull();
589 4 : poDoc = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, 0, gFalse, 0, &oObj), NULL, poUserPwd);
590 4 : delete poUserPwd;
591 :
592 4 : if ( !poDoc->isOk() || poDoc->getNumPages() == 0 )
593 : {
594 0 : if (poDoc->getErrorCode() == errEncrypted)
595 : {
596 0 : if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
597 : {
598 0 : printf( "Enter password (will be echo'ed in the console): " );
599 0 : fgets( szPassword, sizeof(szPassword), stdin );
600 0 : szPassword[sizeof(szPassword)-1] = 0;
601 0 : char* sz10 = strchr(szPassword, '\n');
602 0 : if (sz10)
603 0 : *sz10 = 0;
604 0 : pszUserPwd = szPassword;
605 0 : PDFFreeDoc(poDoc);
606 0 : continue;
607 : }
608 0 : else if (pszUserPwd == NULL)
609 : {
610 : CPLError(CE_Failure, CPLE_AppDefined,
611 : "A password is needed. You can specify it through the PDF_USER_PWD "
612 0 : "configuration option (that can be set to ASK_INTERACTIVE)");
613 : }
614 : else
615 : {
616 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
617 : }
618 : }
619 : else
620 : {
621 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
622 : }
623 :
624 0 : PDFFreeDoc(poDoc);
625 :
626 0 : return NULL;
627 : }
628 : else
629 : break;
630 : }
631 :
632 4 : Catalog* poCatalog = poDoc->getCatalog();
633 4 : if ( poCatalog == NULL || !poCatalog->isOk() )
634 : {
635 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid catalog");
636 0 : PDFFreeDoc(poDoc);
637 0 : return NULL;
638 : }
639 :
640 4 : int nPages = poDoc->getNumPages();
641 4 : if (iPage < 1 || iPage > nPages)
642 : {
643 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
644 0 : iPage, nPages);
645 0 : PDFFreeDoc(poDoc);
646 0 : return NULL;
647 : }
648 :
649 4 : Page* poPage = poCatalog->getPage(iPage);
650 4 : if ( poPage == NULL || !poPage->isOk() )
651 : {
652 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
653 0 : PDFFreeDoc(poDoc);
654 0 : return NULL;
655 : }
656 :
657 : /* Here's the dirty part: this is a private member */
658 : /* so we had to #define private public to get it ! */
659 4 : Object& oPageObj = poPage->pageObj;
660 4 : if ( !oPageObj.isDict() )
661 : {
662 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : !oPageObj.isDict()");
663 0 : PDFFreeDoc(poDoc);
664 0 : return NULL;
665 : }
666 :
667 4 : GDALPDFObjectPoppler oPageObjPoppler(&oPageObj, FALSE);
668 4 : GDALPDFObject* poPageObj = &oPageObjPoppler;
669 : #else
670 : PoDoFo::PdfError::EnableDebug( false );
671 : PoDoFo::PdfError::EnableLogging( false );
672 :
673 : PoDoFo::PdfMemDocument* poDoc = new PoDoFo::PdfMemDocument();
674 : try
675 : {
676 : poDoc->Load(pszFilename);
677 : }
678 : catch(PoDoFo::PdfError& oError)
679 : {
680 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
681 : {
682 : if (pszUserPwd)
683 : {
684 : if (EQUAL(pszUserPwd, "ASK_INTERACTIVE"))
685 : {
686 : printf( "Enter password (will be echo'ed in the console): " );
687 : fgets( szPassword, sizeof(szPassword), stdin );
688 : szPassword[sizeof(szPassword)-1] = 0;
689 : char* sz10 = strchr(szPassword, '\n');
690 : if (sz10)
691 : *sz10 = 0;
692 : pszUserPwd = szPassword;
693 : }
694 :
695 : try
696 : {
697 : poDoc->SetPassword(pszUserPwd);
698 : }
699 : catch(PoDoFo::PdfError& oError)
700 : {
701 : if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword)
702 : {
703 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid password");
704 : }
705 : else
706 : {
707 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
708 : }
709 : delete poDoc;
710 : return NULL;
711 : }
712 : catch(...)
713 : {
714 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
715 : delete poDoc;
716 : return NULL;
717 : }
718 : }
719 : else
720 : {
721 : CPLError(CE_Failure, CPLE_AppDefined,
722 : "A password is needed. You can specify it through the PDF_USER_PWD "
723 : "configuration option (that can be set to ASK_INTERACTIVE)");
724 : delete poDoc;
725 : return NULL;
726 : }
727 : }
728 : else
729 : {
730 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what());
731 : delete poDoc;
732 : return NULL;
733 : }
734 : }
735 : catch(...)
736 : {
737 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF");
738 : delete poDoc;
739 : return NULL;
740 : }
741 :
742 : int nPages = poDoc->GetPageCount();
743 : if (iPage < 1 || iPage > nPages)
744 : {
745 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)",
746 : iPage, nPages);
747 : delete poDoc;
748 : return NULL;
749 : }
750 :
751 : PoDoFo::PdfPage* poPage = poDoc->GetPage(iPage - 1);
752 : if ( poPage == NULL )
753 : {
754 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page");
755 : delete poDoc;
756 : return NULL;
757 : }
758 :
759 : PoDoFo::PdfObject* pObj = poPage->GetObject();
760 : GDALPDFObjectPodofo oPageObjPodofo(pObj, poDoc->GetObjects());
761 : GDALPDFObject* poPageObj = &oPageObjPodofo;
762 : #endif
763 :
764 4 : GDALPDFDictionary* poPageDict = poPageObj->GetDictionary();
765 4 : if ( poPageDict == NULL )
766 : {
767 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : poPageDict == NULL");
768 : #ifdef USE_POPPLER
769 0 : PDFFreeDoc(poDoc);
770 : #else
771 : delete poDoc;
772 : #endif
773 0 : return NULL;
774 : }
775 :
776 4 : const char* pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", NULL);
777 4 : if (pszDumpObject != NULL)
778 : {
779 : FILE* f;
780 0 : if (strcmp(pszDumpObject, "stderr") == 0)
781 0 : f = stderr;
782 0 : else if (EQUAL(pszDumpObject, "YES"))
783 0 : f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), "wt");
784 : else
785 0 : f = fopen(pszDumpObject, "wt");
786 0 : if (f == NULL)
787 0 : f = stderr;
788 0 : DumpObject(f, poPageObj, 0 ,20);
789 0 : if (f != stderr)
790 0 : fclose(f);
791 : }
792 :
793 4 : PDFDataset* poDS = new PDFDataset();
794 8 : poDS->osFilename = pszFilename;
795 :
796 4 : if ( nPages > 1 && !bOpenSubdataset )
797 : {
798 : int i;
799 0 : char** papszSubDatasets = NULL;
800 0 : for(i=0;i<nPages;i++)
801 : {
802 : char szKey[32];
803 0 : sprintf( szKey, "SUBDATASET_%d_NAME", i+1 );
804 : papszSubDatasets =
805 : CSLSetNameValue( papszSubDatasets, szKey,
806 0 : CPLSPrintf("PDF:%d:%s", i+1, poOpenInfo->pszFilename));
807 0 : sprintf( szKey, "SUBDATASET_%d_DESC", i+1 );
808 : papszSubDatasets =
809 : CSLSetNameValue( papszSubDatasets, szKey,
810 0 : CPLSPrintf("Page %d of %s", i+1, poOpenInfo->pszFilename));
811 : }
812 0 : poDS->SetMetadata( papszSubDatasets, "SUBDATASETS" );
813 0 : CSLDestroy(papszSubDatasets);
814 : }
815 :
816 : #ifdef USE_POPPLER
817 4 : poDS->poDoc = poDoc;
818 : #endif
819 4 : poDS->osUserPwd = pszUserPwd ? pszUserPwd : "";
820 4 : poDS->iPage = iPage;
821 4 : poDS->dfDPI = atof(CPLGetConfigOption("GDAL_PDF_DPI", "150"));
822 4 : if (poDS->dfDPI < 1 || poDS->dfDPI > 7200)
823 : {
824 : CPLError(CE_Warning, CPLE_AppDefined,
825 0 : "Invalid value for GDAL_PDF_DPI. Using default value instead");
826 0 : poDS->dfDPI = 150;
827 : }
828 :
829 : #ifdef USE_POPPLER
830 4 : PDFRectangle* psMediaBox = poPage->getMediaBox();
831 4 : double dfX1 = psMediaBox->x1;
832 4 : double dfY1 = psMediaBox->y1;
833 4 : double dfX2 = psMediaBox->x2;
834 4 : double dfY2 = psMediaBox->y2;
835 : #else
836 : PoDoFo::PdfRect oMediaBox = poPage->GetMediaBox();
837 : double dfX1 = oMediaBox.GetLeft();
838 : double dfY1 = oMediaBox.GetBottom();
839 : double dfX2 = dfX1 + oMediaBox.GetWidth();
840 : double dfY2 = dfY1 + oMediaBox.GetHeight();
841 : #endif
842 :
843 4 : double dfPixelPerPt = poDS->dfDPI / 72;
844 4 : poDS->nRasterXSize = (int) ((dfX2 - dfX1) * dfPixelPerPt);
845 4 : poDS->nRasterYSize = (int) ((dfY2 - dfY1) * dfPixelPerPt);
846 :
847 : #ifdef USE_POPPLER
848 4 : double dfRotation = poDoc->getPageRotate(iPage);
849 : #else
850 : double dfRotation = poPage->GetRotation();
851 : #endif
852 4 : if ( dfRotation == 90 ||
853 : dfRotation == 270 )
854 : {
855 : /* FIXME: the non poppler case should be implemented. This needs to rotate */
856 : /* the output of pdftoppm */
857 : #ifdef USE_POPPLER
858 : /* Wondering how it would work with a georeferenced image */
859 : /* Has only been tested with ungeoreferenced image */
860 0 : int nTmp = poDS->nRasterXSize;
861 0 : poDS->nRasterXSize = poDS->nRasterYSize;
862 0 : poDS->nRasterYSize = nTmp;
863 : #endif
864 : }
865 :
866 4 : GDALPDFObject* poLGIDict = NULL;
867 4 : GDALPDFObject* poVP = NULL;
868 4 : int bIsOGCBP = FALSE;
869 4 : if ( (poLGIDict = poPageDict->Get("LGIDict")) != NULL )
870 : {
871 : /* Cf 08-139r2_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */
872 2 : CPLDebug("PDF", "OGC Encoding Best Practice style detected");
873 2 : if (poDS->ParseLGIDictObject(poLGIDict))
874 : {
875 2 : poDS->adfGeoTransform[0] = poDS->adfCTM[4] + poDS->adfCTM[0] * dfX1 + poDS->adfCTM[2] * dfY2;
876 2 : poDS->adfGeoTransform[1] = poDS->adfCTM[0] / dfPixelPerPt;
877 2 : poDS->adfGeoTransform[2] = poDS->adfCTM[1] / dfPixelPerPt;
878 2 : poDS->adfGeoTransform[3] = poDS->adfCTM[5] + poDS->adfCTM[1] * dfX1 + poDS->adfCTM[3] * dfY2;
879 2 : poDS->adfGeoTransform[4] = - poDS->adfCTM[2] / dfPixelPerPt;
880 2 : poDS->adfGeoTransform[5] = - poDS->adfCTM[3] / dfPixelPerPt;
881 2 : poDS->bGeoTransformValid = TRUE;
882 2 : bIsOGCBP = TRUE;
883 : }
884 : }
885 2 : else if ( (poVP = poPageDict->Get("VP")) != NULL )
886 : {
887 : /* Cf adobe_supplement_iso32000.pdf */
888 2 : CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?");
889 2 : if (dfX1 != 0 || dfY1 != 0)
890 : {
891 0 : CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case...");
892 : }
893 2 : poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1);
894 : }
895 : else
896 : {
897 : /* Not a geospatial PDF doc */
898 : }
899 :
900 4 : if (poDS->poNeatLine)
901 : {
902 4 : char* pszNeatLineWkt = NULL;
903 4 : OGRLinearRing* poRing = poDS->poNeatLine->getExteriorRing();
904 : /* Adobe style is already in target SRS units */
905 4 : if (bIsOGCBP)
906 : {
907 2 : int nPoints = poRing->getNumPoints();
908 : int i;
909 :
910 10 : for(i=0;i<nPoints;i++)
911 : {
912 8 : double x = poRing->getX(i) * dfPixelPerPt;
913 8 : double y = poDS->nRasterYSize - poRing->getY(i) * dfPixelPerPt;
914 8 : double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] +
915 8 : y * poDS->adfGeoTransform[2];
916 8 : double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] +
917 8 : y * poDS->adfGeoTransform[5];
918 8 : poRing->setPoint(i, X, Y);
919 : }
920 : }
921 4 : poRing->closeRings();
922 :
923 4 : poDS->poNeatLine->exportToWkt(&pszNeatLineWkt);
924 4 : poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt);
925 4 : CPLFree(pszNeatLineWkt);
926 : }
927 :
928 :
929 : #ifdef USE_POPPLER
930 4 : int nXRefSize = poDoc->getXRef()->getNumObjects();
931 93 : for(int i=0;i<nXRefSize;i++)
932 : {
933 89 : Object o;
934 89 : poDoc->getXRef()->fetch(i, 0, &o);
935 89 : if (o.isStream())
936 : {
937 21 : Dict* dict = o.getStream()->getDict();
938 21 : if (dict)
939 : {
940 21 : Object oSubType, oType;
941 21 : dict->lookup((char*)"Subtype", &oSubType);
942 21 : dict->lookup((char*)"Type", &oType);
943 21 : if (oType.getType() == objName &&
944 : strcmp(oType.getName(), "Metadata") == 0 &&
945 : oSubType.getType() == objName &&
946 : strcmp(oSubType.getName(), "XML") == 0)
947 : {
948 3 : int nBufferAlloc = 4096;
949 3 : char* pszXMP = (char*) CPLMalloc(nBufferAlloc);
950 3 : Stream* poStream = o.getStream();
951 3 : poStream->reset();
952 : int j;
953 10297 : for(j=0; ; j++)
954 : {
955 10297 : const int ch = poStream->getChar();
956 10297 : if (ch == EOF)
957 : break;
958 10294 : if (j == nBufferAlloc - 2)
959 : {
960 0 : nBufferAlloc *= 2;
961 0 : pszXMP = (char*) CPLRealloc(pszXMP, nBufferAlloc);
962 : }
963 10294 : pszXMP[j] = (char)ch;
964 : }
965 3 : pszXMP[j] = '\0';
966 3 : if (strncmp(pszXMP, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
967 : {
968 : char *apszMDList[2];
969 3 : apszMDList[0] = pszXMP;
970 3 : apszMDList[1] = NULL;
971 3 : poDS->SetMetadata(apszMDList, "xml:XMP");
972 : }
973 3 : CPLFree(pszXMP);
974 : }
975 21 : oSubType.free();
976 21 : oType.free();
977 : }
978 : }
979 89 : o.free();
980 : }
981 : #else
982 : PoDoFo::TIVecObjects it = poDoc->GetObjects().begin();
983 : for( ; it != poDoc->GetObjects().end(); ++it )
984 : {
985 : if( (*it)->HasStream() && (*it)->GetDataType() == PoDoFo::ePdfDataType_Dictionary)
986 : {
987 : PoDoFo::PdfDictionary& dict = (*it)->GetDictionary();
988 : const PoDoFo::PdfObject* poType = dict.GetKey(PoDoFo::PdfName("Type"));
989 : const PoDoFo::PdfObject* poSubType = dict.GetKey(PoDoFo::PdfName("Subtype"));
990 : if (poType == NULL ||
991 : poType->GetDataType() != PoDoFo::ePdfDataType_Name ||
992 : poType->GetName().GetName().compare("Metadata") != 0 ||
993 : poSubType == NULL ||
994 : poSubType->GetDataType() != PoDoFo::ePdfDataType_Name ||
995 : poSubType->GetName().GetName().compare("XML") != 0)
996 : continue;
997 :
998 : try
999 : {
1000 : PoDoFo::PdfMemStream* pStream = dynamic_cast<PoDoFo::PdfMemStream*>((*it)->GetStream());
1001 : pStream->Uncompress();
1002 :
1003 : const char* pszContent = pStream->Get();
1004 : int nLength = (int)pStream->GetLength();
1005 : if (pszContent != NULL && nLength > 15 &&
1006 : strncmp(pszContent, "<?xpacket begin=", strlen("<?xpacket begin=")) == 0)
1007 : {
1008 : char *apszMDList[2];
1009 : apszMDList[0] = (char*) CPLMalloc(nLength + 1);
1010 : memcpy(apszMDList[0], pszContent, nLength);
1011 : apszMDList[0][nLength] = 0;
1012 : apszMDList[1] = NULL;
1013 : poDS->SetMetadata(apszMDList, "xml:XMP");
1014 : CPLFree(apszMDList[0]);
1015 :
1016 : break;
1017 : }
1018 : }
1019 : catch( const PoDoFo::PdfError & e )
1020 : {
1021 : e.PrintErrorMsg();
1022 : }
1023 : }
1024 : }
1025 : #endif
1026 :
1027 :
1028 : #ifndef USE_POPPLER
1029 : delete poDoc;
1030 : #endif
1031 :
1032 : int iBand;
1033 16 : for(iBand = 1; iBand <= 3; iBand ++)
1034 12 : poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand));
1035 :
1036 : /* -------------------------------------------------------------------- */
1037 : /* Initialize any PAM information. */
1038 : /* -------------------------------------------------------------------- */
1039 4 : poDS->SetDescription( poOpenInfo->pszFilename );
1040 4 : poDS->TryLoadXML();
1041 :
1042 : /* -------------------------------------------------------------------- */
1043 : /* Support overviews. */
1044 : /* -------------------------------------------------------------------- */
1045 4 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
1046 4 : return( poDS );
1047 : }
1048 :
1049 : /************************************************************************/
1050 : /* ParseLGIDictObject() */
1051 : /************************************************************************/
1052 :
1053 2 : int PDFDataset::ParseLGIDictObject(GDALPDFObject* poLGIDict)
1054 : {
1055 : int i;
1056 2 : int bOK = FALSE;
1057 2 : if (poLGIDict->GetType() == PDFObjectType_Array)
1058 : {
1059 2 : GDALPDFArray* poArray = poLGIDict->GetArray();
1060 2 : int nArrayLength = poArray->GetLength();
1061 2 : int iMax = -1;
1062 : GDALPDFObject* poArrayElt;
1063 4 : for (i=0; i<nArrayLength; i++)
1064 : {
1065 4 : if ( (poArrayElt = poArray->Get(i)) == NULL ||
1066 2 : poArrayElt->GetType() != PDFObjectType_Dictionary )
1067 : {
1068 : CPLError(CE_Failure, CPLE_AppDefined,
1069 0 : "LGIDict[%d] is not a dictionary", i);
1070 0 : return FALSE;
1071 : }
1072 :
1073 2 : int bIsLargestArea = FALSE;
1074 2 : ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), &bIsLargestArea);
1075 2 : if (bIsLargestArea)
1076 2 : iMax = i;
1077 : }
1078 :
1079 2 : if (iMax < 0)
1080 0 : return FALSE;
1081 :
1082 2 : poArrayElt = poArray->Get(iMax);
1083 2 : bOK = ParseLGIDictDictSecondPass(poArrayElt->GetDictionary());
1084 : }
1085 0 : else if (poLGIDict->GetType() == PDFObjectType_Dictionary)
1086 : {
1087 0 : bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) &&
1088 0 : ParseLGIDictDictSecondPass(poLGIDict->GetDictionary());
1089 : }
1090 : else
1091 : {
1092 : CPLError(CE_Failure, CPLE_AppDefined,
1093 0 : "LGIDict is of type %s", poLGIDict->GetTypeName());
1094 : }
1095 :
1096 2 : return bOK;
1097 : }
1098 :
1099 : /************************************************************************/
1100 : /* Get() */
1101 : /************************************************************************/
1102 :
1103 232 : static double Get(GDALPDFObject* poObj, int nIndice = -1)
1104 : {
1105 232 : if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0)
1106 : {
1107 116 : poObj = poObj->GetArray()->Get(nIndice);
1108 116 : if (poObj == NULL)
1109 0 : return 0;
1110 116 : return Get(poObj);
1111 : }
1112 116 : else if (poObj->GetType() == PDFObjectType_Int)
1113 56 : return poObj->GetInt();
1114 60 : else if (poObj->GetType() == PDFObjectType_Real)
1115 16 : return poObj->GetReal();
1116 44 : else if (poObj->GetType() == PDFObjectType_String)
1117 : {
1118 44 : const char* pszStr = poObj->GetString().c_str();
1119 44 : int nLen = strlen(pszStr);
1120 : /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" */
1121 44 : char chLast = pszStr[nLen-1];
1122 44 : if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S')
1123 : {
1124 0 : double dfDeg = atof(pszStr);
1125 0 : double dfMin = 0, dfSec = 0;
1126 0 : const char* pszNext = strchr(pszStr, ' ');
1127 0 : if (pszNext)
1128 0 : pszNext ++;
1129 0 : if (pszNext)
1130 0 : dfMin = atof(pszNext);
1131 0 : if (pszNext)
1132 0 : pszNext = strchr(pszNext, ' ');
1133 0 : if (pszNext)
1134 0 : pszNext ++;
1135 0 : if (pszNext)
1136 0 : dfSec = atof(pszNext);
1137 0 : double dfVal = dfDeg + dfMin / 60 + dfSec / 3600;
1138 0 : if (chLast == 'W' || chLast == 'S')
1139 0 : return -dfVal;
1140 : else
1141 0 : return dfVal;
1142 : }
1143 44 : return atof(pszStr);
1144 : }
1145 : else
1146 : {
1147 : CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s",
1148 0 : poObj->GetTypeName());
1149 0 : return 0;
1150 : }
1151 : }
1152 :
1153 : /************************************************************************/
1154 : /* Get() */
1155 : /************************************************************************/
1156 :
1157 0 : static double Get(GDALPDFDictionary* poDict, const char* pszName)
1158 : {
1159 : GDALPDFObject* poObj;
1160 0 : if ( (poObj = poDict->Get(pszName)) != NULL )
1161 0 : return Get(poObj);
1162 : CPLError(CE_Failure, CPLE_AppDefined,
1163 0 : "Cannot find parameter %s", pszName);
1164 0 : return 0;
1165 : }
1166 :
1167 : /************************************************************************/
1168 : /* ParseLGIDictDictFirstPass() */
1169 : /************************************************************************/
1170 :
1171 2 : int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict,
1172 : int* pbIsLargestArea)
1173 : {
1174 : int i;
1175 :
1176 2 : if (pbIsLargestArea)
1177 2 : *pbIsLargestArea = FALSE;
1178 :
1179 2 : if (poLGIDict == NULL)
1180 0 : return FALSE;
1181 :
1182 : /* -------------------------------------------------------------------- */
1183 : /* Extract Type attribute */
1184 : /* -------------------------------------------------------------------- */
1185 : GDALPDFObject* poType;
1186 2 : if ((poType = poLGIDict->Get("Type")) == NULL)
1187 : {
1188 : CPLError(CE_Failure, CPLE_AppDefined,
1189 0 : "Cannot find Type of LGIDict object");
1190 0 : return FALSE;
1191 : }
1192 :
1193 2 : if ( poType->GetType() != PDFObjectType_Name )
1194 : {
1195 : CPLError(CE_Failure, CPLE_AppDefined,
1196 0 : "Invalid type for Type of LGIDict object");
1197 0 : return FALSE;
1198 : }
1199 :
1200 2 : if ( strcmp(poType->GetName().c_str(), "LGIDict") != 0 )
1201 : {
1202 : CPLError(CE_Failure, CPLE_AppDefined,
1203 : "Invalid value for Type of LGIDict object : %s",
1204 0 : poType->GetName().c_str());
1205 0 : return FALSE;
1206 : }
1207 :
1208 : /* -------------------------------------------------------------------- */
1209 : /* Extract Version attribute */
1210 : /* -------------------------------------------------------------------- */
1211 : GDALPDFObject* poVersion;
1212 2 : if ((poVersion = poLGIDict->Get("Version")) == NULL)
1213 : {
1214 : CPLError(CE_Failure, CPLE_AppDefined,
1215 0 : "Cannot find Version of LGIDict object");
1216 0 : return FALSE;
1217 : }
1218 :
1219 2 : if ( poVersion->GetType() != PDFObjectType_String )
1220 : {
1221 : /* OGC best practice is 2.1 */
1222 : CPLDebug("PDF", "LGIDict Version : %s",
1223 2 : poVersion->GetString().c_str());
1224 : }
1225 0 : else if (poVersion->GetType() != PDFObjectType_Int)
1226 : {
1227 : /* Old TerraGo is 2 */
1228 : CPLDebug("PDF", "LGIDict Version : %d",
1229 0 : poVersion->GetInt());
1230 : }
1231 :
1232 : /* -------------------------------------------------------------------- */
1233 : /* Extract Neatline attribute */
1234 : /* -------------------------------------------------------------------- */
1235 : GDALPDFObject* poNeatline;
1236 4 : if ((poNeatline = poLGIDict->Get("Neatline")) != NULL &&
1237 2 : poNeatline->GetType() == PDFObjectType_Array)
1238 : {
1239 2 : int nLength = poNeatline->GetArray()->GetLength();
1240 2 : if ( (nLength % 2) != 0 || nLength < 4 )
1241 : {
1242 : CPLError(CE_Failure, CPLE_AppDefined,
1243 0 : "Invalid length for Neatline");
1244 0 : return FALSE;
1245 : }
1246 :
1247 2 : double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0;
1248 10 : for(i=0;i<nLength;i+=2)
1249 : {
1250 8 : double dfX = Get(poNeatline, i);
1251 8 : double dfY = Get(poNeatline, i + 1);
1252 8 : if (i == 0 || dfX < dfMinX) dfMinX = dfX;
1253 8 : if (i == 0 || dfY < dfMinY) dfMinY = dfY;
1254 8 : if (i == 0 || dfX > dfMaxX) dfMaxX = dfX;
1255 8 : if (i == 0 || dfY > dfMaxY) dfMaxY = dfY;
1256 : }
1257 2 : double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY);
1258 2 : if (dfArea < dfMaxArea)
1259 : {
1260 0 : CPLDebug("PDF", "Not the largest neatline. Skipping it");
1261 0 : return TRUE;
1262 : }
1263 :
1264 2 : CPLDebug("PDF", "This is a the largest neatline for now");
1265 2 : dfMaxArea = dfArea;
1266 2 : if (pbIsLargestArea)
1267 2 : *pbIsLargestArea = TRUE;
1268 :
1269 2 : delete poNeatLine;
1270 2 : poNeatLine = new OGRPolygon();
1271 4 : OGRLinearRing* poRing = new OGRLinearRing();
1272 10 : for(i=0;i<nLength;i+=2)
1273 : {
1274 8 : double dfX = Get(poNeatline, i);
1275 8 : double dfY = Get(poNeatline, i + 1);
1276 8 : poRing->addPoint(dfX, dfY);
1277 : }
1278 2 : poNeatLine->addRingDirectly(poRing);
1279 : }
1280 :
1281 2 : return TRUE;
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* ParseLGIDictDictSecondPass() */
1286 : /************************************************************************/
1287 :
1288 2 : int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict)
1289 : {
1290 : int i;
1291 :
1292 : /* -------------------------------------------------------------------- */
1293 : /* Extract CTM attribute */
1294 : /* -------------------------------------------------------------------- */
1295 : GDALPDFObject* poCTM;
1296 2 : int bHasCTM = FALSE;
1297 4 : if ((poCTM = poLGIDict->Get("CTM")) != NULL &&
1298 2 : poCTM->GetType() == PDFObjectType_Array)
1299 : {
1300 2 : int nLength = poCTM->GetArray()->GetLength();
1301 2 : if ( nLength != 6 )
1302 : {
1303 : CPLError(CE_Failure, CPLE_AppDefined,
1304 0 : "Invalid length for CTM");
1305 0 : return FALSE;
1306 : }
1307 :
1308 2 : bHasCTM = TRUE;
1309 14 : for(i=0;i<nLength;i++)
1310 : {
1311 12 : adfCTM[i] = Get(poCTM, i);
1312 : /* Nullify rotation terms that are significantly smaller than */
1313 : /* scaling termes */
1314 12 : if ((i == 1 || i == 2) && fabs(adfCTM[i]) < fabs(adfCTM[0]) * 1e-10)
1315 4 : adfCTM[i] = 0;
1316 12 : CPLDebug("PDF", "CTM[%d] = %.16g", i, adfCTM[i]);
1317 : }
1318 : }
1319 :
1320 : /* -------------------------------------------------------------------- */
1321 : /* Extract Registration attribute */
1322 : /* -------------------------------------------------------------------- */
1323 2 : if (!bHasCTM)
1324 : {
1325 : GDALPDFObject* poRegistration;
1326 0 : if ((poRegistration = poLGIDict->Get("Registration")) != NULL)
1327 : {
1328 : /* TODO */
1329 0 : CPLDebug("PDF", "Registration unhandled for now");
1330 0 : return FALSE;
1331 : }
1332 : else
1333 : {
1334 0 : CPLDebug("PDF", "Neither CTM nor Registration found");
1335 0 : return FALSE;
1336 : }
1337 : }
1338 :
1339 : /* -------------------------------------------------------------------- */
1340 : /* Extract Projection attribute */
1341 : /* -------------------------------------------------------------------- */
1342 : GDALPDFObject* poProjection;
1343 4 : if ((poProjection = poLGIDict->Get("Projection")) == NULL ||
1344 2 : poProjection->GetType() != PDFObjectType_Dictionary)
1345 : {
1346 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection");
1347 0 : return FALSE;
1348 : }
1349 :
1350 2 : return ParseProjDict(poProjection->GetDictionary());
1351 : }
1352 :
1353 : /************************************************************************/
1354 : /* ParseProjDict() */
1355 : /************************************************************************/
1356 :
1357 2 : int PDFDataset::ParseProjDict(GDALPDFDictionary* poProjDict)
1358 : {
1359 2 : if (poProjDict == NULL)
1360 0 : return FALSE;
1361 2 : OGRSpatialReference oSRS;
1362 :
1363 : /* -------------------------------------------------------------------- */
1364 : /* Extract Type attribute */
1365 : /* -------------------------------------------------------------------- */
1366 : GDALPDFObject* poType;
1367 2 : if ((poType = poProjDict->Get("Type")) == NULL)
1368 : {
1369 : CPLError(CE_Failure, CPLE_AppDefined,
1370 0 : "Cannot find Type of Projection object");
1371 0 : return FALSE;
1372 : }
1373 :
1374 2 : if ( poType->GetType() != PDFObjectType_Name )
1375 : {
1376 : CPLError(CE_Failure, CPLE_AppDefined,
1377 0 : "Invalid type for Type of Projection object");
1378 0 : return FALSE;
1379 : }
1380 :
1381 2 : if ( strcmp(poType->GetName().c_str(), "Projection") != 0 )
1382 : {
1383 : CPLError(CE_Failure, CPLE_AppDefined,
1384 : "Invalid value for Type of Projection object : %s",
1385 0 : poType->GetName().c_str());
1386 0 : return FALSE;
1387 : }
1388 :
1389 : /* -------------------------------------------------------------------- */
1390 : /* Extract Datum attribute */
1391 : /* -------------------------------------------------------------------- */
1392 2 : int bIsWGS84 = FALSE;
1393 2 : int bIsNAD83 = FALSE;
1394 2 : int bIsNAD27 = FALSE;
1395 :
1396 : GDALPDFObject* poDatum;
1397 2 : if ((poDatum = poProjDict->Get("Datum")) != NULL)
1398 : {
1399 2 : if (poDatum->GetType() == PDFObjectType_String)
1400 : {
1401 2 : const char* pszDatum = poDatum->GetString().c_str();
1402 2 : CPLDebug("PDF", "Datum = %s", pszDatum);
1403 4 : if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE"))
1404 : {
1405 2 : bIsWGS84 = TRUE;
1406 2 : oSRS.SetWellKnownGeogCS("WGS84");
1407 : }
1408 0 : else if (EQUAL(pszDatum, "NAR") || EQUALN(pszDatum, "NAR-", 4))
1409 : {
1410 0 : bIsNAD83 = TRUE;
1411 0 : oSRS.SetWellKnownGeogCS("NAD83");
1412 : }
1413 0 : else if (EQUAL(pszDatum, "NAS") || EQUALN(pszDatum, "NAS-", 4))
1414 : {
1415 0 : bIsNAD27 = TRUE;
1416 0 : oSRS.SetWellKnownGeogCS("NAD27");
1417 : }
1418 0 : else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */
1419 : {
1420 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
1421 : "unknown" /*const char * pszDatumName */,
1422 : "International 1924",
1423 0 : 6378388,297);
1424 0 : oSRS.SetTOWGS84(-333,-222,114);
1425 : }
1426 0 : else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */
1427 : {
1428 0 : oSRS.importFromEPSG(4131);
1429 : }
1430 0 : else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */
1431 : {
1432 0 : oSRS.importFromEPSG(4283);
1433 : }
1434 : else
1435 : {
1436 : CPLError(CE_Warning, CPLE_AppDefined,
1437 : "Unhandled (yet) value for Datum : %s. Defaulting to WGS84...",
1438 0 : pszDatum);
1439 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
1440 : "unknown" /*const char * pszDatumName */,
1441 : "unknown",
1442 0 : 6378137,298.257223563);
1443 : }
1444 : }
1445 0 : else if (poDatum->GetType() == PDFObjectType_Dictionary)
1446 : {
1447 : /* TODO */
1448 : CPLError(CE_Warning, CPLE_AppDefined,
1449 0 : "Datum as dictionary unhandled yet. Defaulting to WGS84...");
1450 : oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/,
1451 : "unknown" /*const char * pszDatumName */,
1452 : "unknown",
1453 0 : 6378137,298.257223563);
1454 : }
1455 : }
1456 :
1457 : /* -------------------------------------------------------------------- */
1458 : /* Extract Hemisphere attribute */
1459 : /* -------------------------------------------------------------------- */
1460 2 : CPLString osHemisphere;
1461 : GDALPDFObject* poHemisphere;
1462 2 : if ((poHemisphere = poProjDict->Get("Hemisphere")) != NULL &&
1463 0 : poHemisphere->GetType() == PDFObjectType_String)
1464 : {
1465 0 : osHemisphere = poHemisphere->GetString();
1466 : }
1467 :
1468 : /* -------------------------------------------------------------------- */
1469 : /* Extract ProjectionType attribute */
1470 : /* -------------------------------------------------------------------- */
1471 : GDALPDFObject* poProjectionType;
1472 4 : if ((poProjectionType = poProjDict->Get("ProjectionType")) == NULL ||
1473 2 : poProjectionType->GetType() != PDFObjectType_String)
1474 : {
1475 : CPLError(CE_Failure, CPLE_AppDefined,
1476 0 : "Cannot find ProjectionType of Projection object");
1477 0 : return FALSE;
1478 : }
1479 2 : CPLString osProjectionType(poProjectionType->GetString());
1480 2 : CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str());
1481 :
1482 : /* Unhandled: NONE, GEODETIC */
1483 :
1484 2 : if (EQUAL(osProjectionType, "GEOGRAPHIC"))
1485 : {
1486 : /* Nothing to do */
1487 : }
1488 :
1489 : /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */
1490 :
1491 0 : else if (EQUAL(osProjectionType, "UT")) /* UTM */
1492 : {
1493 0 : int nZone = (int)Get(poProjDict, "Zone");
1494 0 : int bNorth = EQUAL(osHemisphere, "N");
1495 0 : if (bIsWGS84)
1496 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
1497 : else
1498 0 : oSRS.SetUTM( nZone, bNorth );
1499 : }
1500 :
1501 0 : else if (EQUAL(osProjectionType, "UP")) /* Universal Polar Stereographic (UPS) */
1502 : {
1503 0 : int bNorth = EQUAL(osHemisphere, "N");
1504 0 : if (bIsWGS84)
1505 0 : oSRS.importFromEPSG( (bNorth) ? 32661 : 32761 );
1506 : else
1507 : oSRS.SetPS( (bNorth) ? 90 : -90, 0,
1508 0 : 0.994, 200000, 200000 );
1509 : }
1510 :
1511 0 : else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */
1512 : {
1513 0 : int nZone = (int)Get(poProjDict, "Zone");
1514 0 : oSRS.SetStatePlane( nZone, bIsNAD83 );
1515 : }
1516 :
1517 0 : else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */
1518 : {
1519 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
1520 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
1521 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1522 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1523 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1524 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1525 : oSRS.SetACEA( dfStdP1, dfStdP2,
1526 : dfCenterLat, dfCenterLong,
1527 0 : dfFalseEasting, dfFalseNorthing );
1528 : }
1529 :
1530 0 : else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */
1531 : {
1532 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1533 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1534 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1535 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1536 : oSRS.SetAE( dfCenterLat, dfCenterLong,
1537 0 : dfFalseEasting, dfFalseNorthing );
1538 : }
1539 :
1540 0 : else if (EQUAL(osProjectionType, "BF")) /* Bonne */
1541 : {
1542 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
1543 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
1544 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1545 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1546 : oSRS.SetBonne( dfStdP1, dfCentralMeridian,
1547 0 : dfFalseEasting, dfFalseNorthing );
1548 : }
1549 :
1550 0 : else if (EQUAL(osProjectionType, "CS")) /* Cassini */
1551 : {
1552 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1553 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1554 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1555 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1556 : oSRS.SetCS( dfCenterLat, dfCenterLong,
1557 0 : dfFalseEasting, dfFalseNorthing );
1558 : }
1559 :
1560 0 : else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */
1561 : {
1562 0 : double dfStdP1 = Get(poProjDict, "OriginLatitude");
1563 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
1564 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1565 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1566 : oSRS.SetCEA( dfStdP1, dfCentralMeridian,
1567 0 : dfFalseEasting, dfFalseNorthing );
1568 : }
1569 :
1570 0 : else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */
1571 : {
1572 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
1573 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1574 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1575 : oSRS.SetEckertIV( dfCentralMeridian,
1576 0 : dfFalseEasting, dfFalseNorthing );
1577 : }
1578 :
1579 0 : else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */
1580 : {
1581 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
1582 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1583 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1584 : oSRS.SetEckertVI( dfCentralMeridian,
1585 0 : dfFalseEasting, dfFalseNorthing );
1586 : }
1587 :
1588 0 : else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */
1589 : {
1590 0 : double dfCenterLat = Get(poProjDict, "StandardParallel");
1591 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1592 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1593 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1594 : oSRS.SetEquirectangular( dfCenterLat, dfCenterLong,
1595 0 : dfFalseEasting, dfFalseNorthing );
1596 : }
1597 :
1598 0 : else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */
1599 : {
1600 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1601 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1602 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1603 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1604 : oSRS.SetGnomonic(dfCenterLat, dfCenterLong,
1605 0 : dfFalseEasting, dfFalseNorthing );
1606 : }
1607 :
1608 0 : else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */
1609 : {
1610 0 : double dfStdP1 = Get(poProjDict, "StandardParallelOne");
1611 0 : double dfStdP2 = Get(poProjDict, "StandardParallelTwo");
1612 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1613 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1614 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1615 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1616 : oSRS.SetLCC( dfStdP1, dfStdP2,
1617 : dfCenterLat, dfCenterLong,
1618 0 : dfFalseEasting, dfFalseNorthing );
1619 : }
1620 :
1621 0 : else if (EQUAL(osProjectionType, "MC")) /* Mercator */
1622 : {
1623 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1624 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1625 0 : double dfScale = Get(poProjDict, "ScaleFactor");
1626 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1627 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1628 : oSRS.SetMercator( dfCenterLat, dfCenterLong,
1629 : dfScale,
1630 0 : dfFalseEasting, dfFalseNorthing );
1631 : }
1632 :
1633 0 : else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */
1634 : {
1635 0 : double dfCenterLat = 0 /* ? */;
1636 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1637 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1638 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1639 : oSRS.SetMC( dfCenterLat, dfCenterLong,
1640 0 : dfFalseEasting, dfFalseNorthing );
1641 : }
1642 :
1643 0 : else if (EQUAL(osProjectionType, "MP")) /* Mollweide */
1644 : {
1645 0 : double dfCentralMeridian = Get(poProjDict, "CentralMeridian");
1646 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1647 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1648 : oSRS.SetMollweide( dfCentralMeridian,
1649 0 : dfFalseEasting, dfFalseNorthing );
1650 : }
1651 :
1652 : /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */
1653 :
1654 0 : else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */
1655 : {
1656 : /* No parameter specified in the PDF, so let's take the ones of EPSG:27200 */
1657 0 : double dfCenterLat = -41;
1658 0 : double dfCenterLong = 173;
1659 0 : double dfFalseEasting = 2510000;
1660 0 : double dfFalseNorthing = 6023150;
1661 : oSRS.SetNZMG( dfCenterLat, dfCenterLong,
1662 0 : dfFalseEasting, dfFalseNorthing );
1663 : }
1664 :
1665 0 : else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */
1666 : {
1667 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1668 0 : double dfLat1 = Get(poProjDict, "LatitudeOne");
1669 0 : double dfLong1 = Get(poProjDict, "LongitudeOne");
1670 0 : double dfLat2 = Get(poProjDict, "LatitudeTwo");
1671 0 : double dfLong2 = Get(poProjDict, "LongitudeTwo");
1672 0 : double dfScale = Get(poProjDict, "ScaleFactor");
1673 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1674 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1675 : oSRS.SetHOM2PNO( dfCenterLat,
1676 : dfLat1, dfLong1,
1677 : dfLat2, dfLong2,
1678 : dfScale,
1679 : dfFalseEasting,
1680 0 : dfFalseNorthing );
1681 : }
1682 :
1683 0 : else if (EQUAL(osProjectionType, "OD")) /* Orthographic */
1684 : {
1685 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1686 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1687 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1688 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1689 : oSRS.SetOrthographic( dfCenterLat, dfCenterLong,
1690 0 : dfFalseEasting, dfFalseNorthing );
1691 : }
1692 :
1693 0 : else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */
1694 : {
1695 0 : double dfCenterLat = Get(poProjDict, "LatitudeTrueScale");
1696 0 : double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole");
1697 0 : double dfScale = 1.0;
1698 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1699 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1700 : oSRS.SetPS( dfCenterLat, dfCenterLong,
1701 : dfScale,
1702 0 : dfFalseEasting, dfFalseNorthing);
1703 : }
1704 :
1705 0 : else if (EQUAL(osProjectionType, "PH")) /* Polyconic */
1706 : {
1707 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1708 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1709 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1710 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1711 : oSRS.SetPolyconic( dfCenterLat, dfCenterLong,
1712 0 : dfFalseEasting, dfFalseNorthing );
1713 : }
1714 :
1715 0 : else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */
1716 : {
1717 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1718 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1719 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1720 : oSRS.SetSinusoidal( dfCenterLong,
1721 0 : dfFalseEasting, dfFalseNorthing );
1722 : }
1723 :
1724 0 : else if (EQUAL(osProjectionType, "SD")) /* Stereographic */
1725 : {
1726 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1727 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1728 0 : double dfScale = 1.0;
1729 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1730 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1731 : oSRS.SetStereographic( dfCenterLat, dfCenterLong,
1732 : dfScale,
1733 0 : dfFalseEasting, dfFalseNorthing);
1734 : }
1735 :
1736 0 : else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */
1737 : {
1738 0 : double dfCenterLat = Get(poProjDict, "OriginLatitude");
1739 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1740 0 : double dfScale = Get(poProjDict, "ScaleFactor");
1741 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1742 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1743 0 : if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfFalseEasting == 500000 &&
1744 : (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0))
1745 : {
1746 0 : int nZone = (int) floor( (dfCenterLong + 180.0) / 6.0 ) + 1;
1747 0 : int bNorth = dfFalseNorthing == 0;
1748 0 : if (bIsWGS84)
1749 0 : oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone );
1750 0 : else if (bIsNAD83 && bNorth)
1751 0 : oSRS.importFromEPSG( 26900 + nZone );
1752 : else
1753 0 : oSRS.SetUTM( nZone, bNorth );
1754 : }
1755 : else
1756 : {
1757 : oSRS.SetTM( dfCenterLat, dfCenterLong,
1758 : dfScale,
1759 0 : dfFalseEasting, dfFalseNorthing );
1760 : }
1761 : }
1762 :
1763 : /* Unhandled TX : Transverse Cylindrical Equal Area */
1764 :
1765 0 : else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */
1766 : {
1767 0 : double dfCenterLong = Get(poProjDict, "CentralMeridian");
1768 0 : double dfFalseEasting = Get(poProjDict, "FalseEasting");
1769 0 : double dfFalseNorthing = Get(poProjDict, "FalseNorthing");
1770 : oSRS.SetVDG( dfCenterLong,
1771 0 : dfFalseEasting, dfFalseNorthing );
1772 : }
1773 :
1774 : else
1775 : {
1776 : CPLError(CE_Failure, CPLE_AppDefined,
1777 : "Unhandled (yet) value for ProjectionType : %s",
1778 0 : osProjectionType.c_str());
1779 0 : return FALSE;
1780 : }
1781 :
1782 : /* -------------------------------------------------------------------- */
1783 : /* Extract Units attribute */
1784 : /* -------------------------------------------------------------------- */
1785 2 : CPLString osUnits;
1786 : GDALPDFObject* poUnits;
1787 2 : if ((poUnits = poProjDict->Get("Units")) != NULL &&
1788 0 : poUnits->GetType() == PDFObjectType_String)
1789 : {
1790 0 : osUnits = poUnits->GetString();
1791 0 : CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str());
1792 :
1793 0 : if (EQUAL(osUnits, "FT"))
1794 0 : oSRS.SetLinearUnits( "Foot", 0.3048 );
1795 : }
1796 :
1797 : /* -------------------------------------------------------------------- */
1798 : /* Export SpatialRef */
1799 : /* -------------------------------------------------------------------- */
1800 2 : CPLFree(pszWKT);
1801 2 : pszWKT = NULL;
1802 2 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
1803 : {
1804 0 : CPLFree(pszWKT);
1805 0 : pszWKT = NULL;
1806 : }
1807 :
1808 2 : return TRUE;
1809 : }
1810 :
1811 : /************************************************************************/
1812 : /* ParseVP() */
1813 : /************************************************************************/
1814 :
1815 2 : int PDFDataset::ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight)
1816 : {
1817 : int i;
1818 :
1819 2 : if (poVP->GetType() != PDFObjectType_Array)
1820 0 : return FALSE;
1821 :
1822 2 : GDALPDFArray* poVPArray = poVP->GetArray();
1823 :
1824 2 : int nLength = poVPArray->GetLength();
1825 2 : CPLDebug("PDF", "VP length = %d", nLength);
1826 2 : if (nLength < 1)
1827 0 : return FALSE;
1828 :
1829 : /* -------------------------------------------------------------------- */
1830 : /* Find the largest BBox */
1831 : /* -------------------------------------------------------------------- */
1832 2 : int iLargest = 0;
1833 2 : double dfLargestArea = 0;
1834 :
1835 6 : for(i=0;i<nLength;i++)
1836 : {
1837 4 : GDALPDFObject* poVPElt = poVPArray->Get(i);
1838 4 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
1839 : {
1840 0 : return FALSE;
1841 : }
1842 :
1843 4 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
1844 :
1845 : GDALPDFObject* poBBox;
1846 8 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
1847 4 : poBBox->GetType() != PDFObjectType_Array )
1848 : {
1849 : CPLError(CE_Failure, CPLE_AppDefined,
1850 0 : "Cannot find Bbox object");
1851 0 : return FALSE;
1852 : }
1853 :
1854 4 : int nBboxLength = poBBox->GetArray()->GetLength();
1855 4 : if (nBboxLength != 4)
1856 : {
1857 : CPLError(CE_Failure, CPLE_AppDefined,
1858 0 : "Invalid length for Bbox object");
1859 0 : return FALSE;
1860 : }
1861 :
1862 : double adfBBox[4];
1863 4 : adfBBox[0] = Get(poBBox, 0);
1864 4 : adfBBox[1] = Get(poBBox, 1);
1865 4 : adfBBox[2] = Get(poBBox, 2);
1866 4 : adfBBox[3] = Get(poBBox, 3);
1867 4 : double dfArea = fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]);
1868 4 : if (dfArea > dfLargestArea)
1869 : {
1870 2 : iLargest = i;
1871 2 : dfLargestArea = dfArea;
1872 : }
1873 : }
1874 :
1875 2 : if (nLength > 1)
1876 : {
1877 2 : CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest);
1878 : }
1879 :
1880 :
1881 2 : GDALPDFObject* poVPElt = poVPArray->Get(iLargest);
1882 2 : if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary)
1883 : {
1884 0 : return FALSE;
1885 : }
1886 :
1887 2 : GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary();
1888 :
1889 : GDALPDFObject* poBBox;
1890 4 : if( (poBBox = poVPEltDict->Get("BBox")) == NULL ||
1891 2 : poBBox->GetType() != PDFObjectType_Array )
1892 : {
1893 : CPLError(CE_Failure, CPLE_AppDefined,
1894 0 : "Cannot find Bbox object");
1895 0 : return FALSE;
1896 : }
1897 :
1898 2 : int nBboxLength = poBBox->GetArray()->GetLength();
1899 2 : if (nBboxLength != 4)
1900 : {
1901 : CPLError(CE_Failure, CPLE_AppDefined,
1902 0 : "Invalid length for Bbox object");
1903 0 : return FALSE;
1904 : }
1905 :
1906 2 : double dfULX = Get(poBBox, 0);
1907 2 : double dfULY = dfMediaBoxHeight - Get(poBBox, 1);
1908 2 : double dfLRX = Get(poBBox, 2);
1909 2 : double dfLRY = dfMediaBoxHeight - Get(poBBox, 3);
1910 :
1911 : /* -------------------------------------------------------------------- */
1912 : /* Extract Measure attribute */
1913 : /* -------------------------------------------------------------------- */
1914 : GDALPDFObject* poMeasure;
1915 4 : if( (poMeasure = poVPEltDict->Get("Measure")) == NULL ||
1916 2 : poMeasure->GetType() != PDFObjectType_Dictionary )
1917 : {
1918 : CPLError(CE_Failure, CPLE_AppDefined,
1919 0 : "Cannot find Measure object");
1920 0 : return FALSE;
1921 : }
1922 :
1923 2 : GDALPDFDictionary* poMeasureDict = poMeasure->GetDictionary();
1924 :
1925 : /* -------------------------------------------------------------------- */
1926 : /* Extract Subtype attribute */
1927 : /* -------------------------------------------------------------------- */
1928 : GDALPDFObject* poSubtype;
1929 4 : if( (poSubtype = poMeasureDict->Get("Subtype")) == NULL ||
1930 2 : poSubtype->GetType() != PDFObjectType_Name )
1931 : {
1932 : CPLError(CE_Failure, CPLE_AppDefined,
1933 0 : "Cannot find Subtype object");
1934 0 : return FALSE;
1935 : }
1936 :
1937 2 : CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str());
1938 :
1939 : /* -------------------------------------------------------------------- */
1940 : /* Extract Bounds attribute */
1941 : /* -------------------------------------------------------------------- */
1942 :
1943 : /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf */
1944 : /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */
1945 : /* LPTS, GPTS and Bounds. Use those ones */
1946 :
1947 : GDALPDFObject* poBounds;
1948 2 : if( (poBounds = poMeasureDict->Get("lgit:Bounds")) != NULL &&
1949 0 : poBounds->GetType() == PDFObjectType_Array )
1950 : {
1951 0 : CPLDebug("PDF", "Using lgit:Bounds");
1952 : }
1953 4 : else if( (poBounds = poMeasureDict->Get("Bounds")) == NULL ||
1954 2 : poBounds->GetType() != PDFObjectType_Array )
1955 : {
1956 : CPLError(CE_Failure, CPLE_AppDefined,
1957 0 : "Cannot find Bounds object");
1958 0 : return FALSE;
1959 : }
1960 :
1961 2 : int nBoundsLength = poBounds->GetArray()->GetLength();
1962 2 : if (nBoundsLength != 8)
1963 : {
1964 : CPLError(CE_Failure, CPLE_AppDefined,
1965 0 : "Invalid length for Bounds object");
1966 0 : return FALSE;
1967 : }
1968 :
1969 : double adfBounds[8];
1970 18 : for(i=0;i<8;i++)
1971 : {
1972 16 : adfBounds[i] = Get(poBounds, i);
1973 16 : CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]);
1974 : }
1975 :
1976 : /* -------------------------------------------------------------------- */
1977 : /* Extract GPTS attribute */
1978 : /* -------------------------------------------------------------------- */
1979 : GDALPDFObject* poGPTS;
1980 2 : if( (poGPTS = poMeasureDict->Get("lgit:GPTS")) != NULL &&
1981 0 : poGPTS->GetType() == PDFObjectType_Array )
1982 : {
1983 0 : CPLDebug("PDF", "Using lgit:GPTS");
1984 : }
1985 4 : else if( (poGPTS = poMeasureDict->Get("GPTS")) == NULL ||
1986 2 : poGPTS->GetType() != PDFObjectType_Array )
1987 : {
1988 : CPLError(CE_Failure, CPLE_AppDefined,
1989 0 : "Cannot find GPTS object");
1990 0 : return FALSE;
1991 : }
1992 :
1993 2 : int nGPTSLength = poGPTS->GetArray()->GetLength();
1994 2 : if (nGPTSLength != 8)
1995 : {
1996 : CPLError(CE_Failure, CPLE_AppDefined,
1997 0 : "Invalid length for GPTS object");
1998 0 : return FALSE;
1999 : }
2000 :
2001 : double adfGPTS[8];
2002 18 : for(i=0;i<8;i++)
2003 : {
2004 16 : adfGPTS[i] = Get(poGPTS, i);
2005 16 : CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]);
2006 : }
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Extract LPTS attribute */
2010 : /* -------------------------------------------------------------------- */
2011 : GDALPDFObject* poLPTS;
2012 2 : if( (poLPTS = poMeasureDict->Get("lgit:LPTS")) != NULL &&
2013 0 : poLPTS->GetType() == PDFObjectType_Array )
2014 : {
2015 0 : CPLDebug("PDF", "Using lgit:LPTS");
2016 : }
2017 4 : else if( (poLPTS = poMeasureDict->Get("LPTS")) == NULL ||
2018 2 : poLPTS->GetType() != PDFObjectType_Array )
2019 : {
2020 : CPLError(CE_Failure, CPLE_AppDefined,
2021 0 : "Cannot find LPTS object");
2022 0 : return FALSE;
2023 : }
2024 :
2025 2 : int nLPTSLength = poLPTS->GetArray()->GetLength();
2026 2 : if (nLPTSLength != 8)
2027 : {
2028 : CPLError(CE_Failure, CPLE_AppDefined,
2029 0 : "Invalid length for LPTS object");
2030 0 : return FALSE;
2031 : }
2032 :
2033 : double adfLPTS[8];
2034 18 : for(i=0;i<8;i++)
2035 : {
2036 16 : adfLPTS[i] = Get(poLPTS, i);
2037 16 : CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]);
2038 : }
2039 :
2040 : /* -------------------------------------------------------------------- */
2041 : /* Extract GCS attribute */
2042 : /* -------------------------------------------------------------------- */
2043 : GDALPDFObject* poGCS;
2044 4 : if( (poGCS = poMeasureDict->Get("GCS")) == NULL ||
2045 2 : poGCS->GetType() != PDFObjectType_Dictionary )
2046 : {
2047 : CPLError(CE_Failure, CPLE_AppDefined,
2048 0 : "Cannot find GCS object");
2049 0 : return FALSE;
2050 : }
2051 :
2052 2 : GDALPDFDictionary* poGCSDict = poGCS->GetDictionary();
2053 :
2054 : /* -------------------------------------------------------------------- */
2055 : /* Extract GCS.Type attribute */
2056 : /* -------------------------------------------------------------------- */
2057 : GDALPDFObject* poGCSType;
2058 4 : if( (poGCSType = poGCSDict->Get("Type")) == NULL ||
2059 2 : poGCSType->GetType() != PDFObjectType_Name )
2060 : {
2061 : CPLError(CE_Failure, CPLE_AppDefined,
2062 0 : "Cannot find GCS.Type object");
2063 0 : return FALSE;
2064 : }
2065 :
2066 2 : CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str());
2067 :
2068 : /* -------------------------------------------------------------------- */
2069 : /* Extract GCS.WKT attribute */
2070 : /* -------------------------------------------------------------------- */
2071 : GDALPDFObject* poGCSWKT;
2072 4 : if( (poGCSWKT = poGCSDict->Get("WKT")) == NULL ||
2073 2 : poGCSWKT->GetType() != PDFObjectType_String )
2074 : {
2075 : CPLError(CE_Failure, CPLE_AppDefined,
2076 0 : "Cannot find GCS.WKT object");
2077 0 : return FALSE;
2078 : }
2079 :
2080 2 : CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str());
2081 2 : CPLFree(pszWKT);
2082 2 : pszWKT = CPLStrdup(poGCSWKT->GetString().c_str());
2083 :
2084 : /* -------------------------------------------------------------------- */
2085 : /* Compute geotransform */
2086 : /* -------------------------------------------------------------------- */
2087 2 : OGRSpatialReference oSRS;
2088 2 : char* pszWktTemp = pszWKT;
2089 2 : if (oSRS.importFromWkt(&pszWktTemp) != OGRERR_NONE)
2090 : {
2091 0 : CPLFree(pszWKT);
2092 0 : pszWKT = NULL;
2093 0 : return FALSE;
2094 : }
2095 :
2096 : /* For http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */
2097 : /* or http://www.agmkt.state.ny.us/soilwater/aem/gis_mapping_tools/HUC12_Albany.pdf */
2098 2 : const char* pszDatum = oSRS.GetAttrValue("Datum");
2099 2 : if (pszDatum && strncmp(pszDatum, "D_", 2) == 0)
2100 : {
2101 2 : oSRS.morphFromESRI();
2102 :
2103 2 : CPLFree(pszWKT);
2104 2 : pszWKT = NULL;
2105 2 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
2106 : {
2107 0 : CPLFree(pszWKT);
2108 0 : pszWKT = NULL;
2109 : }
2110 : else
2111 : {
2112 2 : CPLDebug("PDF", "WKT after morphFromESRI() = %s", pszWKT);
2113 : }
2114 : }
2115 :
2116 2 : OGRSpatialReference* poSRSGeog = oSRS.CloneGeogCS();
2117 :
2118 2 : OGRCoordinateTransformation* poCT = OGRCreateCoordinateTransformation( poSRSGeog, &oSRS);
2119 2 : if (poCT == NULL)
2120 : {
2121 0 : delete poSRSGeog;
2122 0 : CPLFree(pszWKT);
2123 0 : pszWKT = NULL;
2124 0 : return FALSE;
2125 : }
2126 :
2127 : GDAL_GCP asGCPS[4];
2128 :
2129 : /* Create NEATLINE */
2130 2 : poNeatLine = new OGRPolygon();
2131 4 : OGRLinearRing* poRing = new OGRLinearRing();
2132 2 : poNeatLine->addRingDirectly(poRing);
2133 :
2134 10 : for(i=0;i<4;i++)
2135 : {
2136 : /* We probably assume LPTS is 0 or 1 */
2137 8 : asGCPS[i].dfGCPPixel = (dfULX * (1 - adfLPTS[2*i+0]) + dfLRX * adfLPTS[2*i+0]) / dfMediaBoxWidth * nRasterXSize;
2138 8 : asGCPS[i].dfGCPLine = (dfULY * (1 - adfLPTS[2*i+1]) + dfLRY * adfLPTS[2*i+1]) / dfMediaBoxHeight * nRasterYSize;
2139 :
2140 8 : double lat = adfGPTS[2*i], lon = adfGPTS[2*i+1];
2141 8 : double x = lon, y = lat;
2142 8 : if (!poCT->Transform(1, &x, &y, NULL))
2143 : {
2144 : CPLError(CE_Failure, CPLE_AppDefined,
2145 0 : "Cannot reproject (%f, %f)", lon, lat);
2146 0 : delete poSRSGeog;
2147 0 : delete poCT;
2148 0 : CPLFree(pszWKT);
2149 0 : pszWKT = NULL;
2150 0 : return FALSE;
2151 : }
2152 8 : asGCPS[i].dfGCPX = x;
2153 8 : asGCPS[i].dfGCPY = y;
2154 :
2155 8 : poRing->addPoint(x, y);
2156 : }
2157 :
2158 2 : delete poSRSGeog;
2159 2 : delete poCT;
2160 :
2161 2 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
2162 : adfGeoTransform, FALSE))
2163 : {
2164 0 : CPLDebug("PDF", "Could not compute GT with exact match. Try with approximate");
2165 0 : if (!GDALGCPsToGeoTransform( 4, asGCPS,
2166 : adfGeoTransform, TRUE))
2167 : {
2168 : CPLError(CE_Failure, CPLE_AppDefined,
2169 0 : "Could not compute GT with approximate match.");
2170 0 : return FALSE;
2171 : }
2172 : }
2173 2 : bGeoTransformValid = TRUE;
2174 :
2175 : /* If the non scaling terms of the geotransform are significantly smaller than */
2176 : /* the pixel size, then nullify them as being just artifacts of reprojection and */
2177 : /* GDALGCPsToGeoTransform() numerical imprecisions */
2178 2 : double dfPixelSize = MIN(fabs(adfGeoTransform[1]), fabs(adfGeoTransform[5]));
2179 2 : double dfRotationShearTerm = MAX(fabs(adfGeoTransform[2]), fabs(adfGeoTransform[4]));
2180 2 : if (dfRotationShearTerm < 1e-5 * dfPixelSize)
2181 : {
2182 2 : double dfLRX = adfGeoTransform[0] + nRasterXSize * adfGeoTransform[1] + nRasterYSize * adfGeoTransform[2];
2183 2 : double dfLRY = adfGeoTransform[3] + nRasterXSize * adfGeoTransform[4] + nRasterYSize * adfGeoTransform[5];
2184 2 : adfGeoTransform[1] = (dfLRX - adfGeoTransform[0]) / nRasterXSize;
2185 2 : adfGeoTransform[5] = (dfLRY - adfGeoTransform[3]) / nRasterYSize;
2186 2 : adfGeoTransform[2] = adfGeoTransform[4] = 0;
2187 : }
2188 :
2189 : /* -------------------------------------------------------------------- */
2190 : /* Extract PointData attribute */
2191 : /* -------------------------------------------------------------------- */
2192 : GDALPDFObject* poPointData;
2193 2 : if( (poPointData = poVPEltDict->Get("PtData")) != NULL &&
2194 0 : poPointData->GetType() == PDFObjectType_Dictionary )
2195 : {
2196 0 : CPLDebug("PDF", "Found PointData");
2197 : }
2198 :
2199 2 : return TRUE;
2200 : }
2201 :
2202 : /************************************************************************/
2203 : /* GetProjectionRef() */
2204 : /************************************************************************/
2205 :
2206 3 : const char* PDFDataset::GetProjectionRef()
2207 : {
2208 3 : if (pszWKT)
2209 3 : return pszWKT;
2210 0 : return "";
2211 : }
2212 :
2213 : /************************************************************************/
2214 : /* GetGeoTransform() */
2215 : /************************************************************************/
2216 :
2217 3 : CPLErr PDFDataset::GetGeoTransform( double * padfTransform )
2218 :
2219 : {
2220 3 : memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
2221 :
2222 3 : return( (bGeoTransformValid) ? CE_None : CE_Failure );
2223 : }
2224 :
2225 : /************************************************************************/
2226 : /* GDALRegister_PDF() */
2227 : /************************************************************************/
2228 :
2229 558 : void GDALRegister_PDF()
2230 :
2231 : {
2232 : GDALDriver *poDriver;
2233 :
2234 558 : if (! GDAL_CHECK_VERSION("PDF driver"))
2235 0 : return;
2236 :
2237 558 : if( GDALGetDriverByName( "PDF" ) == NULL )
2238 : {
2239 537 : poDriver = new GDALDriver();
2240 :
2241 537 : poDriver->SetDescription( "PDF" );
2242 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
2243 537 : "Geospatial PDF" );
2244 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
2245 537 : "frmt_pdf.html" );
2246 537 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pdf" );
2247 :
2248 : #ifdef USE_POPPLER
2249 537 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
2250 : #endif
2251 :
2252 537 : poDriver->pfnOpen = PDFDataset::Open;
2253 537 : poDriver->pfnIdentify = PDFDataset::Identify;
2254 :
2255 537 : GetGDALDriverManager()->RegisterDriver( poDriver );
2256 : }
2257 : }
2258 :
|