EVOLUTION-MANAGER
Edit File: pdfcreatecopy.cpp
/****************************************************************************** * $Id: pdfcreatecopy.cpp 27991 2014-11-21 09:09:00Z rouault $ * * Project: PDF driver * Purpose: GDALDataset driver for PDF dataset. * Author: Even Rouault, <even dot rouault at mines dash paris dot org> * ****************************************************************************** * Copyright (c) 2012-2014, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ /* hack for PDF driver and poppler >= 0.15.0 that defines incompatible "typedef bool GBool" */ /* in include/poppler/goo/gtypes.h with the one defined in cpl_port.h */ #define CPL_GBOOL_DEFINED #include "pdfdataset.h" #include "pdfcreatecopy.h" #include "cpl_vsi_virtual.h" #include "cpl_conv.h" #include "cpl_error.h" #include "ogr_spatialref.h" #include "ogr_geometry.h" #include "vrtdataset.h" #include "pdfobject.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* Cf PDF reference v1.7, Appendix C, page 993 */ #define MAXIMUM_SIZE_IN_UNITS 14400 CPL_CVSID("$Id: pdfcreatecopy.cpp 27991 2014-11-21 09:09:00Z rouault $"); #define PIXEL_TO_GEO_X(x,y) adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2] #define PIXEL_TO_GEO_Y(x,y) adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5] class GDALFakePDFDataset : public GDALDataset { public: GDALFakePDFDataset() {} }; /************************************************************************/ /* Init() */ /************************************************************************/ void GDALPDFWriter::Init() { nPageResourceId = 0; nStructTreeRootId = 0; nCatalogId = nCatalogGen = 0; bInWriteObj = FALSE; nInfoId = nInfoGen = 0; nXMPId = nXMPGen = 0; nNamesId = 0; nLastStartXRef = 0; nLastXRefSize = 0; bCanUpdate = FALSE; } /************************************************************************/ /* GDALPDFWriter() */ /************************************************************************/ GDALPDFWriter::GDALPDFWriter(VSILFILE* fpIn, int bAppend) : fp(fpIn) { Init(); if (!bAppend) { VSIFPrintfL(fp, "%%PDF-1.6\n"); /* See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate that the content will be binary */ VSIFPrintfL(fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF); nPageResourceId = AllocNewObject(); nCatalogId = AllocNewObject(); } } /************************************************************************/ /* ~GDALPDFWriter() */ /************************************************************************/ GDALPDFWriter::~GDALPDFWriter() { Close(); } /************************************************************************/ /* ParseIndirectRef() */ /************************************************************************/ static int ParseIndirectRef(const char* pszStr, int& nNum, int &nGen) { while(*pszStr == ' ') pszStr ++; nNum = atoi(pszStr); while(*pszStr >= '0' && *pszStr <= '9') pszStr ++; if (*pszStr != ' ') return FALSE; while(*pszStr == ' ') pszStr ++; nGen = atoi(pszStr); while(*pszStr >= '0' && *pszStr <= '9') pszStr ++; if (*pszStr != ' ') return FALSE; while(*pszStr == ' ') pszStr ++; return *pszStr == 'R'; } /************************************************************************/ /* ParseTrailerAndXRef() */ /************************************************************************/ int GDALPDFWriter::ParseTrailerAndXRef() { VSIFSeekL(fp, 0, SEEK_END); char szBuf[1024+1]; vsi_l_offset nOffset = VSIFTellL(fp); if (nOffset > 128) nOffset -= 128; else nOffset = 0; /* Find startxref section */ VSIFSeekL(fp, nOffset, SEEK_SET); int nRead = (int) VSIFReadL(szBuf, 1, 128, fp); szBuf[nRead] = 0; if (nRead < 9) return FALSE; const char* pszStartXRef = NULL; int i; for(i = nRead - 9; i>= 0; i --) { if (strncmp(szBuf + i, "startxref", 9) == 0) { pszStartXRef = szBuf + i; break; } } if (pszStartXRef == NULL) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref"); return FALSE; } pszStartXRef += 9; while(*pszStartXRef == '\r' || *pszStartXRef == '\n') pszStartXRef ++; if (*pszStartXRef == '\0') { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref"); return FALSE; } nLastStartXRef = CPLScanUIntBig(pszStartXRef,16); /* Skip to beginning of xref section */ VSIFSeekL(fp, nLastStartXRef, SEEK_SET); /* And skip to trailer */ const char* pszLine; while( (pszLine = CPLReadLineL(fp)) != NULL) { if (strncmp(pszLine, "trailer", 7) == 0) break; } if( pszLine == NULL ) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer"); return FALSE; } /* Read trailer content */ nRead = (int) VSIFReadL(szBuf, 1, 1024, fp); szBuf[nRead] = 0; /* Find XRef size */ const char* pszSize = strstr(szBuf, "/Size"); if (pszSize == NULL) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Size"); return FALSE; } pszSize += 5; while(*pszSize == ' ') pszSize ++; nLastXRefSize = atoi(pszSize); /* Find Root object */ const char* pszRoot = strstr(szBuf, "/Root"); if (pszRoot == NULL) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Root"); return FALSE; } pszRoot += 5; while(*pszRoot == ' ') pszRoot ++; if (!ParseIndirectRef(pszRoot, nCatalogId, nCatalogGen)) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Root"); return FALSE; } /* Find Info object */ const char* pszInfo = strstr(szBuf, "/Info"); if (pszInfo != NULL) { pszInfo += 5; while(*pszInfo == ' ') pszInfo ++; if (!ParseIndirectRef(pszInfo, nInfoId, nInfoGen)) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Info"); nInfoId = nInfoGen = 0; } } VSIFSeekL(fp, 0, SEEK_END); return TRUE; } /************************************************************************/ /* Close() */ /************************************************************************/ void GDALPDFWriter::Close() { if (fp) { CPLAssert(!bInWriteObj); if (nPageResourceId) { WritePages(); WriteXRefTableAndTrailer(); } else if (bCanUpdate) { WriteXRefTableAndTrailer(); } VSIFCloseL(fp); } fp = NULL; } /************************************************************************/ /* UpdateProj() */ /************************************************************************/ void GDALPDFWriter::UpdateProj(GDALDataset* poSrcDS, double dfDPI, GDALPDFDictionaryRW* poPageDict, int nPageNum, int nPageGen) { bCanUpdate = TRUE; if ((int)asXRefEntries.size() < nLastXRefSize - 1) asXRefEntries.resize(nLastXRefSize - 1); int nViewportId = 0; int nLGIDictId = 0; CPLAssert(nPageNum != 0); CPLAssert(poPageDict != NULL); PDFMargins sMargins = {0, 0, 0, 0}; const char* pszGEO_ENCODING = CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); if (EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH")) nViewportId = WriteSRS_ISO32000(poSrcDS, dfDPI * USER_UNIT_IN_INCH, NULL, &sMargins, TRUE); if (EQUAL(pszGEO_ENCODING, "OGC_BP") || EQUAL(pszGEO_ENCODING, "BOTH")) nLGIDictId = WriteSRS_OGC_BP(poSrcDS, dfDPI * USER_UNIT_IN_INCH, NULL, &sMargins); #ifdef invalidate_xref_entry GDALPDFObject* poVP = poPageDict->Get("VP"); if (poVP) { if (poVP->GetType() == PDFObjectType_Array && poVP->GetArray()->GetLength() == 1) poVP = poVP->GetArray()->Get(0); int nVPId = poVP->GetRefNum(); if (nVPId) { asXRefEntries[nVPId - 1].bFree = TRUE; asXRefEntries[nVPId - 1].nGen ++; } } #endif poPageDict->Remove("VP"); poPageDict->Remove("LGIDict"); if (nViewportId) { poPageDict->Add("VP", &((new GDALPDFArrayRW())-> Add(nViewportId, 0))); } if (nLGIDictId) { poPageDict->Add("LGIDict", nLGIDictId, 0); } StartObj(nPageNum, nPageGen); VSIFPrintfL(fp, "%s\n", poPageDict->Serialize().c_str()); EndObj(); } /************************************************************************/ /* UpdateInfo() */ /************************************************************************/ void GDALPDFWriter::UpdateInfo(GDALDataset* poSrcDS) { bCanUpdate = TRUE; if ((int)asXRefEntries.size() < nLastXRefSize - 1) asXRefEntries.resize(nLastXRefSize - 1); int nNewInfoId = SetInfo(poSrcDS, NULL); /* Write empty info, because podofo driver will find the dangling info instead */ if (nNewInfoId == 0 && nInfoId != 0) { #ifdef invalidate_xref_entry asXRefEntries[nInfoId - 1].bFree = TRUE; asXRefEntries[nInfoId - 1].nGen ++; #else StartObj(nInfoId, nInfoGen); VSIFPrintfL(fp, "<< >>\n"); EndObj(); #endif } } /************************************************************************/ /* UpdateXMP() */ /************************************************************************/ void GDALPDFWriter::UpdateXMP(GDALDataset* poSrcDS, GDALPDFDictionaryRW* poCatalogDict) { bCanUpdate = TRUE; if ((int)asXRefEntries.size() < nLastXRefSize - 1) asXRefEntries.resize(nLastXRefSize - 1); CPLAssert(nCatalogId != 0); CPLAssert(poCatalogDict != NULL); GDALPDFObject* poMetadata = poCatalogDict->Get("Metadata"); if (poMetadata) { nXMPId = poMetadata->GetRefNum(); nXMPGen = poMetadata->GetRefGen(); } poCatalogDict->Remove("Metadata"); int nNewXMPId = SetXMP(poSrcDS, NULL); /* Write empty metadata, because podofo driver will find the dangling info instead */ if (nNewXMPId == 0 && nXMPId != 0) { StartObj(nXMPId, nXMPGen); VSIFPrintfL(fp, "<< >>\n"); EndObj(); } if (nXMPId) poCatalogDict->Add("Metadata", nXMPId, 0); StartObj(nCatalogId, nCatalogGen); VSIFPrintfL(fp, "%s\n", poCatalogDict->Serialize().c_str()); EndObj(); } /************************************************************************/ /* AllocNewObject() */ /************************************************************************/ int GDALPDFWriter::AllocNewObject() { asXRefEntries.push_back(GDALXRefEntry()); return (int)asXRefEntries.size(); } /************************************************************************/ /* WriteXRefTableAndTrailer() */ /************************************************************************/ void GDALPDFWriter::WriteXRefTableAndTrailer() { vsi_l_offset nOffsetXREF = VSIFTellL(fp); VSIFPrintfL(fp, "xref\n"); char buffer[16]; if (bCanUpdate) { VSIFPrintfL(fp, "0 1\n"); VSIFPrintfL(fp, "0000000000 65535 f \n"); for(size_t i=0;i<asXRefEntries.size();) { if (asXRefEntries[i].nOffset != 0 || asXRefEntries[i].bFree) { /* Find number of consecutive objects */ size_t nCount = 1; while(i + nCount <asXRefEntries.size() && (asXRefEntries[i + nCount].nOffset != 0 || asXRefEntries[i + nCount].bFree)) nCount ++; VSIFPrintfL(fp, "%d %d\n", (int)i + 1, (int)nCount); size_t iEnd = i + nCount; for(; i < iEnd; i++) { snprintf (buffer, sizeof(buffer), "%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", asXRefEntries[i].nOffset); VSIFPrintfL(fp, "%s %05d %c \n", buffer, asXRefEntries[i].nGen, asXRefEntries[i].bFree ? 'f' : 'n'); } } else { i++; } } } else { VSIFPrintfL(fp, "%d %d\n", 0, (int)asXRefEntries.size() + 1); VSIFPrintfL(fp, "0000000000 65535 f \n"); for(size_t i=0;i<asXRefEntries.size();i++) { snprintf (buffer, sizeof(buffer), "%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", asXRefEntries[i].nOffset); VSIFPrintfL(fp, "%s %05d n \n", buffer, asXRefEntries[i].nGen); } } VSIFPrintfL(fp, "trailer\n"); GDALPDFDictionaryRW oDict; oDict.Add("Size", (int)asXRefEntries.size() + 1) .Add("Root", nCatalogId, nCatalogGen); if (nInfoId) oDict.Add("Info", nInfoId, nInfoGen); if (nLastStartXRef) oDict.Add("Prev", (double)nLastStartXRef); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); VSIFPrintfL(fp, "startxref\n" CPL_FRMT_GUIB "\n" "%%%%EOF\n", nOffsetXREF); } /************************************************************************/ /* StartObj() */ /************************************************************************/ void GDALPDFWriter::StartObj(int nObjectId, int nGen) { CPLAssert(!bInWriteObj); CPLAssert(nObjectId - 1 < (int)asXRefEntries.size()); CPLAssert(asXRefEntries[nObjectId - 1].nOffset == 0); asXRefEntries[nObjectId - 1].nOffset = VSIFTellL(fp); asXRefEntries[nObjectId - 1].nGen = nGen; VSIFPrintfL(fp, "%d %d obj\n", nObjectId, nGen); bInWriteObj = TRUE; } /************************************************************************/ /* EndObj() */ /************************************************************************/ void GDALPDFWriter::EndObj() { CPLAssert(bInWriteObj); VSIFPrintfL(fp, "endobj\n"); bInWriteObj = FALSE; } /************************************************************************/ /* GDALPDFFind4Corners() */ /************************************************************************/ static void GDALPDFFind4Corners(const GDAL_GCP* pasGCPList, int& iUL, int& iUR, int& iLR, int& iLL) { double dfMeanX = 0, dfMeanY = 0; int i; iUL = 0; iUR = 0; iLR = 0; iLL = 0; for(i = 0; i < 4; i++ ) { dfMeanX += pasGCPList[i].dfGCPPixel; dfMeanY += pasGCPList[i].dfGCPLine; } dfMeanX /= 4; dfMeanY /= 4; for(i = 0; i < 4; i++ ) { if (pasGCPList[i].dfGCPPixel < dfMeanX && pasGCPList[i].dfGCPLine < dfMeanY ) iUL = i; else if (pasGCPList[i].dfGCPPixel > dfMeanX && pasGCPList[i].dfGCPLine < dfMeanY ) iUR = i; else if (pasGCPList[i].dfGCPPixel > dfMeanX && pasGCPList[i].dfGCPLine > dfMeanY ) iLR = i; else if (pasGCPList[i].dfGCPPixel < dfMeanX && pasGCPList[i].dfGCPLine > dfMeanY ) iLL = i; } } /************************************************************************/ /* WriteSRS_ISO32000() */ /************************************************************************/ int GDALPDFWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS, double dfUserUnit, const char* pszNEATLINE, PDFMargins* psMargins, int bWriteViewport) { int nWidth = poSrcDS->GetRasterXSize(); int nHeight = poSrcDS->GetRasterYSize(); const char* pszWKT = poSrcDS->GetProjectionRef(); double adfGeoTransform[6]; int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None); const GDAL_GCP* pasGCPList = (poSrcDS->GetGCPCount() == 4) ? poSrcDS->GetGCPs() : NULL; if (pasGCPList != NULL) pszWKT = poSrcDS->GetGCPProjection(); if( !bHasGT && pasGCPList == NULL ) return 0; if( pszWKT == NULL || EQUAL(pszWKT, "") ) return 0; double adfGPTS[8]; double dfULPixel = 0; double dfULLine = 0; double dfLRPixel = nWidth; double dfLRLine = nHeight; GDAL_GCP asNeatLineGCPs[4]; if (pszNEATLINE == NULL) pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE"); if( bHasGT && pszNEATLINE != NULL && pszNEATLINE[0] != '\0' ) { OGRGeometry* poGeom = NULL; OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom ); if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) { OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); double adfGeoTransformInv[6]; if( poLS != NULL && poLS->getNumPoints() == 5 && GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) ) { for(int i=0;i<4;i++) { double X = asNeatLineGCPs[i].dfGCPX = poLS->getX(i); double Y = asNeatLineGCPs[i].dfGCPY = poLS->getY(i); double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2]; double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5]; asNeatLineGCPs[i].dfGCPPixel = x; asNeatLineGCPs[i].dfGCPLine = y; } int iUL = 0, iUR = 0, iLR = 0, iLL = 0; GDALPDFFind4Corners(asNeatLineGCPs, iUL,iUR, iLR, iLL); if (fabs(asNeatLineGCPs[iUL].dfGCPPixel - asNeatLineGCPs[iLL].dfGCPPixel) > .5 || fabs(asNeatLineGCPs[iUR].dfGCPPixel - asNeatLineGCPs[iLR].dfGCPPixel) > .5 || fabs(asNeatLineGCPs[iUL].dfGCPLine - asNeatLineGCPs[iUR].dfGCPLine) > .5 || fabs(asNeatLineGCPs[iLL].dfGCPLine - asNeatLineGCPs[iLR].dfGCPLine) > .5) { CPLError(CE_Warning, CPLE_NotSupported, "Neatline coordinates should form a rectangle in pixel space. Ignoring it"); for(int i=0;i<4;i++) { CPLDebug("PDF", "pixel[%d] = %.1f, line[%d] = %.1f", i, asNeatLineGCPs[i].dfGCPPixel, i, asNeatLineGCPs[i].dfGCPLine); } } else { pasGCPList = asNeatLineGCPs; } } } delete poGeom; } if( pasGCPList ) { int iUL = 0, iUR = 0, iLR = 0, iLL = 0; GDALPDFFind4Corners(pasGCPList, iUL,iUR, iLR, iLL); if (fabs(pasGCPList[iUL].dfGCPPixel - pasGCPList[iLL].dfGCPPixel) > .5 || fabs(pasGCPList[iUR].dfGCPPixel - pasGCPList[iLR].dfGCPPixel) > .5 || fabs(pasGCPList[iUL].dfGCPLine - pasGCPList[iUR].dfGCPLine) > .5 || fabs(pasGCPList[iLL].dfGCPLine - pasGCPList[iLR].dfGCPLine) > .5) { CPLError(CE_Failure, CPLE_NotSupported, "GCPs should form a rectangle in pixel space"); return 0; } dfULPixel = pasGCPList[iUL].dfGCPPixel; dfULLine = pasGCPList[iUL].dfGCPLine; dfLRPixel = pasGCPList[iLR].dfGCPPixel; dfLRLine = pasGCPList[iLR].dfGCPLine; /* Upper-left */ adfGPTS[0] = pasGCPList[iUL].dfGCPX; adfGPTS[1] = pasGCPList[iUL].dfGCPY; /* Lower-left */ adfGPTS[2] = pasGCPList[iLL].dfGCPX; adfGPTS[3] = pasGCPList[iLL].dfGCPY; /* Lower-right */ adfGPTS[4] = pasGCPList[iLR].dfGCPX; adfGPTS[5] = pasGCPList[iLR].dfGCPY; /* Upper-right */ adfGPTS[6] = pasGCPList[iUR].dfGCPX; adfGPTS[7] = pasGCPList[iUR].dfGCPY; } else { /* Upper-left */ adfGPTS[0] = PIXEL_TO_GEO_X(0, 0); adfGPTS[1] = PIXEL_TO_GEO_Y(0, 0); /* Lower-left */ adfGPTS[2] = PIXEL_TO_GEO_X(0, nHeight); adfGPTS[3] = PIXEL_TO_GEO_Y(0, nHeight); /* Lower-right */ adfGPTS[4] = PIXEL_TO_GEO_X(nWidth, nHeight); adfGPTS[5] = PIXEL_TO_GEO_Y(nWidth, nHeight); /* Upper-right */ adfGPTS[6] = PIXEL_TO_GEO_X(nWidth, 0); adfGPTS[7] = PIXEL_TO_GEO_Y(nWidth, 0); } OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT); if( hSRS == NULL ) return 0; OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS); if( hSRSGeog == NULL ) { OSRDestroySpatialReference(hSRS); return 0; } OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog); if( hCT == NULL ) { OSRDestroySpatialReference(hSRS); OSRDestroySpatialReference(hSRSGeog); return 0; } int bSuccess = TRUE; bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 0, adfGPTS + 1, NULL ) == 1); bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 2, adfGPTS + 3, NULL ) == 1); bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 4, adfGPTS + 5, NULL ) == 1); bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 6, adfGPTS + 7, NULL ) == 1); if (!bSuccess) { OSRDestroySpatialReference(hSRS); OSRDestroySpatialReference(hSRSGeog); OCTDestroyCoordinateTransformation(hCT); return 0; } const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, NULL ); const char * pszAuthorityName = OSRGetAuthorityName( hSRS, NULL ); int nEPSGCode = 0; if( pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") && pszAuthorityCode != NULL ) nEPSGCode = atoi(pszAuthorityCode); int bIsGeographic = OSRIsGeographic(hSRS); OSRMorphToESRI(hSRS); char* pszESRIWKT = NULL; OSRExportToWkt(hSRS, &pszESRIWKT); OSRDestroySpatialReference(hSRS); OSRDestroySpatialReference(hSRSGeog); OCTDestroyCoordinateTransformation(hCT); hSRS = NULL; hSRSGeog = NULL; hCT = NULL; if (pszESRIWKT == NULL) return 0; int nViewportId = (bWriteViewport) ? AllocNewObject() : 0; int nMeasureId = AllocNewObject(); int nGCSId = AllocNewObject(); if (nViewportId) { StartObj(nViewportId); GDALPDFDictionaryRW oViewPortDict; oViewPortDict.Add("Type", GDALPDFObjectRW::CreateName("Viewport")) .Add("Name", "Layer") .Add("BBox", &((new GDALPDFArrayRW()) ->Add(dfULPixel / dfUserUnit + psMargins->nLeft) .Add((nHeight - dfLRLine) / dfUserUnit + psMargins->nBottom) .Add(dfLRPixel / dfUserUnit + psMargins->nLeft) .Add((nHeight - dfULLine) / dfUserUnit + psMargins->nBottom))) .Add("Measure", nMeasureId, 0); VSIFPrintfL(fp, "%s\n", oViewPortDict.Serialize().c_str()); EndObj(); } StartObj(nMeasureId); GDALPDFDictionaryRW oMeasureDict; oMeasureDict .Add("Type", GDALPDFObjectRW::CreateName("Measure")) .Add("Subtype", GDALPDFObjectRW::CreateName("GEO")) .Add("Bounds", &((new GDALPDFArrayRW()) ->Add(0).Add(1). Add(0).Add(0). Add(1).Add(0). Add(1).Add(1))) .Add("GPTS", &((new GDALPDFArrayRW()) ->Add(adfGPTS[1]).Add(adfGPTS[0]). Add(adfGPTS[3]).Add(adfGPTS[2]). Add(adfGPTS[5]).Add(adfGPTS[4]). Add(adfGPTS[7]).Add(adfGPTS[6]))) .Add("LPTS", &((new GDALPDFArrayRW()) ->Add(0).Add(1). Add(0).Add(0). Add(1).Add(0). Add(1).Add(1))) .Add("GCS", nGCSId, 0); VSIFPrintfL(fp, "%s\n", oMeasureDict.Serialize().c_str()); EndObj(); StartObj(nGCSId); GDALPDFDictionaryRW oGCSDict; oGCSDict.Add("Type", GDALPDFObjectRW::CreateName(bIsGeographic ? "GEOGCS" : "PROJCS")) .Add("WKT", pszESRIWKT); if (nEPSGCode) oGCSDict.Add("EPSG", nEPSGCode); VSIFPrintfL(fp, "%s\n", oGCSDict.Serialize().c_str()); EndObj(); CPLFree(pszESRIWKT); return nViewportId ? nViewportId : nMeasureId; } /************************************************************************/ /* GDALPDFBuildOGC_BP_Datum() */ /************************************************************************/ static GDALPDFObject* GDALPDFBuildOGC_BP_Datum(const OGRSpatialReference* poSRS) { const OGR_SRSNode* poDatumNode = poSRS->GetAttrNode("DATUM"); const char* pszDatumDescription = NULL; if (poDatumNode && poDatumNode->GetChildCount() > 0) pszDatumDescription = poDatumNode->GetChild(0)->GetValue(); GDALPDFObjectRW* poPDFDatum = NULL; if (pszDatumDescription) { double dfSemiMajor = poSRS->GetSemiMajor(); double dfInvFlattening = poSRS->GetInvFlattening(); int nEPSGDatum = -1; const char *pszAuthority = poSRS->GetAuthorityName( "DATUM" ); if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") ) nEPSGDatum = atoi(poSRS->GetAuthorityCode( "DATUM" )); if( EQUAL(pszDatumDescription,SRS_DN_WGS84) || nEPSGDatum == 6326 ) poPDFDatum = GDALPDFObjectRW::CreateString("WGE"); else if( EQUAL(pszDatumDescription, SRS_DN_NAD27) || nEPSGDatum == 6267 ) poPDFDatum = GDALPDFObjectRW::CreateString("NAS"); else if( EQUAL(pszDatumDescription, SRS_DN_NAD83) || nEPSGDatum == 6269 ) poPDFDatum = GDALPDFObjectRW::CreateString("NAR"); else { CPLDebug("PDF", "Unhandled datum name (%s). Write datum parameters then.", pszDatumDescription); GDALPDFDictionaryRW* poPDFDatumDict = new GDALPDFDictionaryRW(); poPDFDatum = GDALPDFObjectRW::CreateDictionary(poPDFDatumDict); const OGR_SRSNode* poSpheroidNode = poSRS->GetAttrNode("SPHEROID"); if (poSpheroidNode && poSpheroidNode->GetChildCount() >= 3) { poPDFDatumDict->Add("Description", pszDatumDescription); const char* pszEllipsoidCode = NULL; #ifdef disabled_because_terrago_toolbar_does_not_like_it if( ABS(dfSemiMajor-6378249.145) < 0.01 && ABS(dfInvFlattening-293.465) < 0.0001 ) { pszEllipsoidCode = "CD"; /* Clark 1880 */ } else if( ABS(dfSemiMajor-6378245.0) < 0.01 && ABS(dfInvFlattening-298.3) < 0.0001 ) { pszEllipsoidCode = "KA"; /* Krassovsky */ } else if( ABS(dfSemiMajor-6378388.0) < 0.01 && ABS(dfInvFlattening-297.0) < 0.0001 ) { pszEllipsoidCode = "IN"; /* International 1924 */ } else if( ABS(dfSemiMajor-6378160.0) < 0.01 && ABS(dfInvFlattening-298.25) < 0.0001 ) { pszEllipsoidCode = "AN"; /* Australian */ } else if( ABS(dfSemiMajor-6377397.155) < 0.01 && ABS(dfInvFlattening-299.1528128) < 0.0001 ) { pszEllipsoidCode = "BR"; /* Bessel 1841 */ } else if( ABS(dfSemiMajor-6377483.865) < 0.01 && ABS(dfInvFlattening-299.1528128) < 0.0001 ) { pszEllipsoidCode = "BN"; /* Bessel 1841 (Namibia / Schwarzeck)*/ } #if 0 else if( ABS(dfSemiMajor-6378160.0) < 0.01 && ABS(dfInvFlattening-298.247167427) < 0.0001 ) { pszEllipsoidCode = "GRS67"; /* GRS 1967 */ } #endif else if( ABS(dfSemiMajor-6378137) < 0.01 && ABS(dfInvFlattening-298.257222101) < 0.000001 ) { pszEllipsoidCode = "RF"; /* GRS 1980 */ } else if( ABS(dfSemiMajor-6378206.4) < 0.01 && ABS(dfInvFlattening-294.9786982) < 0.0001 ) { pszEllipsoidCode = "CC"; /* Clarke 1866 */ } else if( ABS(dfSemiMajor-6377340.189) < 0.01 && ABS(dfInvFlattening-299.3249646) < 0.0001 ) { pszEllipsoidCode = "AM"; /* Modified Airy */ } else if( ABS(dfSemiMajor-6377563.396) < 0.01 && ABS(dfInvFlattening-299.3249646) < 0.0001 ) { pszEllipsoidCode = "AA"; /* Airy */ } else if( ABS(dfSemiMajor-6378200) < 0.01 && ABS(dfInvFlattening-298.3) < 0.0001 ) { pszEllipsoidCode = "HE"; /* Helmert 1906 */ } else if( ABS(dfSemiMajor-6378155) < 0.01 && ABS(dfInvFlattening-298.3) < 0.0001 ) { pszEllipsoidCode = "FA"; /* Modified Fischer 1960 */ } #if 0 else if( ABS(dfSemiMajor-6377298.556) < 0.01 && ABS(dfInvFlattening-300.8017) < 0.0001 ) { pszEllipsoidCode = "evrstSS"; /* Everest (Sabah & Sarawak) */ } else if( ABS(dfSemiMajor-6378165.0) < 0.01 && ABS(dfInvFlattening-298.3) < 0.0001 ) { pszEllipsoidCode = "WGS60"; } else if( ABS(dfSemiMajor-6378145.0) < 0.01 && ABS(dfInvFlattening-298.25) < 0.0001 ) { pszEllipsoidCode = "WGS66"; } #endif else if( ABS(dfSemiMajor-6378135.0) < 0.01 && ABS(dfInvFlattening-298.26) < 0.0001 ) { pszEllipsoidCode = "WD"; } else if( ABS(dfSemiMajor-6378137.0) < 0.01 && ABS(dfInvFlattening-298.257223563) < 0.000001 ) { pszEllipsoidCode = "WE"; } #endif if( pszEllipsoidCode != NULL ) { poPDFDatumDict->Add("Ellipsoid", pszEllipsoidCode); } else { const char* pszEllipsoidDescription = poSpheroidNode->GetChild(0)->GetValue(); CPLDebug("PDF", "Unhandled ellipsoid name (%s). Write ellipsoid parameters then.", pszEllipsoidDescription); poPDFDatumDict->Add("Ellipsoid", &((new GDALPDFDictionaryRW()) ->Add("Description", pszEllipsoidDescription) .Add("SemiMajorAxis", dfSemiMajor, TRUE) .Add("InvFlattening", dfInvFlattening, TRUE))); } const OGR_SRSNode *poTOWGS84 = poSRS->GetAttrNode( "TOWGS84" ); if( poTOWGS84 != NULL && poTOWGS84->GetChildCount() >= 3 && (poTOWGS84->GetChildCount() < 7 || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"") && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"") && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"") && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) ) { poPDFDatumDict->Add("ToWGS84", &((new GDALPDFDictionaryRW()) ->Add("dx", poTOWGS84->GetChild(0)->GetValue()) .Add("dy", poTOWGS84->GetChild(1)->GetValue()) .Add("dz", poTOWGS84->GetChild(2)->GetValue())) ); } else if( poTOWGS84 != NULL && poTOWGS84->GetChildCount() >= 7) { poPDFDatumDict->Add("ToWGS84", &((new GDALPDFDictionaryRW()) ->Add("dx", poTOWGS84->GetChild(0)->GetValue()) .Add("dy", poTOWGS84->GetChild(1)->GetValue()) .Add("dz", poTOWGS84->GetChild(2)->GetValue()) .Add("rx", poTOWGS84->GetChild(3)->GetValue()) .Add("ry", poTOWGS84->GetChild(4)->GetValue()) .Add("rz", poTOWGS84->GetChild(5)->GetValue()) .Add("sf", poTOWGS84->GetChild(6)->GetValue())) ); } } } } else { CPLError(CE_Warning, CPLE_NotSupported, "No datum name. Defaulting to WGS84."); } if (poPDFDatum == NULL) poPDFDatum = GDALPDFObjectRW::CreateString("WGE"); return poPDFDatum; } /************************************************************************/ /* GDALPDFBuildOGC_BP_Projection() */ /************************************************************************/ static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS) { const char* pszProjectionOGCBP = "GEOGRAPHIC"; const char *pszProjection = poSRS->GetAttrValue("PROJECTION"); GDALPDFDictionaryRW* poProjectionDict = new GDALPDFDictionaryRW(); poProjectionDict->Add("Type", GDALPDFObjectRW::CreateName("Projection")); poProjectionDict->Add("Datum", GDALPDFBuildOGC_BP_Datum(poSRS)); if( pszProjection == NULL ) { if( poSRS->IsGeographic() ) pszProjectionOGCBP = "GEOGRAPHIC"; else if( poSRS->IsLocal() ) pszProjectionOGCBP = "LOCAL CARTESIAN"; else { CPLError(CE_Warning, CPLE_NotSupported, "Unsupported SRS type"); delete poProjectionDict; return NULL; } } else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ) { int bNorth; int nZone = poSRS->GetUTMZone( &bNorth ); if( nZone != 0 ) { pszProjectionOGCBP = "UT"; poProjectionDict->Add("Hemisphere", (bNorth) ? "N" : "S"); poProjectionDict->Add("Zone", nZone); } else { double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.L); double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); /* OGC_BP supports representing numbers as strings for better precision */ /* so use it */ pszProjectionOGCBP = "TC"; poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); poProjectionDict->Add("ScaleFactor", dfScale, TRUE); poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); } } else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) ) { double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); if( fabs(dfCenterLat) == 90.0 && dfCenterLong == 0.0 && dfScale == 0.994 && dfFalseEasting == 200000.0 && dfFalseNorthing == 200000.0) { pszProjectionOGCBP = "UP"; poProjectionDict->Add("Hemisphere", (dfCenterLat > 0) ? "N" : "S"); } else { pszProjectionOGCBP = "PG"; poProjectionDict->Add("LatitudeTrueScale", dfCenterLat, TRUE); poProjectionDict->Add("LongitudeDownFromPole", dfCenterLong, TRUE); poProjectionDict->Add("ScaleFactor", dfScale, TRUE); poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); } } else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP)) { double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); double dfStdP2 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); pszProjectionOGCBP = "LE"; poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE); poProjectionDict->Add("StandardParallelTwo", dfStdP2, TRUE); poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); } else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) { double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); pszProjectionOGCBP = "MC"; poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); poProjectionDict->Add("ScaleFactor", dfScale, TRUE); poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); } #ifdef not_supported else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) ) { double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); pszProjectionOGCBP = "MC"; poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE); poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); } #endif else { CPLError(CE_Warning, CPLE_NotSupported, "Unhandled projection type (%s) for now", pszProjection); } poProjectionDict->Add("ProjectionType", pszProjectionOGCBP); if( poSRS->IsProjected() ) { char* pszUnitName = NULL; double dfLinearUnits = poSRS->GetLinearUnits(&pszUnitName); if (dfLinearUnits == 1.0) poProjectionDict->Add("Units", "M"); else if (dfLinearUnits == 0.3048) poProjectionDict->Add("Units", "FT"); } return poProjectionDict; } /************************************************************************/ /* WriteSRS_OGC_BP() */ /************************************************************************/ int GDALPDFWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS, double dfUserUnit, const char* pszNEATLINE, PDFMargins* psMargins) { int nWidth = poSrcDS->GetRasterXSize(); int nHeight = poSrcDS->GetRasterYSize(); const char* pszWKT = poSrcDS->GetProjectionRef(); double adfGeoTransform[6]; int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None); int nGCPCount = poSrcDS->GetGCPCount(); const GDAL_GCP* pasGCPList = (nGCPCount >= 4) ? poSrcDS->GetGCPs() : NULL; if (pasGCPList != NULL) pszWKT = poSrcDS->GetGCPProjection(); if( !bHasGT && pasGCPList == NULL ) return 0; if( pszWKT == NULL || EQUAL(pszWKT, "") ) return 0; if( !bHasGT ) { if (!GDALGCPsToGeoTransform( nGCPCount, pasGCPList, adfGeoTransform, FALSE )) { CPLDebug("PDF", "Could not compute GT with exact match. Writing Registration then"); } else { bHasGT = TRUE; } } OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT); if( hSRS == NULL ) return 0; const OGRSpatialReference* poSRS = (const OGRSpatialReference*)hSRS; GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS); if (poProjectionDict == NULL) { OSRDestroySpatialReference(hSRS); return 0; } GDALPDFArrayRW* poNeatLineArray = NULL; if (pszNEATLINE == NULL) pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE"); if( bHasGT && pszNEATLINE != NULL && !EQUAL(pszNEATLINE, "NO") && pszNEATLINE[0] != '\0' ) { OGRGeometry* poGeom = NULL; OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom ); if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) { OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); double adfGeoTransformInv[6]; if( poLS != NULL && poLS->getNumPoints() >= 5 && GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) ) { poNeatLineArray = new GDALPDFArrayRW(); // FIXME : ensure that they are in clockwise order ? for(int i=0;i<poLS->getNumPoints() - 1;i++) { double X = poLS->getX(i); double Y = poLS->getY(i); double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2]; double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5]; poNeatLineArray->Add(x / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight - y) / dfUserUnit + psMargins->nBottom, TRUE); } } } delete poGeom; } if( pszNEATLINE != NULL && EQUAL(pszNEATLINE, "NO") ) { // Do nothing } else if( pasGCPList && poNeatLineArray == NULL) { if (nGCPCount == 4) { int iUL = 0, iUR = 0, iLR = 0, iLL = 0; GDALPDFFind4Corners(pasGCPList, iUL,iUR, iLR, iLL); double adfNL[8]; adfNL[0] = pasGCPList[iUL].dfGCPPixel / dfUserUnit + psMargins->nLeft; adfNL[1] = (nHeight - pasGCPList[iUL].dfGCPLine) / dfUserUnit + psMargins->nBottom; adfNL[2] = pasGCPList[iLL].dfGCPPixel / dfUserUnit + psMargins->nLeft; adfNL[3] = (nHeight - pasGCPList[iLL].dfGCPLine) / dfUserUnit + psMargins->nBottom; adfNL[4] = pasGCPList[iLR].dfGCPPixel / dfUserUnit + psMargins->nLeft; adfNL[5] = (nHeight - pasGCPList[iLR].dfGCPLine) / dfUserUnit + psMargins->nBottom; adfNL[6] = pasGCPList[iUR].dfGCPPixel / dfUserUnit + psMargins->nLeft; adfNL[7] = (nHeight - pasGCPList[iUR].dfGCPLine) / dfUserUnit + psMargins->nBottom; poNeatLineArray = new GDALPDFArrayRW(); poNeatLineArray->Add(adfNL, 8, TRUE); } else { poNeatLineArray = new GDALPDFArrayRW(); // FIXME : ensure that they are in clockwise order ? int i; for(i = 0; i < nGCPCount; i++) { poNeatLineArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE); } } } else if (poNeatLineArray == NULL) { poNeatLineArray = new GDALPDFArrayRW(); poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE); poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE); poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE); poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE); poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE); } int nLGIDictId = AllocNewObject(); StartObj(nLGIDictId); GDALPDFDictionaryRW oLGIDict; oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict")) .Add("Version", "2.1"); if( bHasGT ) { double adfCTM[6]; double dfX1 = psMargins->nLeft; double dfY2 = nHeight / dfUserUnit + psMargins->nBottom ; adfCTM[0] = adfGeoTransform[1] * dfUserUnit; adfCTM[1] = adfGeoTransform[2] * dfUserUnit; adfCTM[2] = - adfGeoTransform[4] * dfUserUnit; adfCTM[3] = - adfGeoTransform[5] * dfUserUnit; adfCTM[4] = adfGeoTransform[0] - (adfCTM[0] * dfX1 + adfCTM[2] * dfY2); adfCTM[5] = adfGeoTransform[3] - (adfCTM[1] * dfX1 + adfCTM[3] * dfY2); oLGIDict.Add("CTM", &((new GDALPDFArrayRW())->Add(adfCTM, 6, TRUE))); } else { GDALPDFArrayRW* poRegistrationArray = new GDALPDFArrayRW(); int i; for(i = 0; i < nGCPCount; i++) { GDALPDFArrayRW* poPTArray = new GDALPDFArrayRW(); poPTArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE); poPTArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE); poPTArray->Add(pasGCPList[i].dfGCPX, TRUE); poPTArray->Add(pasGCPList[i].dfGCPY, TRUE); poRegistrationArray->Add(poPTArray); } oLGIDict.Add("Registration", poRegistrationArray); } if( poNeatLineArray ) { oLGIDict.Add("Neatline", poNeatLineArray); } const OGR_SRSNode* poNode = poSRS->GetRoot(); if( poNode != NULL ) poNode = poNode->GetChild(0); const char* pszDescription = NULL; if( poNode != NULL ) pszDescription = poNode->GetValue(); if( pszDescription ) { oLGIDict.Add("Description", pszDescription); } oLGIDict.Add("Projection", poProjectionDict); /* GDAL extension */ if( CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) ) poProjectionDict->Add("WKT", pszWKT); VSIFPrintfL(fp, "%s\n", oLGIDict.Serialize().c_str()); EndObj(); OSRDestroySpatialReference(hSRS); return nLGIDictId; } /************************************************************************/ /* GDALPDFGetValueFromDSOrOption() */ /************************************************************************/ static const char* GDALPDFGetValueFromDSOrOption(GDALDataset* poSrcDS, char** papszOptions, const char* pszKey) { const char* pszValue = CSLFetchNameValue(papszOptions, pszKey); if (pszValue == NULL) pszValue = poSrcDS->GetMetadataItem(pszKey); if (pszValue != NULL && pszValue[0] == '\0') return NULL; else return pszValue; } /************************************************************************/ /* SetInfo() */ /************************************************************************/ int GDALPDFWriter::SetInfo(GDALDataset* poSrcDS, char** papszOptions) { const char* pszAUTHOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "AUTHOR"); const char* pszPRODUCER = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "PRODUCER"); const char* pszCREATOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATOR"); const char* pszCREATION_DATE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATION_DATE"); const char* pszSUBJECT = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "SUBJECT"); const char* pszTITLE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "TITLE"); const char* pszKEYWORDS = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "KEYWORDS"); if (pszAUTHOR == NULL && pszPRODUCER == NULL && pszCREATOR == NULL && pszCREATION_DATE == NULL && pszSUBJECT == NULL && pszTITLE == NULL && pszKEYWORDS == NULL) return 0; if (nInfoId == 0) nInfoId = AllocNewObject(); StartObj(nInfoId, nInfoGen); GDALPDFDictionaryRW oDict; if (pszAUTHOR != NULL) oDict.Add("Author", pszAUTHOR); if (pszPRODUCER != NULL) oDict.Add("Producer", pszPRODUCER); if (pszCREATOR != NULL) oDict.Add("Creator", pszCREATOR); if (pszCREATION_DATE != NULL) oDict.Add("CreationDate", pszCREATION_DATE); if (pszSUBJECT != NULL) oDict.Add("Subject", pszSUBJECT); if (pszTITLE != NULL) oDict.Add("Title", pszTITLE); if (pszKEYWORDS != NULL) oDict.Add("Keywords", pszKEYWORDS); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); EndObj(); return nInfoId; } /************************************************************************/ /* SetXMP() */ /************************************************************************/ int GDALPDFWriter::SetXMP(GDALDataset* poSrcDS, const char* pszXMP) { if (pszXMP != NULL && EQUALN(pszXMP, "NO", 2)) return 0; if (pszXMP != NULL && pszXMP[0] == '\0') return 0; char** papszXMP = poSrcDS->GetMetadata("xml:XMP"); if (pszXMP == NULL && papszXMP != NULL && papszXMP[0] != NULL) pszXMP = papszXMP[0]; if (pszXMP == NULL) return 0; CPLXMLNode* psNode = CPLParseXMLString(pszXMP); if (psNode == NULL) return 0; CPLDestroyXMLNode(psNode); if(nXMPId == 0) nXMPId = AllocNewObject(); StartObj(nXMPId, nXMPGen); GDALPDFDictionaryRW oDict; oDict.Add("Type", GDALPDFObjectRW::CreateName("Metadata")) .Add("Subtype", GDALPDFObjectRW::CreateName("XML")) .Add("Length", (int)strlen(pszXMP)); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); VSIFPrintfL(fp, "stream\n"); VSIFPrintfL(fp, "%s\n", pszXMP); VSIFPrintfL(fp, "endstream\n"); EndObj(); return nXMPId; } /************************************************************************/ /* WriteOCG() */ /************************************************************************/ int GDALPDFWriter::WriteOCG(const char* pszLayerName, int nParentId) { if (pszLayerName == NULL || pszLayerName[0] == '\0') return 0; int nOGCId = AllocNewObject(); GDALPDFOCGDesc oOCGDesc; oOCGDesc.nId = nOGCId; oOCGDesc.nParentId = nParentId; oOCGDesc.osLayerName = pszLayerName; asOCGs.push_back(oOCGDesc); StartObj(nOGCId); { GDALPDFDictionaryRW oDict; oDict.Add("Type", GDALPDFObjectRW::CreateName("OCG")); oDict.Add("Name", pszLayerName); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); return nOGCId; } /************************************************************************/ /* StartPage() */ /************************************************************************/ int GDALPDFWriter::StartPage(GDALDataset* poClippingDS, double dfDPI, const char* pszGEO_ENCODING, const char* pszNEATLINE, PDFMargins* psMargins, PDFCompressMethod eStreamCompressMethod, int bHasOGRData) { int nWidth = poClippingDS->GetRasterXSize(); int nHeight = poClippingDS->GetRasterYSize(); int nBands = poClippingDS->GetRasterCount(); double dfUserUnit = dfDPI * USER_UNIT_IN_INCH; double dfWidthInUserUnit = nWidth / dfUserUnit + psMargins->nLeft + psMargins->nRight; double dfHeightInUserUnit = nHeight / dfUserUnit + psMargins->nBottom + psMargins->nTop; int nPageId = AllocNewObject(); asPageId.push_back(nPageId); int nContentId = AllocNewObject(); int nResourcesId = AllocNewObject(); int nAnnotsId = AllocNewObject(); int bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH"); int bOGC_BP = EQUAL(pszGEO_ENCODING, "OGC_BP") || EQUAL(pszGEO_ENCODING, "BOTH"); int nViewportId = 0; if( bISO32000 ) nViewportId = WriteSRS_ISO32000(poClippingDS, dfUserUnit, pszNEATLINE, psMargins, TRUE); int nLGIDictId = 0; if( bOGC_BP ) nLGIDictId = WriteSRS_OGC_BP(poClippingDS, dfUserUnit, pszNEATLINE, psMargins); StartObj(nPageId); GDALPDFDictionaryRW oDictPage; oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page")) .Add("Parent", nPageResourceId, 0) .Add("MediaBox", &((new GDALPDFArrayRW()) ->Add(0).Add(0).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit))) .Add("UserUnit", dfUserUnit) .Add("Contents", nContentId, 0) .Add("Resources", nResourcesId, 0) .Add("Annots", nAnnotsId, 0); if (nBands == 4) { oDictPage.Add("Group", &((new GDALPDFDictionaryRW()) ->Add("Type", GDALPDFObjectRW::CreateName("Group")) .Add("S", GDALPDFObjectRW::CreateName("Transparency")) .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB")))); } if (nViewportId) { oDictPage.Add("VP", &((new GDALPDFArrayRW()) ->Add(nViewportId, 0))); } if (nLGIDictId) { oDictPage.Add("LGIDict", nLGIDictId, 0); } if (bHasOGRData) oDictPage.Add("StructParents", 0); VSIFPrintfL(fp, "%s\n", oDictPage.Serialize().c_str()); EndObj(); oPageContext.poClippingDS = poClippingDS; oPageContext.nPageId = nPageId; oPageContext.nContentId = nContentId; oPageContext.nResourcesId = nResourcesId; oPageContext.nAnnotsId = nAnnotsId; oPageContext.dfDPI = dfDPI; oPageContext.sMargins = *psMargins; oPageContext.eStreamCompressMethod = eStreamCompressMethod; return TRUE; } /************************************************************************/ /* WriteColorTable() */ /************************************************************************/ int GDALPDFWriter::WriteColorTable(GDALDataset* poSrcDS) { /* Does the source image has a color table ? */ GDALColorTable* poCT = NULL; if (poSrcDS->GetRasterCount() > 0) poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); int nColorTableId = 0; if (poCT != NULL && poCT->GetColorEntryCount() <= 256) { int nColors = poCT->GetColorEntryCount(); nColorTableId = AllocNewObject(); int nLookupTableId = AllocNewObject(); /* Index object */ StartObj(nColorTableId); { GDALPDFArrayRW oArray; oArray.Add(GDALPDFObjectRW::CreateName("Indexed")) .Add(&((new GDALPDFArrayRW())->Add(GDALPDFObjectRW::CreateName("DeviceRGB")))) .Add(nColors-1) .Add(nLookupTableId, 0); VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str()); } EndObj(); /* Lookup table object */ StartObj(nLookupTableId); { GDALPDFDictionaryRW oDict; oDict.Add("Length", nColors * 3); VSIFPrintfL(fp, "%s %% Lookup table\n", oDict.Serialize().c_str()); } VSIFPrintfL(fp, "stream\n"); GByte pabyLookup[768]; for(int i=0;i<nColors;i++) { const GDALColorEntry* poEntry = poCT->GetColorEntry(i); pabyLookup[3 * i + 0] = (GByte)poEntry->c1; pabyLookup[3 * i + 1] = (GByte)poEntry->c2; pabyLookup[3 * i + 2] = (GByte)poEntry->c3; } VSIFWriteL(pabyLookup, 3 * nColors, 1, fp); VSIFPrintfL(fp, "\n"); VSIFPrintfL(fp, "endstream\n"); EndObj(); } return nColorTableId; } /************************************************************************/ /* WriteImagery() */ /************************************************************************/ int GDALPDFWriter::WriteImagery(GDALDataset* poDS, const char* pszLayerName, PDFCompressMethod eCompressMethod, int nPredictor, int nJPEGQuality, const char* pszJPEG2000_DRIVER, int nBlockXSize, int nBlockYSize, GDALProgressFunc pfnProgress, void * pProgressData) { int nWidth = poDS->GetRasterXSize(); int nHeight = poDS->GetRasterYSize(); double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; GDALPDFRasterDesc oRasterDesc; if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName); /* Does the source image has a color table ? */ int nColorTableId = WriteColorTable(poDS); int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize; int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize; int nBlocks = nXBlocks * nYBlocks; int nBlockXOff, nBlockYOff; for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++) { for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++) { int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize); int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize); int iImage = nBlockYOff * nXBlocks + nBlockXOff; void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks, (iImage + 1) / (double)nBlocks, pfnProgress, pProgressData); int nX = nBlockXOff * nBlockXSize; int nY = nBlockYOff * nBlockYSize; int nImageId = WriteBlock(poDS, nX, nY, nReqWidth, nReqHeight, nColorTableId, eCompressMethod, nPredictor, nJPEGQuality, pszJPEG2000_DRIVER, GDALScaledProgress, pScaledData); GDALDestroyScaledProgress(pScaledData); if (nImageId == 0) return FALSE; GDALPDFImageDesc oImageDesc; oImageDesc.nImageId = nImageId; oImageDesc.dfXOff = nX / dfUserUnit + oPageContext.sMargins.nLeft; oImageDesc.dfYOff = (nHeight - nY - nReqHeight) / dfUserUnit + oPageContext.sMargins.nBottom; oImageDesc.dfXSize = nReqWidth / dfUserUnit; oImageDesc.dfYSize = nReqHeight / dfUserUnit; oRasterDesc.asImageDesc.push_back(oImageDesc); } } oPageContext.asRasterDesc.push_back(oRasterDesc); return TRUE; } /************************************************************************/ /* WriteClippedImagery() */ /************************************************************************/ int GDALPDFWriter::WriteClippedImagery( GDALDataset* poDS, const char* pszLayerName, PDFCompressMethod eCompressMethod, int nPredictor, int nJPEGQuality, const char* pszJPEG2000_DRIVER, int nBlockXSize, int nBlockYSize, GDALProgressFunc pfnProgress, void * pProgressData) { double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; GDALPDFRasterDesc oRasterDesc; /* Get clipping dataset bounding-box */ double adfClippingGeoTransform[6]; GDALDataset* poClippingDS = oPageContext.poClippingDS; poClippingDS->GetGeoTransform(adfClippingGeoTransform); int nClippingWidth = poClippingDS->GetRasterXSize(); int nClippingHeight = poClippingDS->GetRasterYSize(); double dfClippingMinX = adfClippingGeoTransform[0]; double dfClippingMaxX = dfClippingMinX + nClippingWidth * adfClippingGeoTransform[1]; double dfClippingMaxY = adfClippingGeoTransform[3]; double dfClippingMinY = dfClippingMaxY + nClippingHeight * adfClippingGeoTransform[5]; if( dfClippingMaxY < dfClippingMinY ) { double dfTmp = dfClippingMinY; dfClippingMinY = dfClippingMaxY; dfClippingMaxY = dfTmp; } /* Get current dataset dataset bounding-box */ double adfGeoTransform[6]; poDS->GetGeoTransform(adfGeoTransform); int nWidth = poDS->GetRasterXSize(); int nHeight = poDS->GetRasterYSize(); double dfRasterMinX = adfGeoTransform[0]; //double dfRasterMaxX = dfRasterMinX + nWidth * adfGeoTransform[1]; double dfRasterMaxY = adfGeoTransform[3]; double dfRasterMinY = dfRasterMaxY + nHeight * adfGeoTransform[5]; if( dfRasterMaxY < dfRasterMinY ) { double dfTmp = dfRasterMinY; dfRasterMinY = dfRasterMaxY; dfRasterMaxY = dfTmp; } if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName); /* Does the source image has a color table ? */ int nColorTableId = WriteColorTable(poDS); int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize; int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize; int nBlocks = nXBlocks * nYBlocks; int nBlockXOff, nBlockYOff; for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++) { for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++) { int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize); int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize); int iImage = nBlockYOff * nXBlocks + nBlockXOff; void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks, (iImage + 1) / (double)nBlocks, pfnProgress, pProgressData); int nX = nBlockXOff * nBlockXSize; int nY = nBlockYOff * nBlockYSize; /* Compute extent of block to write */ double dfBlockMinX = adfGeoTransform[0] + nX * adfGeoTransform[1]; double dfBlockMaxX = adfGeoTransform[0] + (nX + nReqWidth) * adfGeoTransform[1]; double dfBlockMinY = adfGeoTransform[3] + (nY + nReqHeight) * adfGeoTransform[5]; double dfBlockMaxY = adfGeoTransform[3] + nY * adfGeoTransform[5]; if( dfBlockMaxY < dfBlockMinY ) { double dfTmp = dfBlockMinY; dfBlockMinY = dfBlockMaxY; dfBlockMaxY = dfTmp; } /* Clip the extent of the block with the extent of the main raster */ double dfIntersectMinX = MAX(dfBlockMinX, dfClippingMinX); double dfIntersectMinY = MAX(dfBlockMinY, dfClippingMinY); double dfIntersectMaxX = MIN(dfBlockMaxX, dfClippingMaxX); double dfIntersectMaxY = MIN(dfBlockMaxY, dfClippingMaxY); if( dfIntersectMinX < dfIntersectMaxX && dfIntersectMinY < dfIntersectMaxY ) { /* Re-compute (x,y,width,height) subwindow of current raster from */ /* the extent of the clipped block */ nX = (int)((dfIntersectMinX - dfRasterMinX) / adfGeoTransform[1] + 0.5); if( adfGeoTransform[5] < 0 ) nY = (int)((dfRasterMaxY - dfIntersectMaxY) / (-adfGeoTransform[5]) + 0.5); else nY = (int)((dfIntersectMinY - dfRasterMinY) / adfGeoTransform[5] + 0.5); nReqWidth = (int)((dfIntersectMaxX - dfRasterMinX) / adfGeoTransform[1] + 0.5) - nX; if( adfGeoTransform[5] < 0 ) nReqHeight = (int)((dfRasterMaxY - dfIntersectMinY) / (-adfGeoTransform[5]) + 0.5) - nY; else nReqHeight = (int)((dfIntersectMaxY - dfRasterMinY) / adfGeoTransform[5] + 0.5) - nY; if( nReqWidth > 0 && nReqHeight > 0) { int nImageId = WriteBlock(poDS, nX, nY, nReqWidth, nReqHeight, nColorTableId, eCompressMethod, nPredictor, nJPEGQuality, pszJPEG2000_DRIVER, GDALScaledProgress, pScaledData); if (nImageId == 0) { GDALDestroyScaledProgress(pScaledData); return FALSE; } /* Compute the subwindow in image coordinates of the main raster corresponding */ /* to the extent of the clipped block */ double dfXInClippingUnits, dfYInClippingUnits, dfReqWidthInClippingUnits, dfReqHeightInClippingUnits; dfXInClippingUnits = (dfIntersectMinX - dfClippingMinX) / adfClippingGeoTransform[1]; if( adfClippingGeoTransform[5] < 0 ) dfYInClippingUnits = (dfClippingMaxY - dfIntersectMaxY) / (-adfClippingGeoTransform[5]); else dfYInClippingUnits = (dfIntersectMinY - dfClippingMinY) / adfClippingGeoTransform[5]; dfReqWidthInClippingUnits = (dfIntersectMaxX - dfClippingMinX) / adfClippingGeoTransform[1] - dfXInClippingUnits; if( adfClippingGeoTransform[5] < 0 ) dfReqHeightInClippingUnits = (dfClippingMaxY - dfIntersectMinY) / (-adfClippingGeoTransform[5]) - dfYInClippingUnits; else dfReqHeightInClippingUnits = (dfIntersectMaxY - dfClippingMinY) / adfClippingGeoTransform[5] - dfYInClippingUnits; GDALPDFImageDesc oImageDesc; oImageDesc.nImageId = nImageId; oImageDesc.dfXOff = dfXInClippingUnits / dfUserUnit + oPageContext.sMargins.nLeft; oImageDesc.dfYOff = (nClippingHeight - dfYInClippingUnits - dfReqHeightInClippingUnits) / dfUserUnit + oPageContext.sMargins.nBottom; oImageDesc.dfXSize = dfReqWidthInClippingUnits / dfUserUnit; oImageDesc.dfYSize = dfReqHeightInClippingUnits / dfUserUnit; oRasterDesc.asImageDesc.push_back(oImageDesc); } } GDALDestroyScaledProgress(pScaledData); } } oPageContext.asRasterDesc.push_back(oRasterDesc); return TRUE; } #ifdef OGR_ENABLED /************************************************************************/ /* WriteOGRDataSource() */ /************************************************************************/ int GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource, const char* pszOGRDisplayField, const char* pszOGRDisplayLayerNames, const char* pszOGRLinkField, int bWriteOGRAttributes) { if (OGRGetDriverCount() == 0) OGRRegisterAll(); OGRDataSourceH hDS = OGROpen(pszOGRDataSource, 0, NULL); if (hDS == NULL) return FALSE; int iObj = 0; int nLayers = OGR_DS_GetLayerCount(hDS); char** papszLayerNames = CSLTokenizeString2(pszOGRDisplayLayerNames,",",0); for(int iLayer = 0; iLayer < nLayers; iLayer ++) { CPLString osLayerName; if (CSLCount(papszLayerNames) < nLayers) osLayerName = OGR_L_GetName(OGR_DS_GetLayer(hDS, iLayer)); else osLayerName = papszLayerNames[iLayer]; WriteOGRLayer(hDS, iLayer, pszOGRDisplayField, pszOGRLinkField, osLayerName, bWriteOGRAttributes, iObj); } OGRReleaseDataSource(hDS); CSLDestroy(papszLayerNames); return TRUE; } /************************************************************************/ /* StartOGRLayer() */ /************************************************************************/ GDALPDFLayerDesc GDALPDFWriter::StartOGRLayer(CPLString osLayerName, int bWriteOGRAttributes) { GDALPDFLayerDesc osVectorDesc; osVectorDesc.osLayerName = osLayerName; osVectorDesc.bWriteOGRAttributes = bWriteOGRAttributes; osVectorDesc.nOGCId = WriteOCG(osLayerName); osVectorDesc.nFeatureLayerId = (bWriteOGRAttributes) ? AllocNewObject() : 0; osVectorDesc.nOCGTextId = 0; return osVectorDesc; } /************************************************************************/ /* EndOGRLayer() */ /************************************************************************/ void GDALPDFWriter::EndOGRLayer(GDALPDFLayerDesc& osVectorDesc) { if (osVectorDesc.bWriteOGRAttributes) { StartObj(osVectorDesc.nFeatureLayerId); GDALPDFDictionaryRW oDict; oDict.Add("A", &(new GDALPDFDictionaryRW())->Add("O", GDALPDFObjectRW::CreateName("UserProperties"))); GDALPDFArrayRW* poArray = new GDALPDFArrayRW(); oDict.Add("K", poArray); for(int i = 0; i < (int)osVectorDesc.aUserPropertiesIds.size(); i++) { poArray->Add(osVectorDesc.aUserPropertiesIds[i], 0); } if (nStructTreeRootId == 0) nStructTreeRootId = AllocNewObject(); oDict.Add("P", nStructTreeRootId, 0); oDict.Add("S", GDALPDFObjectRW::CreateName("Feature")); oDict.Add("T", osVectorDesc.osLayerName); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); EndObj(); } oPageContext.asVectorDesc.push_back(osVectorDesc); } /************************************************************************/ /* WriteOGRLayer() */ /************************************************************************/ int GDALPDFWriter::WriteOGRLayer(OGRDataSourceH hDS, int iLayer, const char* pszOGRDisplayField, const char* pszOGRLinkField, CPLString osLayerName, int bWriteOGRAttributes, int& iObj) { GDALDataset* poClippingDS = oPageContext.poClippingDS; double adfGeoTransform[6]; if (poClippingDS->GetGeoTransform(adfGeoTransform) != CE_None) return FALSE; GDALPDFLayerDesc osVectorDesc = StartOGRLayer(osLayerName, bWriteOGRAttributes); OGRLayerH hLyr = OGR_DS_GetLayer(hDS, iLayer); const char* pszWKT = poClippingDS->GetProjectionRef(); OGRSpatialReferenceH hGDAL_SRS = NULL; if( pszWKT && pszWKT[0] != '\0' ) hGDAL_SRS = OSRNewSpatialReference(pszWKT); OGRSpatialReferenceH hOGR_SRS = OGR_L_GetSpatialRef(hLyr); OGRCoordinateTransformationH hCT = NULL; if( hGDAL_SRS == NULL && hOGR_SRS != NULL ) { CPLError(CE_Warning, CPLE_AppDefined, "Vector layer has a SRS set, but Raster layer has no SRS set. Assuming they are the same."); } else if( hGDAL_SRS != NULL && hOGR_SRS == NULL ) { CPLError(CE_Warning, CPLE_AppDefined, "Vector layer has no SRS set, but Raster layer has a SRS set. Assuming they are the same."); } else if( hGDAL_SRS != NULL && hOGR_SRS != NULL ) { if (!OSRIsSame(hGDAL_SRS, hOGR_SRS)) { hCT = OCTNewCoordinateTransformation( hOGR_SRS, hGDAL_SRS ); if( hCT == NULL ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot compute coordinate transformation from vector SRS to raster SRS"); } } } if( hCT == NULL ) { double dfXMin = adfGeoTransform[0]; double dfYMin = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5]; double dfXMax = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1]; double dfYMax = adfGeoTransform[3]; OGR_L_SetSpatialFilterRect(hLyr, dfXMin, dfYMin, dfXMax, dfYMax); } OGRFeatureH hFeat; int iObjLayer = 0; while( (hFeat = OGR_L_GetNextFeature(hLyr)) != NULL) { WriteOGRFeature(osVectorDesc, hFeat, hCT, pszOGRDisplayField, pszOGRLinkField, bWriteOGRAttributes, iObj, iObjLayer); OGR_F_Destroy(hFeat); } EndOGRLayer(osVectorDesc); if( hCT != NULL ) OCTDestroyCoordinateTransformation(hCT); if( hGDAL_SRS != NULL ) OSRDestroySpatialReference(hGDAL_SRS); return TRUE; } /************************************************************************/ /* DrawGeometry() */ /************************************************************************/ static void DrawGeometry(VSILFILE* fp, OGRGeometryH hGeom, double adfMatrix[4], int bPaint = TRUE) { switch(wkbFlatten(OGR_G_GetGeometryType(hGeom))) { case wkbLineString: { int nPoints = OGR_G_GetPointCount(hGeom); for(int i=0;i<nPoints;i++) { double dfX = OGR_G_GetX(hGeom, i) * adfMatrix[1] + adfMatrix[0]; double dfY = OGR_G_GetY(hGeom, i) * adfMatrix[3] + adfMatrix[2]; VSIFPrintfL(fp, "%f %f %c\n", dfX, dfY, (i == 0) ? 'm' : 'l'); } if (bPaint) VSIFPrintfL(fp, "S\n"); break; } case wkbPolygon: { int nParts = OGR_G_GetGeometryCount(hGeom); for(int i=0;i<nParts;i++) { DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE); VSIFPrintfL(fp, "h\n"); } if (bPaint) VSIFPrintfL(fp, "b*\n"); break; } case wkbMultiLineString: { int nParts = OGR_G_GetGeometryCount(hGeom); for(int i=0;i<nParts;i++) { DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE); } if (bPaint) VSIFPrintfL(fp, "S\n"); break; } case wkbMultiPolygon: { int nParts = OGR_G_GetGeometryCount(hGeom); for(int i=0;i<nParts;i++) { DrawGeometry(fp, OGR_G_GetGeometryRef(hGeom, i), adfMatrix, FALSE); } if (bPaint) VSIFPrintfL(fp, "b*\n"); break; } default: break; } } /************************************************************************/ /* WriteOGRFeature() */ /************************************************************************/ int GDALPDFWriter::WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc, OGRFeatureH hFeat, OGRCoordinateTransformationH hCT, const char* pszOGRDisplayField, const char* pszOGRLinkField, int bWriteOGRAttributes, int& iObj, int& iObjLayer) { GDALDataset* poClippingDS = oPageContext.poClippingDS; int nHeight = poClippingDS->GetRasterYSize(); double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; double adfGeoTransform[6]; poClippingDS->GetGeoTransform(adfGeoTransform); double adfMatrix[4]; adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft; adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit); adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom; adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit); OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat); if (hGeom == NULL) { return TRUE; } OGREnvelope sEnvelope; if( hCT != NULL ) { /* Reproject */ if( OGR_G_Transform(hGeom, hCT) != OGRERR_NONE ) { return TRUE; } OGREnvelope sRasterEnvelope; sRasterEnvelope.MinX = adfGeoTransform[0]; sRasterEnvelope.MinY = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5]; sRasterEnvelope.MaxX = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1]; sRasterEnvelope.MaxY = adfGeoTransform[3]; /* Check that the reprojected geometry interescts the raster envelope */ OGR_G_GetEnvelope(hGeom, &sEnvelope); if( !(sRasterEnvelope.Intersects(sEnvelope)) ) { return TRUE; } } else { OGR_G_GetEnvelope(hGeom, &sEnvelope); } /* -------------------------------------------------------------- */ /* Get style */ /* -------------------------------------------------------------- */ int nPenR = 0, nPenG = 0, nPenB = 0, nPenA = 255; int nBrushR = 127, nBrushG = 127, nBrushB = 127, nBrushA = 127; int nTextR = 0, nTextG = 0, nTextB = 0, nTextA = 255; int bSymbolColorDefined = FALSE; int nSymbolR = 0, nSymbolG = 0, nSymbolB = 0, nSymbolA = 255; double dfTextSize = 12, dfTextAngle = 0, dfTextDx = 0, dfTextDy = 0; double dfPenWidth = 1; double dfSymbolSize = 5; CPLString osDashArray; CPLString osLabelText; CPLString osSymbolId; int nImageSymbolId = 0, nImageWidth = 0, nImageHeight = 0; OGRStyleMgrH hSM = OGR_SM_Create(NULL); OGR_SM_InitFromFeature(hSM, hFeat); int nCount = OGR_SM_GetPartCount(hSM, NULL); for(int iPart = 0; iPart < nCount; iPart++) { OGRStyleToolH hTool = OGR_SM_GetPart(hSM, iPart, NULL); if (hTool) { if (OGR_ST_GetType(hTool) == OGRSTCPen) { int bIsNull = TRUE; const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTPenColor, &bIsNull); if (pszColor && !bIsNull) { int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); if (nVals >= 3) { nPenR = nRed; nPenG = nGreen; nPenB = nBlue; if (nVals == 4) nPenA = nAlpha; } } const char* pszDash = OGR_ST_GetParamStr(hTool, OGRSTPenPattern, &bIsNull); if (pszDash && !bIsNull) { char** papszTokens = CSLTokenizeString2(pszDash, " ", 0); int nTokens = CSLCount(papszTokens); if ((nTokens % 2) == 0) { for(int i=0;i<nTokens;i++) { osDashArray += CPLSPrintf("%d ", atoi(papszTokens[i])); } } CSLDestroy(papszTokens); } //OGRSTUnitId eUnit = OGR_ST_GetUnit(hTool); double dfWidth = OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bIsNull); if (!bIsNull) dfPenWidth = dfWidth; } else if (OGR_ST_GetType(hTool) == OGRSTCBrush) { int bIsNull; const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTBrushFColor, &bIsNull); if (pszColor) { int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); if (nVals >= 3) { nBrushR = nRed; nBrushG = nGreen; nBrushB = nBlue; if (nVals == 4) nBrushA = nAlpha; } } } else if (OGR_ST_GetType(hTool) == OGRSTCLabel) { int bIsNull; const char* pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelTextString, &bIsNull); if (pszStr) { osLabelText = pszStr; /* If the text is of the form {stuff}, then it means we want to fetch */ /* the value of the field "stuff" in the feature */ if( osLabelText.size() && osLabelText[0] == '{' && osLabelText[osLabelText.size() - 1] == '}' ) { osLabelText = pszStr + 1; osLabelText.resize(osLabelText.size() - 1); int nIdxField = OGR_F_GetFieldIndex(hFeat, osLabelText); if( nIdxField >= 0 ) osLabelText = OGR_F_GetFieldAsString(hFeat, nIdxField); else osLabelText = ""; } } const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTLabelFColor, &bIsNull); if (pszColor && !bIsNull) { int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); if (nVals >= 3) { nTextR = nRed; nTextG = nGreen; nTextB = nBlue; if (nVals == 4) nTextA = nAlpha; } } double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelSize, &bIsNull); if (!bIsNull) { dfTextSize = dfVal; } dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelAngle, &bIsNull); if (!bIsNull) { dfTextAngle = dfVal; } dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDx, &bIsNull); if (!bIsNull) { dfTextDx = dfVal; } dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDy, &bIsNull); if (!bIsNull) { dfTextDy = dfVal; } } else if (OGR_ST_GetType(hTool) == OGRSTCSymbol) { int bIsNull; const char* pszSymbolId = OGR_ST_GetParamStr(hTool, OGRSTSymbolId, &bIsNull); if (pszSymbolId && !bIsNull) { osSymbolId = pszSymbolId; if (strstr(pszSymbolId, "ogr-sym-") == NULL) { if (oMapSymbolFilenameToDesc.find(osSymbolId) == oMapSymbolFilenameToDesc.end()) { CPLPushErrorHandler(CPLQuietErrorHandler); GDALDatasetH hImageDS = GDALOpen(osSymbolId, GA_ReadOnly); CPLPopErrorHandler(); if (hImageDS != NULL) { nImageWidth = GDALGetRasterXSize(hImageDS); nImageHeight = GDALGetRasterYSize(hImageDS); nImageSymbolId = WriteBlock((GDALDataset*) hImageDS, 0, 0, nImageWidth, nImageHeight, 0, COMPRESS_DEFAULT, 0, -1, NULL, NULL, NULL); GDALClose(hImageDS); } GDALPDFImageDesc oDesc; oDesc.nImageId = nImageSymbolId; oDesc.dfXOff = 0; oDesc.dfYOff = 0; oDesc.dfXSize = nImageWidth; oDesc.dfYSize = nImageHeight; oMapSymbolFilenameToDesc[osSymbolId] = oDesc; } else { GDALPDFImageDesc& oDesc = oMapSymbolFilenameToDesc[osSymbolId]; nImageSymbolId = oDesc.nImageId; nImageWidth = (int)oDesc.dfXSize; nImageHeight = (int)oDesc.dfYSize; } } } double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTSymbolSize, &bIsNull); if (!bIsNull) { dfSymbolSize = dfVal; } const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTSymbolColor, &bIsNull); if (pszColor && !bIsNull) { int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); if (nVals >= 3) { bSymbolColorDefined = TRUE; nSymbolR = nRed; nSymbolG = nGreen; nSymbolB = nBlue; if (nVals == 4) nSymbolA = nAlpha; } } } OGR_ST_Destroy(hTool); } } OGR_SM_Destroy(hSM); if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && bSymbolColorDefined) { nPenR = nSymbolR; nPenG = nSymbolG; nPenB = nSymbolB; nPenA = nSymbolA; nBrushR = nSymbolR; nBrushG = nSymbolG; nBrushB = nSymbolB; nBrushA = nSymbolA; } double dfRadius = dfSymbolSize * dfUserUnit; /* -------------------------------------------------------------- */ /* Write object dictionary */ /* -------------------------------------------------------------- */ int nObjectId = AllocNewObject(); int nObjectLengthId = AllocNewObject(); osVectorDesc.aIds.push_back(nObjectId); int bboxXMin, bboxYMin, bboxXMax, bboxYMax; if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && nImageSymbolId != 0) { bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - nImageWidth / 2); bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - nImageHeight / 2); bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + nImageWidth / 2); bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + nImageHeight / 2); } else { double dfMargin = dfPenWidth; if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint ) { if (osSymbolId == "ogr-sym-6" || osSymbolId == "ogr-sym-7") { const double dfSqrt3 = 1.73205080757; dfMargin += dfRadius * 2 * dfSqrt3 / 3; } else dfMargin += dfRadius; } bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin); bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin); bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin); bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin); } int iField = -1; const char* pszLinkVal = NULL; if (pszOGRLinkField != NULL && (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRLinkField)) >= 0 && OGR_F_IsFieldSet(hFeat, iField) && strcmp((pszLinkVal = OGR_F_GetFieldAsString(hFeat, iField)), "") != 0) { int nAnnotId = AllocNewObject(); oPageContext.anAnnotationsId.push_back(nAnnotId); StartObj(nAnnotId); { GDALPDFDictionaryRW oDict; oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot")); oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link")); oDict.Add("Rect", &(new GDALPDFArrayRW())->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax).Add(bboxYMax)); oDict.Add("A", &(new GDALPDFDictionaryRW())-> Add("S", GDALPDFObjectRW::CreateName("URI")). Add("URI", pszLinkVal)); oDict.Add("BS", &(new GDALPDFDictionaryRW())-> Add("Type", GDALPDFObjectRW::CreateName("Border")). Add("S", GDALPDFObjectRW::CreateName("S")). Add("W", 0)); oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0)); oDict.Add("H", GDALPDFObjectRW::CreateName("I")); if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon && OGR_G_GetGeometryCount(hGeom) == 1 ) { OGRGeometryH hSubGeom = OGR_G_GetGeometryRef(hGeom, 0); int nPoints = OGR_G_GetPointCount(hSubGeom); if( nPoints == 4 || nPoints == 5 ) { std::vector<double> adfX, adfY; for(int i=0;i<nPoints;i++) { double dfX = OGR_G_GetX(hSubGeom, i) * adfMatrix[1] + adfMatrix[0]; double dfY = OGR_G_GetY(hSubGeom, i) * adfMatrix[3] + adfMatrix[2]; adfX.push_back(dfX); adfY.push_back(dfY); } if( nPoints == 4 ) { oDict.Add("QuadPoints", &(new GDALPDFArrayRW())-> Add(adfX[0]).Add(adfY[0]). Add(adfX[1]).Add(adfY[1]). Add(adfX[2]).Add(adfY[2]). Add(adfX[0]).Add(adfY[0])); } else if( nPoints == 5 ) { oDict.Add("QuadPoints", &(new GDALPDFArrayRW())-> Add(adfX[0]).Add(adfY[0]). Add(adfX[1]).Add(adfY[1]). Add(adfX[2]).Add(adfY[2]). Add(adfX[3]).Add(adfY[3])); } } } VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); } StartObj(nObjectId); { GDALPDFDictionaryRW oDict; GDALPDFArrayRW* poBBOX = new GDALPDFArrayRW(); poBBOX->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax). Add(bboxYMax); oDict.Add("Length", nObjectLengthId, 0) .Add("Type", GDALPDFObjectRW::CreateName("XObject")) .Add("BBox", poBBOX) .Add("Subtype", GDALPDFObjectRW::CreateName("Form")); if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); } GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW(); poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState")); if (nPenA != 255) poGS1->Add("CA", (nPenA == 127 || nPenA == 128) ? 0.5 : nPenA / 255.0); if (nBrushA != 255) poGS1->Add("ca", (nBrushA == 127 || nBrushA == 128) ? 0.5 : nBrushA / 255.0 ); GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW(); poExtGState->Add("GS1", poGS1); GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW(); poResources->Add("ExtGState", poExtGState); if( nImageSymbolId != 0 ) { GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW(); poResources->Add("XObject", poDictXObject); poDictXObject->Add(CPLSPrintf("SymImage%d", nImageSymbolId), nImageSymbolId, 0); } oDict.Add("Resources", poResources); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } /* -------------------------------------------------------------- */ /* Write object stream */ /* -------------------------------------------------------------- */ VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } VSIFPrintfL(fp, "q\n"); VSIFPrintfL(fp, "/GS1 gs\n"); if (nImageSymbolId == 0) { VSIFPrintfL(fp, "%f w\n" "0 J\n" "0 j\n" "10 M\n" "[%s]0 d\n", dfPenWidth, osDashArray.c_str()); VSIFPrintfL(fp, "%f %f %f RG\n", nPenR / 255.0, nPenG / 255.0, nPenB / 255.0); VSIFPrintfL(fp, "%f %f %f rg\n", nBrushR / 255.0, nBrushG / 255.0, nBrushB / 255.0); } if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint) { double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0]; double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2]; if (nImageSymbolId != 0) { VSIFPrintfL(fp, "%d 0 0 %d %f %f cm\n", nImageWidth, nImageHeight, dfX - nImageWidth / 2, dfY - nImageHeight / 2); VSIFPrintfL(fp, "/SymImage%d Do\n", nImageSymbolId); } else if (osSymbolId == "") osSymbolId = "ogr-sym-3"; /* symbol by default */ else if ( !(osSymbolId == "ogr-sym-0" || osSymbolId == "ogr-sym-1" || osSymbolId == "ogr-sym-2" || osSymbolId == "ogr-sym-3" || osSymbolId == "ogr-sym-4" || osSymbolId == "ogr-sym-5" || osSymbolId == "ogr-sym-6" || osSymbolId == "ogr-sym-7" || osSymbolId == "ogr-sym-8" || osSymbolId == "ogr-sym-9") ) { CPLDebug("PDF", "Unhandled symbol id : %s. Using ogr-sym-3 instead", osSymbolId.c_str()); osSymbolId = "ogr-sym-3"; } if (osSymbolId == "ogr-sym-0") /* cross (+) */ { VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY); VSIFPrintfL(fp, "%f %f m\n", dfX, dfY - dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + dfRadius); VSIFPrintfL(fp, "S\n"); } else if (osSymbolId == "ogr-sym-1") /* diagcross (X) */ { VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius); VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius); VSIFPrintfL(fp, "S\n"); } else if (osSymbolId == "ogr-sym-2" || osSymbolId == "ogr-sym-3") /* circle */ { /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */ const double dfKappa = 0.5522847498; VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY); VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", dfX - dfRadius, dfY - dfRadius * dfKappa, dfX - dfRadius * dfKappa, dfY - dfRadius, dfX, dfY - dfRadius); VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", dfX + dfRadius * dfKappa, dfY - dfRadius, dfX + dfRadius, dfY - dfRadius * dfKappa, dfX + dfRadius, dfY); VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", dfX + dfRadius, dfY + dfRadius * dfKappa, dfX + dfRadius * dfKappa, dfY + dfRadius, dfX, dfY + dfRadius); VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", dfX - dfRadius * dfKappa, dfY + dfRadius, dfX - dfRadius, dfY + dfRadius * dfKappa, dfX - dfRadius, dfY); if (osSymbolId == "ogr-sym-2") VSIFPrintfL(fp, "s\n"); /* not filled */ else VSIFPrintfL(fp, "b*\n"); /* filled */ } else if (osSymbolId == "ogr-sym-4" || osSymbolId == "ogr-sym-5") /* square */ { VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius); VSIFPrintfL(fp, "%f %f l\n", dfX - dfRadius, dfY - dfRadius); if (osSymbolId == "ogr-sym-4") VSIFPrintfL(fp, "s\n"); /* not filled */ else VSIFPrintfL(fp, "b*\n"); /* filled */ } else if (osSymbolId == "ogr-sym-6" || osSymbolId == "ogr-sym-7") /* triangle */ { const double dfSqrt3 = 1.73205080757; VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius * dfSqrt3 / 3); VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + 2 * dfRadius * dfSqrt3 / 3); VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius * dfSqrt3 / 3); if (osSymbolId == "ogr-sym-6") VSIFPrintfL(fp, "s\n"); /* not filled */ else VSIFPrintfL(fp, "b*\n"); /* filled */ } else if (osSymbolId == "ogr-sym-8" || osSymbolId == "ogr-sym-9") /* star */ { const double dfSin18divSin126 = 0.38196601125; VSIFPrintfL(fp, "%f %f m\n", dfX, dfY + dfRadius); for(int i=1; i<10;i++) { double dfFactor = ((i % 2) == 1) ? dfSin18divSin126 : 1.0; VSIFPrintfL(fp, "%f %f l\n", dfX + cos(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor, dfY + sin(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor); } if (osSymbolId == "ogr-sym-8") VSIFPrintfL(fp, "s\n"); /* not filled */ else VSIFPrintfL(fp, "b*\n"); /* filled */ } } else { DrawGeometry(fp, hGeom, adfMatrix); } VSIFPrintfL(fp, "Q"); if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; vsi_l_offset nStreamEnd = VSIFTellL(fp); VSIFPrintfL(fp, "\n"); VSIFPrintfL(fp, "endstream\n"); EndObj(); StartObj(nObjectLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); /* -------------------------------------------------------------- */ /* Write label */ /* -------------------------------------------------------------- */ if (osLabelText.size() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint) { if (osVectorDesc.nOCGTextId == 0) osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOGCId); /* -------------------------------------------------------------- */ /* Write object dictionary */ /* -------------------------------------------------------------- */ nObjectId = AllocNewObject(); nObjectLengthId = AllocNewObject(); osVectorDesc.aIdsText.push_back(nObjectId); StartObj(nObjectId); { GDALPDFDictionaryRW oDict; GDALDataset* poClippingDS = oPageContext.poClippingDS; int nWidth = poClippingDS->GetRasterXSize(); int nHeight = poClippingDS->GetRasterYSize(); double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight; double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop; oDict.Add("Length", nObjectLengthId, 0) .Add("Type", GDALPDFObjectRW::CreateName("XObject")) .Add("BBox", &((new GDALPDFArrayRW()) ->Add(0).Add(0)).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit)) .Add("Subtype", GDALPDFObjectRW::CreateName("Form")); if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); } GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW(); if (nTextA != 255) { GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW(); poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState")); poGS1->Add("ca", (nTextA == 127 || nTextA == 128) ? 0.5 : nTextA / 255.0); GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW(); poExtGState->Add("GS1", poGS1); poResources->Add("ExtGState", poExtGState); } GDALPDFDictionaryRW* poDictFTimesRoman = NULL; poDictFTimesRoman = new GDALPDFDictionaryRW(); poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font")); poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman")); poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW(); if (poDictFTimesRoman) poDictFont->Add("FTimesRoman", poDictFTimesRoman); poResources->Add("Font", poDictFont); oDict.Add("Resources", poResources); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } /* -------------------------------------------------------------- */ /* Write object stream */ /* -------------------------------------------------------------- */ VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0] + dfTextDx; double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2] + dfTextDy; VSIFPrintfL(fp, "q\n"); VSIFPrintfL(fp, "BT\n"); if (nTextA != 255) { VSIFPrintfL(fp, "/GS1 gs\n"); } if (dfTextAngle == 0) { VSIFPrintfL(fp, "%f %f Td\n", dfX, dfY); } else { dfTextAngle = - dfTextAngle * M_PI / 180.0; VSIFPrintfL(fp, "%f %f %f %f %f %f Tm\n", cos(dfTextAngle), -sin(dfTextAngle), sin(dfTextAngle), cos(dfTextAngle), dfX, dfY); } VSIFPrintfL(fp, "%f %f %f rg\n", nTextR / 255.0, nTextG / 255.0, nTextB / 255.0); VSIFPrintfL(fp, "/FTimesRoman %f Tf\n", dfTextSize); VSIFPrintfL(fp, "("); for(size_t i=0;i<osLabelText.size();i++) { /*if (osLabelText[i] == '\n') VSIFPrintfL(fp, ") Tj T* ("); else */ /* Tautology. Always true. */ /* if (osLabelText[i] >= 32 && osLabelText[i] <= 127) { */ VSIFPrintfL(fp, "%c", osLabelText[i]); /* } else { VSIFPrintfL(fp, "_"); } */ } VSIFPrintfL(fp, ") Tj\n"); VSIFPrintfL(fp, "ET\n"); VSIFPrintfL(fp, "Q"); if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; vsi_l_offset nStreamEnd = VSIFTellL(fp); VSIFPrintfL(fp, "\n"); VSIFPrintfL(fp, "endstream\n"); EndObj(); StartObj(nObjectLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); } else { osVectorDesc.aIdsText.push_back(0); } /* -------------------------------------------------------------- */ /* Write feature attributes */ /* -------------------------------------------------------------- */ int nFeatureUserProperties = 0; CPLString osFeatureName; if (bWriteOGRAttributes) { int iField = -1; if (pszOGRDisplayField && (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField)) >= 0) osFeatureName = OGR_F_GetFieldAsString(hFeat, iField); else osFeatureName = CPLSPrintf("feature%d", iObjLayer + 1); nFeatureUserProperties = AllocNewObject(); StartObj(nFeatureUserProperties); GDALPDFDictionaryRW oDict; GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW(); oDict.Add("A", poDictA); poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties")); int nFields = OGR_F_GetFieldCount(hFeat); GDALPDFArrayRW* poArray = new GDALPDFArrayRW(); for(int i = 0; i < nFields; i++) { if (OGR_F_IsFieldSet(hFeat, i)) { OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i ); GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW(); poKV->Add("N", OGR_Fld_GetNameRef(hFDefn)); if (OGR_Fld_GetType(hFDefn) == OFTInteger) poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i)); else if (OGR_Fld_GetType(hFDefn) == OFTReal) poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i)); else poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i)); poArray->Add(poKV); } } poDictA->Add("P", poArray); oDict.Add("K", iObj); oDict.Add("P", osVectorDesc.nFeatureLayerId, 0); oDict.Add("Pg", oPageContext.nPageId, 0); oDict.Add("S", GDALPDFObjectRW::CreateName("feature")); oDict.Add("T", osFeatureName); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); EndObj(); } iObj ++; iObjLayer ++; osVectorDesc.aUserPropertiesIds.push_back(nFeatureUserProperties); osVectorDesc.aFeatureNames.push_back(osFeatureName); return TRUE; } #endif /************************************************************************/ /* EndPage() */ /************************************************************************/ int GDALPDFWriter::EndPage(const char* pszExtraImages, const char* pszExtraStream, const char* pszExtraLayerName, const char* pszOffLayers, const char* pszExclusiveLayers) { int nLayerExtraId = WriteOCG(pszExtraLayerName); if( pszOffLayers ) osOffLayers = pszOffLayers; if( pszExclusiveLayers ) osExclusiveLayers = pszExclusiveLayers; int bHasTimesRoman = pszExtraStream && strstr(pszExtraStream, "/FTimesRoman"); int bHasTimesBold = pszExtraStream && strstr(pszExtraStream, "/FTimesBold"); /* -------------------------------------------------------------- */ /* Write extra images */ /* -------------------------------------------------------------- */ std::vector<GDALPDFImageDesc> asExtraImageDesc; if (pszExtraImages) { if( GDALGetDriverCount() == 0 ) GDALAllRegister(); char** papszExtraImagesTokens = CSLTokenizeString2(pszExtraImages, ",", 0); double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; int nCount = CSLCount(papszExtraImagesTokens); for(int i=0;i+4<=nCount; /* */) { const char* pszImageFilename = papszExtraImagesTokens[i+0]; double dfX = atof(papszExtraImagesTokens[i+1]); double dfY = atof(papszExtraImagesTokens[i+2]); double dfScale = atof(papszExtraImagesTokens[i+3]); const char* pszLinkVal = NULL; i += 4; if( i < nCount && EQUALN(papszExtraImagesTokens[i],"link=",5) ) { pszLinkVal = papszExtraImagesTokens[i] + 5; i++; } GDALDataset* poImageDS = (GDALDataset* )GDALOpen(pszImageFilename, GA_ReadOnly); if (poImageDS) { int nImageId = WriteBlock( poImageDS, 0, 0, poImageDS->GetRasterXSize(), poImageDS->GetRasterYSize(), 0, COMPRESS_DEFAULT, 0, -1, NULL, NULL, NULL ); if (nImageId) { GDALPDFImageDesc oImageDesc; oImageDesc.nImageId = nImageId; oImageDesc.dfXSize = poImageDS->GetRasterXSize() / dfUserUnit * dfScale; oImageDesc.dfYSize = poImageDS->GetRasterYSize() / dfUserUnit * dfScale; oImageDesc.dfXOff = dfX; oImageDesc.dfYOff = dfY; asExtraImageDesc.push_back(oImageDesc); if( pszLinkVal != NULL ) { int nAnnotId = AllocNewObject(); oPageContext.anAnnotationsId.push_back(nAnnotId); StartObj(nAnnotId); { GDALPDFDictionaryRW oDict; oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot")); oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link")); oDict.Add("Rect", &(new GDALPDFArrayRW())-> Add(oImageDesc.dfXOff). Add(oImageDesc.dfYOff). Add(oImageDesc.dfXOff + oImageDesc.dfXSize). Add(oImageDesc.dfYOff + oImageDesc.dfYSize)); oDict.Add("A", &(new GDALPDFDictionaryRW())-> Add("S", GDALPDFObjectRW::CreateName("URI")). Add("URI", pszLinkVal)); oDict.Add("BS", &(new GDALPDFDictionaryRW())-> Add("Type", GDALPDFObjectRW::CreateName("Border")). Add("S", GDALPDFObjectRW::CreateName("S")). Add("W", 0)); oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0)); oDict.Add("H", GDALPDFObjectRW::CreateName("I")); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); } } GDALClose(poImageDS); } } CSLDestroy(papszExtraImagesTokens); } /* -------------------------------------------------------------- */ /* Write content dictionary */ /* -------------------------------------------------------------- */ int nContentLengthId = AllocNewObject(); StartObj(oPageContext.nContentId); { GDALPDFDictionaryRW oDict; oDict.Add("Length", nContentLengthId, 0); if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); } VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } /* -------------------------------------------------------------- */ /* Write content stream */ /* -------------------------------------------------------------- */ VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } /* -------------------------------------------------------------- */ /* Write drawing instructions for raster blocks */ /* -------------------------------------------------------------- */ for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++) { const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster]; if (oDesc.nOCGRasterId) VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oDesc.nOCGRasterId); for(size_t iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++) { VSIFPrintfL(fp, "q\n"); GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXSize); GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYSize); GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXOff); GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYOff); VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n", poXSize->Serialize().c_str(), poYSize->Serialize().c_str(), poXOff->Serialize().c_str(), poYOff->Serialize().c_str()); delete poXSize; delete poYSize; delete poXOff; delete poYOff; VSIFPrintfL(fp, "/Image%d Do\n", oDesc.asImageDesc[iImage].nImageId); VSIFPrintfL(fp, "Q\n"); } if (oDesc.nOCGRasterId) VSIFPrintfL(fp, "EMC\n"); } /* -------------------------------------------------------------- */ /* Write drawing instructions for vector features */ /* -------------------------------------------------------------- */ int iObj = 0; for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) { GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId); for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) { CPLString osName = oLayerDesc.aFeatureNames[iVector]; if (osName.size()) { VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n", iObj); } iObj ++; VSIFPrintfL(fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector]); if (osName.size()) { VSIFPrintfL(fp, "EMC\n"); } } VSIFPrintfL(fp, "EMC\n"); } /* -------------------------------------------------------------- */ /* Write drawing instructions for labels of vector features */ /* -------------------------------------------------------------- */ iObj = 0; for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) { GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; if (oLayerDesc.nOCGTextId) { VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId); VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId); for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) { if (oLayerDesc.aIdsText[iVector]) { CPLString osName = oLayerDesc.aFeatureNames[iVector]; if (osName.size()) { VSIFPrintfL(fp, "/feature <</MCID %d>> BDC\n", iObj); } VSIFPrintfL(fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector]); if (osName.size()) { VSIFPrintfL(fp, "EMC\n"); } } iObj ++; } VSIFPrintfL(fp, "EMC\n"); VSIFPrintfL(fp, "EMC\n"); } else iObj += (int) oLayerDesc.aIds.size(); } /* -------------------------------------------------------------- */ /* Write drawing instructions for extra content. */ /* -------------------------------------------------------------- */ if (pszExtraStream || asExtraImageDesc.size()) { if (nLayerExtraId) VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", nLayerExtraId); /* -------------------------------------------------------------- */ /* Write drawing instructions for extra images. */ /* -------------------------------------------------------------- */ for(size_t iImage = 0; iImage < asExtraImageDesc.size(); iImage ++) { VSIFPrintfL(fp, "q\n"); GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXSize); GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYSize); GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXOff); GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYOff); VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n", poXSize->Serialize().c_str(), poYSize->Serialize().c_str(), poXOff->Serialize().c_str(), poYOff->Serialize().c_str()); delete poXSize; delete poYSize; delete poXOff; delete poYOff; VSIFPrintfL(fp, "/Image%d Do\n", asExtraImageDesc[iImage].nImageId); VSIFPrintfL(fp, "Q\n"); } if (pszExtraStream) VSIFPrintfL(fp, "%s\n", pszExtraStream); if (nLayerExtraId) VSIFPrintfL(fp, "EMC\n"); } if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; vsi_l_offset nStreamEnd = VSIFTellL(fp); if (fpGZip) VSIFPrintfL(fp, "\n"); VSIFPrintfL(fp, "endstream\n"); EndObj(); StartObj(nContentLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); /* -------------------------------------------------------------- */ /* Write objects for feature tree. */ /* -------------------------------------------------------------- */ if (nStructTreeRootId) { int nParentTreeId = AllocNewObject(); StartObj(nParentTreeId); VSIFPrintfL(fp, "<< /Nums [ 0 "); VSIFPrintfL(fp, "[ "); for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) { GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) { int nId = oLayerDesc.aUserPropertiesIds[iVector]; if (nId) VSIFPrintfL(fp, "%d 0 R ", nId); } } VSIFPrintfL(fp, " ]\n"); VSIFPrintfL(fp, " ] >> \n"); EndObj(); StartObj(nStructTreeRootId); VSIFPrintfL(fp, "<< " "/Type /StructTreeRoot " "/ParentTree %d 0 R " "/K [ ", nParentTreeId); for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) { VSIFPrintfL(fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId); } VSIFPrintfL(fp,"] >>\n"); EndObj(); } /* -------------------------------------------------------------- */ /* Write page resource dictionary. */ /* -------------------------------------------------------------- */ StartObj(oPageContext.nResourcesId); { GDALPDFDictionaryRW oDict; GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW(); oDict.Add("XObject", poDictXObject); size_t iImage; for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++) { const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster]; for(iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++) { poDictXObject->Add(CPLSPrintf("Image%d", oDesc.asImageDesc[iImage].nImageId), oDesc.asImageDesc[iImage].nImageId, 0); } } for(iImage = 0; iImage < asExtraImageDesc.size(); iImage ++) { poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId), asExtraImageDesc[iImage].nImageId, 0); } for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) { GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) { poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector]), oLayerDesc.aIds[iVector], 0); if (oLayerDesc.aIdsText[iVector]) poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector]), oLayerDesc.aIdsText[iVector], 0); } } GDALPDFDictionaryRW* poDictFTimesRoman = NULL; if (bHasTimesRoman) { poDictFTimesRoman = new GDALPDFDictionaryRW(); poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font")); poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman")); poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); } GDALPDFDictionaryRW* poDictFTimesBold = NULL; if (bHasTimesBold) { poDictFTimesBold = new GDALPDFDictionaryRW(); poDictFTimesBold->Add("Type", GDALPDFObjectRW::CreateName("Font")); poDictFTimesBold->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Bold")); poDictFTimesBold->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); poDictFTimesBold->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); } if (poDictFTimesRoman != NULL || poDictFTimesBold != NULL) { GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW(); if (poDictFTimesRoman) poDictFont->Add("FTimesRoman", poDictFTimesRoman); if (poDictFTimesBold) poDictFont->Add("FTimesBold", poDictFTimesBold); oDict.Add("Font", poDictFont); } if (asOCGs.size()) { GDALPDFDictionaryRW* poDictProperties = new GDALPDFDictionaryRW(); for(size_t i=0; i<asOCGs.size(); i++) poDictProperties->Add(CPLSPrintf("Lyr%d", asOCGs[i].nId), asOCGs[i].nId, 0); oDict.Add("Properties", poDictProperties); } VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); /* -------------------------------------------------------------- */ /* Write annotation arrays. */ /* -------------------------------------------------------------- */ StartObj(oPageContext.nAnnotsId); { GDALPDFArrayRW oArray; for(size_t i = 0; i < oPageContext.anAnnotationsId.size(); i++) { oArray.Add(oPageContext.anAnnotationsId[i], 0); } VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str()); } EndObj(); return TRUE; } /************************************************************************/ /* WriteMask() */ /************************************************************************/ int GDALPDFWriter::WriteMask(GDALDataset* poSrcDS, int nXOff, int nYOff, int nReqXSize, int nReqYSize, PDFCompressMethod eCompressMethod) { int nMaskSize = nReqXSize * nReqYSize; GByte* pabyMask = (GByte*)VSIMalloc(nMaskSize); if (pabyMask == NULL) return 0; CPLErr eErr; eErr = poSrcDS->GetRasterBand(4)->RasterIO( GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pabyMask, nReqXSize, nReqYSize, GDT_Byte, 0, 0); if (eErr != CE_None) { VSIFree(pabyMask); return 0; } int bOnly0or255 = TRUE; int bOnly255 = TRUE; /* int bOnly0 = TRUE; */ int i; for(i=0;i<nReqXSize * nReqYSize;i++) { if (pabyMask[i] == 0) bOnly255 = FALSE; else if (pabyMask[i] == 255) { /* bOnly0 = FALSE; */ } else { /* bOnly0 = FALSE; */ bOnly255 = FALSE; bOnly0or255 = FALSE; break; } } if (bOnly255) { CPLFree(pabyMask); return 0; } if (bOnly0or255) { /* Translate to 1 bit */ int nReqXSize1 = (nReqXSize + 7) / 8; GByte* pabyMask1 = (GByte*)VSICalloc(nReqXSize1, nReqYSize); if (pabyMask1 == NULL) { CPLFree(pabyMask); return 0; } for(int y=0;y<nReqYSize;y++) { for(int x=0;x<nReqXSize;x++) { if (pabyMask[y * nReqXSize + x]) pabyMask1[y * nReqXSize1 + x / 8] |= 1 << (7 - (x % 8)); } } VSIFree(pabyMask); pabyMask = pabyMask1; nMaskSize = nReqXSize1 * nReqYSize; } int nMaskId = AllocNewObject(); int nMaskLengthId = AllocNewObject(); StartObj(nMaskId); GDALPDFDictionaryRW oDict; oDict.Add("Length", nMaskLengthId, 0) .Add("Type", GDALPDFObjectRW::CreateName("XObject")); if( eCompressMethod != COMPRESS_NONE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); } oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image")) .Add("Width", nReqXSize) .Add("Height", nReqYSize) .Add("ColorSpace", GDALPDFObjectRW::CreateName("DeviceGray")) .Add("BitsPerComponent", (bOnly0or255) ? 1 : 8); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( eCompressMethod != COMPRESS_NONE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } VSIFWriteL(pabyMask, nMaskSize, 1, fp); CPLFree(pabyMask); if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; vsi_l_offset nStreamEnd = VSIFTellL(fp); VSIFPrintfL(fp, "\n" "endstream\n"); EndObj(); StartObj(nMaskLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); return nMaskId; } /************************************************************************/ /* WriteBlock() */ /************************************************************************/ int GDALPDFWriter::WriteBlock(GDALDataset* poSrcDS, int nXOff, int nYOff, int nReqXSize, int nReqYSize, int nColorTableId, PDFCompressMethod eCompressMethod, int nPredictor, int nJPEGQuality, const char* pszJPEG2000_DRIVER, GDALProgressFunc pfnProgress, void * pProgressData) { int nBands = poSrcDS->GetRasterCount(); if (nBands == 0) return 0; if (nColorTableId == 0) nColorTableId = WriteColorTable(poSrcDS); CPLErr eErr = CE_None; GDALDataset* poBlockSrcDS = NULL; GDALDatasetH hMemDS = NULL; GByte* pabyMEMDSBuffer = NULL; if (eCompressMethod == COMPRESS_DEFAULT) { GDALDataset* poSrcDSToTest = poSrcDS; /* Test if we can directly copy original JPEG content */ /* if available */ if (poSrcDS->GetDriver() != NULL && poSrcDS->GetDriver() == GDALGetDriverByName("VRT")) { VRTDataset* poVRTDS = (VRTDataset* )poSrcDS; poSrcDSToTest = poVRTDS->GetSingleSimpleSource(); } if (poSrcDSToTest != NULL && poSrcDSToTest->GetDriver() != NULL && EQUAL(poSrcDSToTest->GetDriver()->GetDescription(), "JPEG") && nXOff == 0 && nYOff == 0 && nReqXSize == poSrcDSToTest->GetRasterXSize() && nReqYSize == poSrcDSToTest->GetRasterYSize() && nJPEGQuality < 0) { VSILFILE* fpSrc = VSIFOpenL(poSrcDSToTest->GetDescription(), "rb"); if (fpSrc != NULL) { CPLDebug("PDF", "Copying directly original JPEG file"); VSIFSeekL(fpSrc, 0, SEEK_END); int nLength = (int)VSIFTellL(fpSrc); VSIFSeekL(fpSrc, 0, SEEK_SET); int nImageId = AllocNewObject(); StartObj(nImageId); GDALPDFDictionaryRW oDict; oDict.Add("Length", nLength) .Add("Type", GDALPDFObjectRW::CreateName("XObject")) .Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode")) .Add("Subtype", GDALPDFObjectRW::CreateName("Image")) .Add("Width", nReqXSize) .Add("Height", nReqYSize) .Add("ColorSpace", (nBands == 1) ? GDALPDFObjectRW::CreateName("DeviceGray") : GDALPDFObjectRW::CreateName("DeviceRGB")) .Add("BitsPerComponent", 8); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); VSIFPrintfL(fp, "stream\n"); GByte abyBuffer[1024]; for(int i=0;i<nLength;i += 1024) { int nRead = (int) VSIFReadL(abyBuffer, 1, 1024, fpSrc); if ((int)VSIFWriteL(abyBuffer, 1, nRead, fp) != nRead) { eErr = CE_Failure; break; } if( eErr == CE_None && pfnProgress != NULL && !pfnProgress( (i + nRead) / (double)nLength, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" ); eErr = CE_Failure; break; } } VSIFPrintfL(fp, "\nendstream\n"); EndObj(); VSIFCloseL(fpSrc); return eErr == CE_None ? nImageId : 0; } } eCompressMethod = COMPRESS_DEFLATE; } int nMaskId = 0; if (nBands == 4) { nMaskId = WriteMask(poSrcDS, nXOff, nYOff, nReqXSize, nReqYSize, eCompressMethod); } if( nReqXSize == poSrcDS->GetRasterXSize() && nReqYSize == poSrcDS->GetRasterYSize() && nBands != 4) { poBlockSrcDS = poSrcDS; } else { if (nBands == 4) nBands = 3; GDALDriverH hMemDriver = GDALGetDriverByName("MEM"); if( hMemDriver == NULL ) return 0; hMemDS = GDALCreate(hMemDriver, "MEM:::", nReqXSize, nReqYSize, 0, GDT_Byte, NULL); if (hMemDS == NULL) return 0; pabyMEMDSBuffer = (GByte*)VSIMalloc3(nReqXSize, nReqYSize, nBands); if (pabyMEMDSBuffer == NULL) { GDALClose(hMemDS); return 0; } eErr = poSrcDS->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pabyMEMDSBuffer, nReqXSize, nReqYSize, GDT_Byte, nBands, NULL, 0, 0, 0); if( eErr != CE_None ) { CPLFree(pabyMEMDSBuffer); GDALClose(hMemDS); return 0; } int iBand; for(iBand = 0; iBand < nBands; iBand ++) { char** papszMEMDSOptions = NULL; char szTmp[64]; memset(szTmp, 0, sizeof(szTmp)); CPLPrintPointer(szTmp, pabyMEMDSBuffer + iBand * nReqXSize * nReqYSize, sizeof(szTmp)); papszMEMDSOptions = CSLSetNameValue(papszMEMDSOptions, "DATAPOINTER", szTmp); GDALAddBand(hMemDS, GDT_Byte, papszMEMDSOptions); CSLDestroy(papszMEMDSOptions); } poBlockSrcDS = (GDALDataset*) hMemDS; } int nImageId = AllocNewObject(); int nImageLengthId = AllocNewObject(); int nMeasureId = 0; if( CSLTestBoolean(CPLGetConfigOption("GDAL_PDF_WRITE_GEOREF_ON_IMAGE", "FALSE")) && nReqXSize == poSrcDS->GetRasterXSize() && nReqYSize == poSrcDS->GetRasterYSize() ) { PDFMargins sMargins = {0, 0, 0, 0}; nMeasureId = WriteSRS_ISO32000(poSrcDS, 1, NULL, &sMargins, FALSE); } StartObj(nImageId); GDALPDFDictionaryRW oDict; oDict.Add("Length", nImageLengthId, 0) .Add("Type", GDALPDFObjectRW::CreateName("XObject")); if( eCompressMethod == COMPRESS_DEFLATE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); if( nPredictor == 2 ) oDict.Add("DecodeParms", &((new GDALPDFDictionaryRW()) ->Add("Predictor", 2) .Add("Colors", nBands) .Add("Columns", nReqXSize))); } else if( eCompressMethod == COMPRESS_JPEG ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode")); } else if( eCompressMethod == COMPRESS_JPEG2000 ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("JPXDecode")); } oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image")) .Add("Width", nReqXSize) .Add("Height", nReqYSize) .Add("ColorSpace", (nColorTableId != 0) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) : (nBands == 1) ? GDALPDFObjectRW::CreateName("DeviceGray") : GDALPDFObjectRW::CreateName("DeviceRGB")) .Add("BitsPerComponent", 8); if( nMaskId ) { oDict.Add("SMask", nMaskId, 0); } if( nMeasureId ) { oDict.Add("Measure", nMeasureId, 0); } VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); if( eCompressMethod == COMPRESS_JPEG || eCompressMethod == COMPRESS_JPEG2000 ) { GDALDriver* poJPEGDriver = NULL; char szTmp[64]; char** papszOptions = NULL; if( eCompressMethod == COMPRESS_JPEG ) { poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG"); if (poJPEGDriver != NULL && nJPEGQuality > 0) papszOptions = CSLAddString(papszOptions, CPLSPrintf("QUALITY=%d", nJPEGQuality)); sprintf(szTmp, "/vsimem/pdftemp/%p.jpg", this); } else { if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2KAK")) poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2KAK"); if (poJPEGDriver == NULL) { if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2ECW")) { poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2ECW"); if( poJPEGDriver && poJPEGDriver->GetMetadataItem(GDAL_DMD_CREATIONDATATYPES) == NULL ) { poJPEGDriver = NULL; } } if (poJPEGDriver) { papszOptions = CSLAddString(papszOptions, "PROFILE=NPJE"); papszOptions = CSLAddString(papszOptions, "LAYERS=1"); papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF"); papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF"); } } if (poJPEGDriver == NULL) { if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2OpenJPEG")) poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2OpenJPEG"); if (poJPEGDriver) { papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF"); papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF"); } } if (poJPEGDriver == NULL) { if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JPEG2000")) poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG2000"); } sprintf(szTmp, "/vsimem/pdftemp/%p.jp2", this); } if( poJPEGDriver == NULL ) { CPLError(CE_Failure, CPLE_NotSupported, "No %s driver found", ( eCompressMethod == COMPRESS_JPEG ) ? "JPEG" : "JPEG2000"); eErr = CE_Failure; goto end; } GDALDataset* poJPEGDS = NULL; poJPEGDS = poJPEGDriver->CreateCopy(szTmp, poBlockSrcDS, FALSE, papszOptions, pfnProgress, pProgressData); CSLDestroy(papszOptions); if( poJPEGDS == NULL ) { eErr = CE_Failure; goto end; } GDALClose(poJPEGDS); vsi_l_offset nJPEGDataSize = 0; GByte* pabyJPEGData = VSIGetMemFileBuffer(szTmp, &nJPEGDataSize, TRUE); VSIFWriteL(pabyJPEGData, nJPEGDataSize, 1, fp); CPLFree(pabyJPEGData); } else { VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( eCompressMethod == COMPRESS_DEFLATE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } GByte* pabyLine = (GByte*)CPLMalloc(nReqXSize * nBands); for(int iLine = 0; iLine < nReqYSize; iLine ++) { /* Get pixel interleaved data */ eErr = poBlockSrcDS->RasterIO(GF_Read, 0, iLine, nReqXSize, 1, pabyLine, nReqXSize, 1, GDT_Byte, nBands, NULL, nBands, 0, 1); if( eErr != CE_None ) break; /* Apply predictor if needed */ if( nPredictor == 2 ) { if( nBands == 1 ) { int nPrevValue = pabyLine[0]; for(int iPixel = 1; iPixel < nReqXSize; iPixel ++) { int nCurValue = pabyLine[iPixel]; pabyLine[iPixel] = (GByte) (nCurValue - nPrevValue); nPrevValue = nCurValue; } } else if( nBands == 3 ) { int nPrevValueR = pabyLine[0]; int nPrevValueG = pabyLine[1]; int nPrevValueB = pabyLine[2]; for(int iPixel = 1; iPixel < nReqXSize; iPixel ++) { int nCurValueR = pabyLine[3 * iPixel + 0]; int nCurValueG = pabyLine[3 * iPixel + 1]; int nCurValueB = pabyLine[3 * iPixel + 2]; pabyLine[3 * iPixel + 0] = (GByte) (nCurValueR - nPrevValueR); pabyLine[3 * iPixel + 1] = (GByte) (nCurValueG - nPrevValueG); pabyLine[3 * iPixel + 2] = (GByte) (nCurValueB - nPrevValueB); nPrevValueR = nCurValueR; nPrevValueG = nCurValueG; nPrevValueB = nCurValueB; } } } if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, fp) != 1 ) { eErr = CE_Failure; break; } if( eErr == CE_None && pfnProgress != NULL && !pfnProgress( (iLine+1) / (double)nReqYSize, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" ); eErr = CE_Failure; break; } } CPLFree(pabyLine); if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; } end: CPLFree(pabyMEMDSBuffer); pabyMEMDSBuffer = NULL; if( hMemDS != NULL ) { GDALClose(hMemDS); hMemDS = NULL; } vsi_l_offset nStreamEnd = VSIFTellL(fp); VSIFPrintfL(fp, "\n" "endstream\n"); EndObj(); StartObj(nImageLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); return eErr == CE_None ? nImageId : 0; } /************************************************************************/ /* WriteJavascript() */ /************************************************************************/ int GDALPDFWriter::WriteJavascript(const char* pszJavascript) { int nJSId = AllocNewObject(); int nJSLengthId = AllocNewObject(); StartObj(nJSId); { GDALPDFDictionaryRW oDict; oDict.Add("Length", nJSLengthId, 0); if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); } VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } VSIFPrintfL(fp, "stream\n"); vsi_l_offset nStreamStart = VSIFTellL(fp); VSILFILE* fpGZip = NULL; VSILFILE* fpBack = fp; if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) { fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); fp = fpGZip; } VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, fp); if (fpGZip) VSIFCloseL(fpGZip); fp = fpBack; vsi_l_offset nStreamEnd = VSIFTellL(fp); VSIFPrintfL(fp, "\n" "endstream\n"); EndObj(); StartObj(nJSLengthId); VSIFPrintfL(fp, " %ld\n", (long)(nStreamEnd - nStreamStart)); EndObj(); nNamesId = AllocNewObject(); StartObj(nNamesId); { GDALPDFDictionaryRW oDict; GDALPDFDictionaryRW* poJavaScriptDict = new GDALPDFDictionaryRW(); oDict.Add("JavaScript", poJavaScriptDict); GDALPDFArrayRW* poNamesArray = new GDALPDFArrayRW(); poJavaScriptDict->Add("Names", poNamesArray); poNamesArray->Add("GDAL"); GDALPDFDictionaryRW* poJSDict = new GDALPDFDictionaryRW(); poNamesArray->Add(poJSDict); poJSDict->Add("JS", nJSId, 0); poJSDict->Add("S", GDALPDFObjectRW::CreateName("JavaScript")); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); return nNamesId; } /************************************************************************/ /* WriteJavascriptFile() */ /************************************************************************/ int GDALPDFWriter::WriteJavascriptFile(const char* pszJavascriptFile) { int nRet = 0; char* pszJavascriptToFree = (char*)CPLMalloc(65536); VSILFILE* fpJS = VSIFOpenL(pszJavascriptFile, "rb"); if( fpJS != NULL ) { int nRead = (int)VSIFReadL(pszJavascriptToFree, 1, 65536, fpJS); if( nRead < 65536 ) { pszJavascriptToFree[nRead] = '\0'; nRet = WriteJavascript(pszJavascriptToFree); } VSIFCloseL(fpJS); } CPLFree(pszJavascriptToFree); return nRet; } /************************************************************************/ /* WritePages() */ /************************************************************************/ void GDALPDFWriter::WritePages() { StartObj(nPageResourceId); { GDALPDFDictionaryRW oDict; GDALPDFArrayRW* poKids = new GDALPDFArrayRW(); oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages")) .Add("Count", (int)asPageId.size()) .Add("Kids", poKids); for(size_t i=0;i<asPageId.size();i++) poKids->Add(asPageId[i], 0); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); StartObj(nCatalogId); { GDALPDFDictionaryRW oDict; oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog")) .Add("Pages", nPageResourceId, 0); if (nXMPId) oDict.Add("Metadata", nXMPId, 0); if (asOCGs.size()) { GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW(); oDict.Add("OCProperties", poDictOCProperties); GDALPDFDictionaryRW* poDictD = new GDALPDFDictionaryRW(); poDictOCProperties->Add("D", poDictD); /* Build "Order" array of D dict */ GDALPDFArrayRW* poArrayOrder = new GDALPDFArrayRW(); size_t i; for(i=0;i<asOCGs.size();i++) { poArrayOrder->Add(asOCGs[i].nId, 0); if (i + 1 < asOCGs.size() && asOCGs[i+1].nParentId == asOCGs[i].nId) { GDALPDFArrayRW* poSubArrayOrder = new GDALPDFArrayRW(); poSubArrayOrder->Add(asOCGs[i+1].nId, 0); poArrayOrder->Add(poSubArrayOrder); i ++; } } poDictD->Add("Order", poArrayOrder); /* Build "OFF" array of D dict */ if( osOffLayers.size() ) { GDALPDFArrayRW* poArrayOFF = new GDALPDFArrayRW(); char** papszTokens = CSLTokenizeString2(osOffLayers, ",", 0); for(int i=0; papszTokens[i] != NULL; i++) { size_t j; int bFound = FALSE; for(j=0;j<asOCGs.size();j++) { if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0) { poArrayOFF->Add(asOCGs[j].nId, 0); bFound = TRUE; } if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId) { j ++; } } if( !bFound ) { CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer name (%s) specified in OFF_LAYERS", papszTokens[i]); } } CSLDestroy(papszTokens); poDictD->Add("OFF", poArrayOFF); } /* Build "RBGroups" array of D dict */ if( osExclusiveLayers.size() ) { GDALPDFArrayRW* poArrayRBGroups = new GDALPDFArrayRW(); char** papszTokens = CSLTokenizeString2(osExclusiveLayers, ",", 0); for(int i=0; papszTokens[i] != NULL; i++) { size_t j; int bFound = FALSE; for(j=0;j<asOCGs.size();j++) { if( strcmp(papszTokens[i], asOCGs[j].osLayerName) == 0) { poArrayRBGroups->Add(asOCGs[j].nId, 0); bFound = TRUE; } if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId) { j ++; } } if( !bFound ) { CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer name (%s) specified in EXCLUSIVE_LAYERS", papszTokens[i]); } } CSLDestroy(papszTokens); if( poArrayRBGroups->GetLength() ) { GDALPDFArrayRW* poMainArrayRBGroups = new GDALPDFArrayRW(); poMainArrayRBGroups->Add(poArrayRBGroups); poDictD->Add("RBGroups", poMainArrayRBGroups); } else delete poArrayRBGroups; } GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW(); for(i=0;i<asOCGs.size();i++) poArrayOGCs->Add(asOCGs[i].nId, 0); poDictOCProperties->Add("OCGs", poArrayOGCs); } if (nStructTreeRootId) { GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW(); oDict.Add("MarkInfo", poDictMarkInfo); poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE)); oDict.Add("StructTreeRoot", nStructTreeRootId, 0); } if (nNamesId) oDict.Add("Names", nNamesId, 0); VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); } EndObj(); } /************************************************************************/ /* GDALPDFGetJPEGQuality() */ /************************************************************************/ static int GDALPDFGetJPEGQuality(char** papszOptions) { int nJpegQuality = -1; const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" ); if( pszValue != NULL ) { nJpegQuality = atoi( pszValue ); if (!(nJpegQuality >= 1 && nJpegQuality <= 100)) { CPLError( CE_Warning, CPLE_IllegalArg, "JPEG_QUALITY=%s value not recognised, ignoring.", pszValue ); nJpegQuality = -1; } } return nJpegQuality; } /************************************************************************/ /* GDALPDFClippingDataset */ /************************************************************************/ class GDALPDFClippingDataset: public GDALDataset { GDALDataset* poSrcDS; double adfGeoTransform[6]; public: GDALPDFClippingDataset(GDALDataset* poSrcDS, double adfClippingExtent[4]) : poSrcDS(poSrcDS) { double adfSrcGeoTransform[6]; poSrcDS->GetGeoTransform(adfSrcGeoTransform); adfGeoTransform[0] = adfClippingExtent[0]; adfGeoTransform[1] = adfSrcGeoTransform[1]; adfGeoTransform[2] = 0.0; adfGeoTransform[3] = adfSrcGeoTransform[5] < 0 ? adfClippingExtent[3] : adfClippingExtent[1]; adfGeoTransform[4] = 0.0; adfGeoTransform[5] = adfSrcGeoTransform[5]; nRasterXSize = (int)((adfClippingExtent[2] - adfClippingExtent[0]) / adfSrcGeoTransform[1]); nRasterYSize = (int)((adfClippingExtent[3] - adfClippingExtent[1]) / fabs(adfSrcGeoTransform[5])); } virtual CPLErr GetGeoTransform( double * padfGeoTransform ) { memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); return CE_None; } virtual const char* GetProjectionRef() { return poSrcDS->GetProjectionRef(); } }; /************************************************************************/ /* GDALPDFCreateCopy() */ /************************************************************************/ GDALDataset *GDALPDFCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, int bStrict, char **papszOptions, GDALProgressFunc pfnProgress, void * pProgressData ) { int nBands = poSrcDS->GetRasterCount(); int nWidth = poSrcDS->GetRasterXSize(); int nHeight = poSrcDS->GetRasterYSize(); if( !pfnProgress( 0.0, NULL, pProgressData ) ) return NULL; /* -------------------------------------------------------------------- */ /* Some some rudimentary checks */ /* -------------------------------------------------------------------- */ if( nBands != 1 && nBands != 3 && nBands != 4 ) { CPLError( CE_Failure, CPLE_NotSupported, "PDF driver doesn't support %d bands. Must be 1 (grey or with color table), " "3 (RGB) or 4 bands.\n", nBands ); return NULL; } GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); if( eDT != GDT_Byte ) { CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, "PDF driver doesn't support data type %s. " "Only eight bit byte bands supported.\n", GDALGetDataTypeName( poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); if (bStrict) return NULL; } /* -------------------------------------------------------------------- */ /* Read options. */ /* -------------------------------------------------------------------- */ PDFCompressMethod eCompressMethod = COMPRESS_DEFAULT; const char* pszCompressMethod = CSLFetchNameValue(papszOptions, "COMPRESS"); if (pszCompressMethod) { if( EQUAL(pszCompressMethod, "NONE") ) eCompressMethod = COMPRESS_NONE; else if( EQUAL(pszCompressMethod, "DEFLATE") ) eCompressMethod = COMPRESS_DEFLATE; else if( EQUAL(pszCompressMethod, "JPEG") ) eCompressMethod = COMPRESS_JPEG; else if( EQUAL(pszCompressMethod, "JPEG2000") ) eCompressMethod = COMPRESS_JPEG2000; else { CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, "Unsupported value for COMPRESS."); if (bStrict) return NULL; } } PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE; const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS"); if (pszStreamCompressMethod) { if( EQUAL(pszStreamCompressMethod, "NONE") ) eStreamCompressMethod = COMPRESS_NONE; else if( EQUAL(pszStreamCompressMethod, "DEFLATE") ) eStreamCompressMethod = COMPRESS_DEFLATE; else { CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, "Unsupported value for STREAM_COMPRESS."); if (bStrict) return NULL; } } if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL && (eCompressMethod == COMPRESS_JPEG || eCompressMethod == COMPRESS_JPEG2000)) { CPLError( CE_Warning, CPLE_AppDefined, "The source raster band has a color table, which is not appropriate with JPEG or JPEG2000 compression.\n" "You should rather consider using color table expansion (-expand option in gdal_translate)"); } int nBlockXSize = nWidth; int nBlockYSize = nHeight; const char* pszValue; int bTiled = CSLFetchBoolean( papszOptions, "TILED", FALSE ); if( bTiled ) nBlockXSize = nBlockYSize = 256; pszValue = CSLFetchNameValue(papszOptions, "BLOCKXSIZE"); if( pszValue != NULL ) { nBlockXSize = atoi( pszValue ); if (nBlockXSize < 0 || nBlockXSize >= nWidth) nBlockXSize = nWidth; } pszValue = CSLFetchNameValue(papszOptions, "BLOCKYSIZE"); if( pszValue != NULL ) { nBlockYSize = atoi( pszValue ); if (nBlockYSize < 0 || nBlockYSize >= nHeight) nBlockYSize = nHeight; } int nJPEGQuality = GDALPDFGetJPEGQuality(papszOptions); const char* pszJPEG2000_DRIVER = CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER"); const char* pszGEO_ENCODING = CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000"); const char* pszXMP = CSLFetchNameValue(papszOptions, "XMP"); const char* pszPredictor = CSLFetchNameValue(papszOptions, "PREDICTOR"); int nPredictor = 1; if (pszPredictor) { if (eCompressMethod == COMPRESS_DEFAULT) eCompressMethod = COMPRESS_DEFLATE; if (eCompressMethod != COMPRESS_DEFLATE) { CPLError(CE_Warning, CPLE_NotSupported, "PREDICTOR option is only taken into account for DEFLATE compression"); } else { nPredictor = atoi(pszPredictor); if (nPredictor != 1 && nPredictor != 2) { CPLError(CE_Warning, CPLE_NotSupported, "Supported PREDICTOR values are 1 or 2"); nPredictor = 1; } } } const char* pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE"); int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0")); PDFMargins sMargins; sMargins.nLeft = nMargin; sMargins.nRight = nMargin; sMargins.nTop = nMargin; sMargins.nBottom = nMargin; const char* pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN"); if (pszLeftMargin) sMargins.nLeft = atoi(pszLeftMargin); const char* pszRightMargin = CSLFetchNameValue(papszOptions, "RIGHT_MARGIN"); if (pszRightMargin) sMargins.nRight = atoi(pszRightMargin); const char* pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN"); if (pszTopMargin) sMargins.nTop = atoi(pszTopMargin); const char* pszBottomMargin = CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN"); if (pszBottomMargin) sMargins.nBottom = atoi(pszBottomMargin); const char* pszDPI = CSLFetchNameValue(papszOptions, "DPI"); double dfDPI = DEFAULT_DPI; if( pszDPI != NULL ) dfDPI = CPLAtof(pszDPI); double dfUserUnit = dfDPI * USER_UNIT_IN_INCH; double dfWidthInUserUnit = nWidth / dfUserUnit + sMargins.nLeft + sMargins.nRight; double dfHeightInUserUnit = nHeight / dfUserUnit + sMargins.nBottom + sMargins.nTop; if( dfWidthInUserUnit > MAXIMUM_SIZE_IN_UNITS || dfHeightInUserUnit > MAXIMUM_SIZE_IN_UNITS ) { if( pszDPI == NULL ) { if( sMargins.nLeft + sMargins.nRight >= MAXIMUM_SIZE_IN_UNITS || sMargins.nBottom + sMargins.nTop >= MAXIMUM_SIZE_IN_UNITS ) { CPLError(CE_Warning, CPLE_AppDefined, "Margins too big compared to maximum page dimension (%d) " "in user units allowed by Acrobat", MAXIMUM_SIZE_IN_UNITS); } else { if( dfWidthInUserUnit >= dfHeightInUserUnit ) { dfDPI = (int)(0.5 + (double)nWidth / (MAXIMUM_SIZE_IN_UNITS - (sMargins.nLeft + sMargins.nRight)) / USER_UNIT_IN_INCH); } else { dfDPI = (int)(0.5 + (double)nHeight / (MAXIMUM_SIZE_IN_UNITS - (sMargins.nBottom + sMargins.nTop)) / USER_UNIT_IN_INCH); } CPLDebug("PDF", "Adjusting DPI to %d so that page dimension in " "user units remain in what is accepted by Acrobat", (int)dfDPI); } } else { CPLError(CE_Warning, CPLE_AppDefined, "The page dimension in user units is %d x %d whereas the " "maximum allowed by Acrobat is %d x %d", (int)(dfWidthInUserUnit + 0.5), (int)(dfHeightInUserUnit + 0.5), MAXIMUM_SIZE_IN_UNITS, MAXIMUM_SIZE_IN_UNITS); } } if (dfDPI < DEFAULT_DPI) dfDPI = DEFAULT_DPI; const char* pszClippingExtent = CSLFetchNameValue(papszOptions, "CLIPPING_EXTENT"); int bUseClippingExtent = FALSE; double adfClippingExtent[4] = { 0.0, 0.0, 0.0, 0.0 }; if( pszClippingExtent != NULL ) { char** papszTokens = CSLTokenizeString2(pszClippingExtent, ",", 0); if( CSLCount(papszTokens) == 4 ) { bUseClippingExtent = TRUE; adfClippingExtent[0] = CPLAtof(papszTokens[0]); adfClippingExtent[1] = CPLAtof(papszTokens[1]); adfClippingExtent[2] = CPLAtof(papszTokens[2]); adfClippingExtent[3] = CPLAtof(papszTokens[3]); if( adfClippingExtent[0] > adfClippingExtent[2] || adfClippingExtent[1] > adfClippingExtent[3] ) { CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for CLIPPING_EXTENT. Should be xmin,ymin,xmax,ymax"); bUseClippingExtent = TRUE; } if( bUseClippingExtent ) { double adfGeoTransform[6]; if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None ) { if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use CLIPPING_EXTENT because main raster has a rotated geotransform"); bUseClippingExtent = TRUE; } } else { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use CLIPPING_EXTENT because main raster has no geotransform"); bUseClippingExtent = TRUE; } } } CSLDestroy(papszTokens); } const char* pszLayerName = CSLFetchNameValue(papszOptions, "LAYER_NAME"); const char* pszExtraImages = CSLFetchNameValue(papszOptions, "EXTRA_IMAGES"); const char* pszExtraStream = CSLFetchNameValue(papszOptions, "EXTRA_STREAM"); const char* pszExtraLayerName = CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME"); const char* pszOGRDataSource = CSLFetchNameValue(papszOptions, "OGR_DATASOURCE"); const char* pszOGRDisplayField = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD"); const char* pszOGRDisplayLayerNames = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES"); const char* pszOGRLinkField = CSLFetchNameValue(papszOptions, "OGR_LINK_FIELD"); int bWriteOGRAttributes = CSLFetchBoolean(papszOptions, "OGR_WRITE_ATTRIBUTES", TRUE); const char* pszExtraRasters = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS"); const char* pszExtraRastersLayerName = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS_LAYER_NAME"); const char* pszOffLayers = CSLFetchNameValue(papszOptions, "OFF_LAYERS"); const char* pszExclusiveLayers = CSLFetchNameValue(papszOptions, "EXCLUSIVE_LAYERS"); const char* pszJavascript = CSLFetchNameValue(papszOptions, "JAVASCRIPT"); const char* pszJavascriptFile = CSLFetchNameValue(papszOptions, "JAVASCRIPT_FILE"); /* -------------------------------------------------------------------- */ /* Create file. */ /* -------------------------------------------------------------------- */ VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create PDF file %s.\n", pszFilename ); return NULL; } GDALPDFWriter oWriter(fp); GDALDataset* poClippingDS = poSrcDS; if( bUseClippingExtent ) poClippingDS = new GDALPDFClippingDataset(poSrcDS, adfClippingExtent); if( CSLFetchBoolean(papszOptions, "WRITE_INFO", TRUE) ) oWriter.SetInfo(poSrcDS, papszOptions); oWriter.SetXMP(poClippingDS, pszXMP); oWriter.StartPage(poClippingDS, dfDPI, pszGEO_ENCODING, pszNEATLINE, &sMargins, eStreamCompressMethod, pszOGRDataSource != NULL && bWriteOGRAttributes); int bRet; if( !bUseClippingExtent ) { bRet = oWriter.WriteImagery(poSrcDS, pszLayerName, eCompressMethod, nPredictor, nJPEGQuality, pszJPEG2000_DRIVER, nBlockXSize, nBlockYSize, pfnProgress, pProgressData); } else { bRet = oWriter.WriteClippedImagery(poSrcDS, pszLayerName, eCompressMethod, nPredictor, nJPEGQuality, pszJPEG2000_DRIVER, nBlockXSize, nBlockYSize, pfnProgress, pProgressData); } char** papszExtraRasters = CSLTokenizeString2( pszExtraRasters ? pszExtraRasters : "", ",", 0); char** papszExtraRastersLayerName = CSLTokenizeString2( pszExtraRastersLayerName ? pszExtraRastersLayerName : "", ",", 0); int bUseExtraRastersLayerName = (CSLCount(papszExtraRasters) == CSLCount(papszExtraRastersLayerName)); int bUseExtraRasters = TRUE; const char* pszClippingProjectionRef = poSrcDS->GetProjectionRef(); if( CSLCount(papszExtraRasters) != 0 ) { double adfGeoTransform[6]; if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None ) { if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use EXTRA_RASTERS because main raster has a rotated geotransform"); bUseExtraRasters = FALSE; } } else { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use EXTRA_RASTERS because main raster has no geotransform"); bUseExtraRasters = FALSE; } if( bUseExtraRasters && (pszClippingProjectionRef == NULL || pszClippingProjectionRef[0] == '\0') ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use EXTRA_RASTERS because main raster has no projection"); bUseExtraRasters = FALSE; } } for(int i=0; bRet && bUseExtraRasters && papszExtraRasters[i] != NULL; i++) { GDALDataset* poDS = (GDALDataset*)GDALOpen(papszExtraRasters[i], GA_ReadOnly); if( poDS != NULL ) { double adfGeoTransform[6]; int bUseRaster = TRUE; if( poDS->GetGeoTransform(adfGeoTransform) == CE_None ) { if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use %s because it has a rotated geotransform", papszExtraRasters[i]); bUseRaster = FALSE; } } else { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use %s because it has no geotransform", papszExtraRasters[i]); bUseRaster = FALSE; } const char* pszProjectionRef = poDS->GetProjectionRef(); if( bUseRaster && (pszProjectionRef == NULL || pszProjectionRef[0] == '\0') ) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use %s because it has no projection", papszExtraRasters[i]); bUseRaster = FALSE; } if( bUseRaster ) { if( pszClippingProjectionRef != NULL && pszProjectionRef != NULL && !EQUAL(pszClippingProjectionRef, pszProjectionRef) ) { OGRSpatialReferenceH hClippingSRS = OSRNewSpatialReference(pszClippingProjectionRef); OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszProjectionRef); if (!OSRIsSame(hClippingSRS, hSRS)) { CPLError(CE_Warning, CPLE_AppDefined, "Cannot use %s because it has a different projection than main dataset", papszExtraRasters[i]); bUseRaster = FALSE; } OSRDestroySpatialReference(hClippingSRS); OSRDestroySpatialReference(hSRS); } } if( bUseRaster ) { bRet = oWriter.WriteClippedImagery(poDS, bUseExtraRastersLayerName ? papszExtraRastersLayerName[i] : NULL, eCompressMethod, nPredictor, nJPEGQuality, pszJPEG2000_DRIVER, nBlockXSize, nBlockYSize, NULL, NULL); } GDALClose(poDS); } } CSLDestroy(papszExtraRasters); CSLDestroy(papszExtraRastersLayerName); #ifdef OGR_ENABLED if (bRet && pszOGRDataSource != NULL) oWriter.WriteOGRDataSource(pszOGRDataSource, pszOGRDisplayField, pszOGRDisplayLayerNames, pszOGRLinkField, bWriteOGRAttributes); #endif if (bRet) oWriter.EndPage(pszExtraImages, pszExtraStream, pszExtraLayerName, pszOffLayers, pszExclusiveLayers); if (pszJavascript) oWriter.WriteJavascript(pszJavascript); else if (pszJavascriptFile) oWriter.WriteJavascriptFile(pszJavascriptFile); oWriter.Close(); if (poClippingDS != poSrcDS) delete poClippingDS; if (!bRet) { VSIUnlink(pszFilename); return NULL; } else { #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) return GDALPDFOpen(pszFilename, GA_ReadOnly); #else return new GDALFakePDFDataset(); #endif } }