mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-19 22:03:51 -06:00
4740 lines
176 KiB
C++
4740 lines
176 KiB
C++
/******************************************************************************
|
|
* $Id: pdfcreatecopy.cpp 28780 2015-03-26 12:29:35Z 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.
|
|
****************************************************************************/
|
|
|
|
#include "gdal_pdf.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 28780 2015-03-26 12:29:35Z 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 if( nEPSGDatum == 6135 )
|
|
poPDFDatum = GDALPDFObjectRW::CreateString("OHA-M");
|
|
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 = CPLAtof(papszExtraImagesTokens[i+1]);
|
|
double dfY = CPLAtof(papszExtraImagesTokens[i+2]);
|
|
double dfScale = CPLAtof(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, NULL);
|
|
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, NULL);
|
|
|
|
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, NULL);
|
|
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
|
|
}
|
|
}
|