mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-20 06:05:32 -06:00
1666 lines
59 KiB
C++
1666 lines
59 KiB
C++
/******************************************************************************
|
|
* $Id: pdfreadvectors.cpp 27942 2014-11-11 00:57:41Z rouault $
|
|
*
|
|
* Project: PDF driver
|
|
* Purpose: GDALDataset driver for PDF dataset (read vector features)
|
|
* Author: Even Rouault, <even dot rouault at mines dash paris dot org>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2010-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"
|
|
|
|
#define SQUARE(x) ((x)*(x))
|
|
#define EPSILON 1e-5
|
|
|
|
CPL_CVSID("$Id: pdfreadvectors.cpp 27942 2014-11-11 00:57:41Z rouault $");
|
|
|
|
#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO)
|
|
|
|
/************************************************************************/
|
|
/* OpenVectorLayers() */
|
|
/************************************************************************/
|
|
|
|
int PDFDataset::OpenVectorLayers(GDALPDFDictionary* poPageDict)
|
|
{
|
|
GetCatalog();
|
|
if( poCatalogObject == NULL )
|
|
return FALSE;
|
|
|
|
GDALPDFObject* poContents = poPageDict->Get("Contents");
|
|
if (poContents == NULL)
|
|
return FALSE;
|
|
|
|
if (poContents->GetType() != PDFObjectType_Dictionary &&
|
|
poContents->GetType() != PDFObjectType_Array)
|
|
return FALSE;
|
|
|
|
GDALPDFObject* poResources = poPageDict->Get("Resources");
|
|
if (poResources == NULL || poResources->GetType() != PDFObjectType_Dictionary)
|
|
return FALSE;
|
|
|
|
GDALPDFObject* poStructTreeRoot = poCatalogObject->GetDictionary()->Get("StructTreeRoot");
|
|
if (CSLTestBoolean(CPLGetConfigOption("OGR_PDF_READ_NON_STRUCTURED", "NO")) ||
|
|
poStructTreeRoot == NULL ||
|
|
poStructTreeRoot->GetType() != PDFObjectType_Dictionary)
|
|
{
|
|
ExploreContentsNonStructured(poContents, poResources);
|
|
}
|
|
else
|
|
{
|
|
ExploreContents(poContents, poResources);
|
|
ExploreTree(poStructTreeRoot, 0);
|
|
}
|
|
|
|
CleanupIntermediateResources();
|
|
|
|
int bEmptyDS = TRUE;
|
|
for(int i=0;i<nLayers;i++)
|
|
{
|
|
if (papoLayers[i]->GetFeatureCount() != 0)
|
|
{
|
|
bEmptyDS = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return !bEmptyDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CleanupIntermediateResources() */
|
|
/************************************************************************/
|
|
|
|
void PDFDataset::CleanupIntermediateResources()
|
|
{
|
|
std::map<int,OGRGeometry*>::iterator oMapIter = oMapMCID.begin();
|
|
for( ; oMapIter != oMapMCID.end(); ++oMapIter)
|
|
delete oMapIter->second;
|
|
oMapMCID.erase(oMapMCID.begin(), oMapMCID.end());
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* InitMapOperators() */
|
|
/************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
char szOpName[4];
|
|
int nArgs;
|
|
} PDFOperator;
|
|
|
|
static const PDFOperator asPDFOperators [] =
|
|
{
|
|
{ "b", 0 },
|
|
{ "B", 0 },
|
|
{ "b*", 0 },
|
|
{ "B*", 0 },
|
|
{ "BDC", 2 },
|
|
// BI
|
|
{ "BMC", 1 },
|
|
// BT
|
|
{ "BX", 0 },
|
|
{ "c", 6 },
|
|
{ "cm", 6 },
|
|
{ "CS", 1 },
|
|
{ "cs", 1 },
|
|
{ "d", 1 }, /* we have ignored the first arg */
|
|
// d0
|
|
// d1
|
|
{ "Do", 1 },
|
|
{ "DP", 2 },
|
|
// EI
|
|
{ "EMC", 0 },
|
|
// ET
|
|
{ "EX", 0 },
|
|
{ "f", 0 },
|
|
{ "F", 0 },
|
|
{ "f*", 0 },
|
|
{ "G", 1 },
|
|
{ "g", 1 },
|
|
{ "gs", 1 },
|
|
{ "h", 0 },
|
|
{ "i", 1 },
|
|
// ID
|
|
{ "j", 1 },
|
|
{ "J", 1 },
|
|
{ "K", 4 },
|
|
{ "k", 4 },
|
|
{ "l", 2 },
|
|
{ "m", 2 },
|
|
{ "M", 1 },
|
|
{ "MP", 1 },
|
|
{ "n", 0 },
|
|
{ "q", 0 },
|
|
{ "Q", 0 },
|
|
{ "re", 4 },
|
|
{ "RG", 3 },
|
|
{ "rg", 3 },
|
|
{ "ri", 1 },
|
|
{ "s", 0 },
|
|
{ "S", 0 },
|
|
{ "SC", -1 },
|
|
{ "sc", -1 },
|
|
{ "SCN", -1 },
|
|
{ "scn", -1 },
|
|
{ "sh", 1 },
|
|
// T*
|
|
{ "Tc", 1},
|
|
{ "Td", 2},
|
|
{ "TD", 2},
|
|
{ "Tf", 1},
|
|
{ "Tj", 1},
|
|
{ "TJ", 1},
|
|
{ "TL", 1},
|
|
{ "Tm", 6},
|
|
{ "Tr", 1},
|
|
{ "Ts", 1},
|
|
{ "Tw", 1},
|
|
{ "Tz", 1},
|
|
{ "v", 4 },
|
|
{ "w", 1 },
|
|
{ "W", 0 },
|
|
{ "W*", 0 },
|
|
{ "y", 4 },
|
|
// '
|
|
// "
|
|
};
|
|
|
|
void PDFDataset::InitMapOperators()
|
|
{
|
|
for(size_t i=0;i<sizeof(asPDFOperators) / sizeof(asPDFOperators[0]); i++)
|
|
oMapOperators[asPDFOperators[i].szOpName] = asPDFOperators[i].nArgs;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TestCapability() */
|
|
/************************************************************************/
|
|
|
|
int PDFDataset::TestCapability( CPL_UNUSED const char * pszCap )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetLayer() */
|
|
/************************************************************************/
|
|
|
|
OGRLayer *PDFDataset::GetLayer( int iLayer )
|
|
|
|
{
|
|
if (iLayer < 0 || iLayer >= nLayers)
|
|
return NULL;
|
|
|
|
return papoLayers[iLayer];
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetLayerCount() */
|
|
/************************************************************************/
|
|
|
|
int PDFDataset::GetLayerCount()
|
|
{
|
|
return nLayers;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ExploreTree() */
|
|
/************************************************************************/
|
|
|
|
void PDFDataset::ExploreTree(GDALPDFObject* poObj, int nRecLevel)
|
|
{
|
|
if (nRecLevel == 16)
|
|
return;
|
|
|
|
if (poObj->GetType() != PDFObjectType_Dictionary)
|
|
return;
|
|
|
|
GDALPDFDictionary* poDict = poObj->GetDictionary();
|
|
|
|
GDALPDFObject* poS = poDict->Get("S");
|
|
CPLString osS;
|
|
if (poS != NULL && poS->GetType() == PDFObjectType_Name)
|
|
{
|
|
osS = poS->GetName();
|
|
}
|
|
|
|
GDALPDFObject* poT = poDict->Get("T");
|
|
CPLString osT;
|
|
if (poT != NULL && poT->GetType() == PDFObjectType_String)
|
|
{
|
|
osT = poT->GetString();
|
|
}
|
|
|
|
GDALPDFObject* poK = poDict->Get("K");
|
|
if (poK == NULL)
|
|
return;
|
|
|
|
if (poK->GetType() == PDFObjectType_Array)
|
|
{
|
|
GDALPDFArray* poArray = poK->GetArray();
|
|
if (poArray->GetLength() > 0 &&
|
|
poArray->Get(0)->GetType() == PDFObjectType_Dictionary &&
|
|
poArray->Get(0)->GetDictionary()->Get("K") != NULL &&
|
|
poArray->Get(0)->GetDictionary()->Get("K")->GetType() == PDFObjectType_Int)
|
|
{
|
|
CPLString osLayerName;
|
|
if (osT.size())
|
|
osLayerName = osT;
|
|
else
|
|
{
|
|
if (osS.size())
|
|
osLayerName = osS;
|
|
else
|
|
osLayerName = CPLSPrintf("Layer%d", nLayers + 1);
|
|
}
|
|
|
|
const char* pszWKT = GetProjectionRef();
|
|
OGRSpatialReference* poSRS = NULL;
|
|
if (pszWKT && pszWKT[0] != '\0')
|
|
{
|
|
poSRS = new OGRSpatialReference();
|
|
poSRS->importFromWkt((char**) &pszWKT);
|
|
}
|
|
|
|
OGRPDFLayer* poLayer =
|
|
new OGRPDFLayer(this, osLayerName.c_str(), poSRS, wkbUnknown);
|
|
delete poSRS;
|
|
|
|
poLayer->Fill(poArray);
|
|
|
|
papoLayers = (OGRLayer**)
|
|
CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
|
|
papoLayers[nLayers] = poLayer;
|
|
nLayers ++;
|
|
}
|
|
else
|
|
{
|
|
for(int i=0;i<poArray->GetLength();i++)
|
|
ExploreTree(poArray->Get(i), nRecLevel + 1);
|
|
}
|
|
}
|
|
else if (poK->GetType() == PDFObjectType_Dictionary)
|
|
{
|
|
ExploreTree(poK, nRecLevel + 1);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGeometryFromMCID() */
|
|
/************************************************************************/
|
|
|
|
OGRGeometry* PDFDataset::GetGeometryFromMCID(int nMCID)
|
|
{
|
|
std::map<int,OGRGeometry*>::iterator oMapIter = oMapMCID.find(nMCID);
|
|
if (oMapIter != oMapMCID.end())
|
|
return oMapIter->second;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GraphicState */
|
|
/************************************************************************/
|
|
|
|
class GraphicState
|
|
{
|
|
public:
|
|
double adfCM[6];
|
|
double adfStrokeColor[3];
|
|
double adfFillColor[3];
|
|
|
|
GraphicState()
|
|
{
|
|
adfCM[0] = 1;
|
|
adfCM[1] = 0;
|
|
adfCM[2] = 0;
|
|
adfCM[3] = 1;
|
|
adfCM[4] = 0;
|
|
adfCM[5] = 0;
|
|
adfStrokeColor[0] = 0.0;
|
|
adfStrokeColor[1] = 0.0;
|
|
adfStrokeColor[2] = 0.0;
|
|
adfFillColor[0] = 1.0;
|
|
adfFillColor[1] = 1.0;
|
|
adfFillColor[2] = 1.0;
|
|
}
|
|
|
|
void MultiplyBy(double adfMatrix[6])
|
|
{
|
|
/*
|
|
[ a b 0 ] [ a' b' 0] [ aa' + bc' ab' + bd' 0 ]
|
|
[ c d 0 ] * [ c' d' 0] = [ ca' + dc' cb' + dd' 0 ]
|
|
[ e f 1 ] [ e' f' 1] [ ea' + fc' + e' eb' + fd' + f' 1 ]
|
|
*/
|
|
|
|
double a = adfCM[0];
|
|
double b = adfCM[1];
|
|
double c = adfCM[2];
|
|
double d = adfCM[3];
|
|
double e = adfCM[4];
|
|
double f = adfCM[5];
|
|
double ap = adfMatrix[0];
|
|
double bp = adfMatrix[1];
|
|
double cp = adfMatrix[2];
|
|
double dp = adfMatrix[3];
|
|
double ep = adfMatrix[4];
|
|
double fp = adfMatrix[5];
|
|
adfCM[0] = a*ap + b*cp;
|
|
adfCM[1] = a*bp + b*dp;
|
|
adfCM[2] = c*ap + d*cp;
|
|
adfCM[3] = c*bp + d*dp;
|
|
adfCM[4] = e*ap + f*cp + ep;
|
|
adfCM[5] = e*bp + f*dp + fp;
|
|
}
|
|
|
|
void ApplyMatrix(double adfCoords[2])
|
|
{
|
|
double x = adfCoords[0];
|
|
double y = adfCoords[1];
|
|
|
|
adfCoords[0] = x * adfCM[0] + y * adfCM[2] + adfCM[4];
|
|
adfCoords[1] = x * adfCM[1] + y * adfCM[3] + adfCM[5];
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* PDFCoordsToSRSCoords() */
|
|
/************************************************************************/
|
|
|
|
void PDFDataset::PDFCoordsToSRSCoords(double x, double y,
|
|
double& X, double &Y)
|
|
{
|
|
x = x / dfPageWidth * nRasterXSize;
|
|
y = (1 - y / dfPageHeight) * nRasterYSize;
|
|
|
|
X = adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2];
|
|
Y = adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5];
|
|
|
|
if( fabs(X - (int)floor(X + 0.5)) < 1e-8 )
|
|
X = (int)floor(X + 0.5);
|
|
if( fabs(Y - (int)floor(Y + 0.5)) < 1e-8 )
|
|
Y = (int)floor(Y + 0.5);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PDFGetCircleCenter() */
|
|
/************************************************************************/
|
|
|
|
/* Return the center of a circle, or NULL if it is not recognized */
|
|
|
|
static OGRPoint* PDFGetCircleCenter(OGRLineString* poLS)
|
|
{
|
|
if (poLS == NULL || poLS->getNumPoints() != 5)
|
|
return NULL;
|
|
|
|
if (poLS->getY(0) == poLS->getY(2) &&
|
|
poLS->getX(1) == poLS->getX(3) &&
|
|
fabs((poLS->getX(0) + poLS->getX(2)) / 2 - poLS->getX(1)) < EPSILON &&
|
|
fabs((poLS->getY(1) + poLS->getY(3)) / 2 - poLS->getY(0)) < EPSILON)
|
|
{
|
|
return new OGRPoint((poLS->getX(0) + poLS->getX(2)) / 2,
|
|
(poLS->getY(1) + poLS->getY(3)) / 2);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PDFGetSquareCenter() */
|
|
/************************************************************************/
|
|
|
|
/* Return the center of a square, or NULL if it is not recognized */
|
|
|
|
static OGRPoint* PDFGetSquareCenter(OGRLineString* poLS)
|
|
{
|
|
if (poLS == NULL || poLS->getNumPoints() < 4 || poLS->getNumPoints() > 5)
|
|
return NULL;
|
|
|
|
if (poLS->getX(0) == poLS->getX(3) &&
|
|
poLS->getY(0) == poLS->getY(1) &&
|
|
poLS->getX(1) == poLS->getX(2) &&
|
|
poLS->getY(2) == poLS->getY(3) &&
|
|
fabs(fabs(poLS->getX(0) - poLS->getX(1)) - fabs(poLS->getY(0) - poLS->getY(3))) < EPSILON)
|
|
{
|
|
return new OGRPoint((poLS->getX(0) + poLS->getX(1)) / 2,
|
|
(poLS->getY(0) + poLS->getY(3)) / 2);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PDFGetTriangleCenter() */
|
|
/************************************************************************/
|
|
|
|
/* Return the center of a equilateral triangle, or NULL if it is not recognized */
|
|
|
|
static OGRPoint* PDFGetTriangleCenter(OGRLineString* poLS)
|
|
{
|
|
if (poLS == NULL || poLS->getNumPoints() < 3 || poLS->getNumPoints() > 4)
|
|
return NULL;
|
|
|
|
double dfSqD1 = SQUARE(poLS->getX(0) - poLS->getX(1)) + SQUARE(poLS->getY(0) - poLS->getY(1));
|
|
double dfSqD2 = SQUARE(poLS->getX(1) - poLS->getX(2)) + SQUARE(poLS->getY(1) - poLS->getY(2));
|
|
double dfSqD3 = SQUARE(poLS->getX(0) - poLS->getX(2)) + SQUARE(poLS->getY(0) - poLS->getY(2));
|
|
if (fabs(dfSqD1 - dfSqD2) < EPSILON && fabs(dfSqD2 - dfSqD3) < EPSILON)
|
|
{
|
|
return new OGRPoint((poLS->getX(0) + poLS->getX(1) + poLS->getX(2)) / 3,
|
|
(poLS->getY(0) + poLS->getY(1) + poLS->getY(2)) / 3);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PDFGetStarCenter() */
|
|
/************************************************************************/
|
|
|
|
/* Return the center of a 5-point star, or NULL if it is not recognized */
|
|
|
|
static OGRPoint* PDFGetStarCenter(OGRLineString* poLS)
|
|
{
|
|
if (poLS == NULL || poLS->getNumPoints() < 10 || poLS->getNumPoints() > 11)
|
|
return NULL;
|
|
|
|
double dfSqD01 = SQUARE(poLS->getX(0) - poLS->getX(1)) +
|
|
SQUARE(poLS->getY(0) - poLS->getY(1));
|
|
double dfSqD02 = SQUARE(poLS->getX(0) - poLS->getX(2)) +
|
|
SQUARE(poLS->getY(0) - poLS->getY(2));
|
|
double dfSqD13 = SQUARE(poLS->getX(1) - poLS->getX(3)) +
|
|
SQUARE(poLS->getY(1) - poLS->getY(3));
|
|
const double dfSin18divSin126 = 0.38196601125;
|
|
int bOK = fabs(dfSqD13 / dfSqD02 - SQUARE(dfSin18divSin126)) < EPSILON;
|
|
for(int i=1;i<10 && bOK;i++)
|
|
{
|
|
double dfSqDiip1 = SQUARE(poLS->getX(i) - poLS->getX((i+1)%10)) +
|
|
SQUARE(poLS->getY(i) - poLS->getY((i+1)%10));
|
|
if (fabs(dfSqDiip1 - dfSqD01) > EPSILON)
|
|
{
|
|
bOK = FALSE;
|
|
}
|
|
double dfSqDiip2 = SQUARE(poLS->getX(i) - poLS->getX((i+2)%10)) +
|
|
SQUARE(poLS->getY(i) - poLS->getY((i+2)%10));
|
|
if ( (i%2) == 1 && fabs(dfSqDiip2 - dfSqD13) > EPSILON )
|
|
{
|
|
bOK = FALSE;
|
|
}
|
|
if ( (i%2) == 0 && fabs(dfSqDiip2 - dfSqD02) > EPSILON )
|
|
{
|
|
bOK = FALSE;
|
|
}
|
|
}
|
|
if (bOK)
|
|
{
|
|
return new OGRPoint((poLS->getX(0) + poLS->getX(2) + poLS->getX(4) +
|
|
poLS->getX(6) + poLS->getX(8)) / 5,
|
|
(poLS->getY(0) + poLS->getY(2) + poLS->getY(4) +
|
|
poLS->getY(6) + poLS->getY(8)) / 5);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* UnstackTokens() */
|
|
/************************************************************************/
|
|
|
|
int PDFDataset::UnstackTokens(const char* pszToken,
|
|
int nRequiredArgs,
|
|
char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE],
|
|
int& nTokenStackSize,
|
|
double* adfCoords)
|
|
{
|
|
if (nTokenStackSize < nRequiredArgs)
|
|
{
|
|
CPLDebug("PDF", "not enough arguments for %s", pszToken);
|
|
return FALSE;
|
|
}
|
|
nTokenStackSize -= nRequiredArgs;
|
|
for(int i=0;i<nRequiredArgs;i++)
|
|
{
|
|
adfCoords[i] = CPLAtof(aszTokenStack[nTokenStackSize+i]);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ParseContent() */
|
|
/************************************************************************/
|
|
|
|
#define NEW_SUBPATH -99
|
|
#define CLOSE_SUBPATH -98
|
|
#define FILL_SUBPATH -97
|
|
|
|
OGRGeometry* PDFDataset::ParseContent(const char* pszContent,
|
|
GDALPDFObject* poResources,
|
|
int bInitBDCStack,
|
|
int bMatchQ,
|
|
std::map<CPLString, OGRPDFLayer*>& oMapPropertyToLayer,
|
|
OGRPDFLayer* poCurLayer)
|
|
{
|
|
|
|
#define PUSH(aszTokenStack, str, strlen) \
|
|
do \
|
|
{ \
|
|
if(nTokenStackSize < TOKEN_STACK_SIZE) \
|
|
memcpy(aszTokenStack[nTokenStackSize ++], str, strlen + 1); \
|
|
else \
|
|
{ \
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Max token stack size reached");\
|
|
return NULL; \
|
|
}; \
|
|
} while(0)
|
|
|
|
#define ADD_CHAR(szToken, c) \
|
|
do \
|
|
{ \
|
|
if(nTokenSize < MAX_TOKEN_SIZE-1) \
|
|
{ \
|
|
szToken[nTokenSize ++ ] = c; \
|
|
szToken[nTokenSize ] = '\0'; \
|
|
} \
|
|
else \
|
|
{ \
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Max token size reached");\
|
|
return NULL; \
|
|
}; \
|
|
} while(0)
|
|
|
|
char szToken[MAX_TOKEN_SIZE];
|
|
int nTokenSize = 0;
|
|
char ch;
|
|
char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE];
|
|
int nTokenStackSize = 0;
|
|
int bInString = FALSE;
|
|
int nBDCLevel = 0;
|
|
int nParenthesisLevel = 0;
|
|
int nArrayLevel = 0;
|
|
int nBTLevel = 0;
|
|
|
|
int bCollectAllObjects = poResources != NULL && !bInitBDCStack && !bMatchQ;
|
|
|
|
GraphicState oGS;
|
|
std::stack<GraphicState> oGSStack;
|
|
std::stack<OGRPDFLayer*> oLayerStack;
|
|
|
|
std::vector<double> oCoords;
|
|
int bHasFoundFill = FALSE;
|
|
int bHasMultiPart = FALSE;
|
|
|
|
szToken[0] = '\0';
|
|
|
|
if (bInitBDCStack)
|
|
{
|
|
PUSH(aszTokenStack, "dummy", 5);
|
|
PUSH(aszTokenStack, "dummy", 5);
|
|
oLayerStack.push(NULL);
|
|
}
|
|
|
|
while((ch = *pszContent) != '\0')
|
|
{
|
|
int bPushToken = FALSE;
|
|
|
|
if (!bInString && ch == '%')
|
|
{
|
|
/* Skip comments until end-of-line */
|
|
while((ch = *pszContent) != '\0')
|
|
{
|
|
if (ch == '\r' || ch == '\n')
|
|
break;
|
|
pszContent ++;
|
|
}
|
|
if (ch == 0)
|
|
break;
|
|
}
|
|
else if (!bInString && (ch == ' ' || ch == '\r' || ch == '\n'))
|
|
{
|
|
bPushToken = TRUE;
|
|
}
|
|
|
|
/* Ignore arrays */
|
|
else if (!bInString && nTokenSize == 0 && ch == '[')
|
|
{
|
|
nArrayLevel ++;
|
|
}
|
|
else if (!bInString && nArrayLevel && nTokenSize == 0 && ch == ']')
|
|
{
|
|
nArrayLevel --;
|
|
}
|
|
|
|
else if (!bInString && nTokenSize == 0 && ch == '(')
|
|
{
|
|
bInString = TRUE;
|
|
nParenthesisLevel ++;
|
|
ADD_CHAR(szToken, ch);
|
|
}
|
|
else if (bInString && ch == '(')
|
|
{
|
|
nParenthesisLevel ++;
|
|
ADD_CHAR(szToken, ch);
|
|
}
|
|
else if (bInString && ch == ')')
|
|
{
|
|
nParenthesisLevel --;
|
|
ADD_CHAR(szToken, ch);
|
|
if (nParenthesisLevel == 0)
|
|
{
|
|
bInString = FALSE;
|
|
bPushToken = TRUE;
|
|
}
|
|
}
|
|
else if (ch == '<' && pszContent[1] == '<' && nTokenSize == 0)
|
|
{
|
|
int nDictDepth = 0;
|
|
|
|
while(*pszContent != '\0')
|
|
{
|
|
if (pszContent[0] == '<' && pszContent[1] == '<')
|
|
{
|
|
ADD_CHAR(szToken, '<');
|
|
ADD_CHAR(szToken, '<');
|
|
nDictDepth ++;
|
|
pszContent += 2;
|
|
}
|
|
else if (pszContent[0] == '>' && pszContent[1] == '>')
|
|
{
|
|
ADD_CHAR(szToken, '>');
|
|
ADD_CHAR(szToken, '>');
|
|
nDictDepth --;
|
|
pszContent += 2;
|
|
if (nDictDepth == 0)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ADD_CHAR(szToken, *pszContent);
|
|
pszContent ++;
|
|
}
|
|
}
|
|
if (nDictDepth == 0)
|
|
{
|
|
bPushToken = TRUE;
|
|
pszContent --;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ADD_CHAR(szToken, ch);
|
|
}
|
|
|
|
pszContent ++;
|
|
if (pszContent[0] == '\0')
|
|
bPushToken = TRUE;
|
|
|
|
#define EQUAL1(szToken, s) (szToken[0] == s[0] && szToken[1] == '\0')
|
|
#define EQUAL2(szToken, s) (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == '\0')
|
|
#define EQUAL3(szToken, s) (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == s[2] && szToken[3] == '\0')
|
|
|
|
if (bPushToken && nTokenSize)
|
|
{
|
|
if (EQUAL2(szToken, "BI"))
|
|
{
|
|
while(*pszContent != '\0')
|
|
{
|
|
if( pszContent[0] == 'E' && pszContent[1] == 'I' && pszContent[2] == ' ' )
|
|
{
|
|
break;
|
|
}
|
|
pszContent ++;
|
|
}
|
|
if( pszContent[0] == 'E' )
|
|
pszContent += 3;
|
|
else
|
|
return NULL;
|
|
}
|
|
else if (EQUAL3(szToken, "BDC"))
|
|
{
|
|
if (nTokenStackSize < 2)
|
|
{
|
|
CPLDebug("PDF",
|
|
"not enough arguments for %s",
|
|
szToken);
|
|
return NULL;
|
|
}
|
|
nTokenStackSize -= 2;
|
|
const char* pszOC = aszTokenStack[nTokenStackSize];
|
|
const char* pszOCGName = aszTokenStack[nTokenStackSize+1];
|
|
|
|
nBDCLevel ++;
|
|
|
|
if( EQUAL3(pszOC, "/OC") && pszOCGName[0] == '/' )
|
|
{
|
|
std::map<CPLString, OGRPDFLayer*>::iterator oIter =
|
|
oMapPropertyToLayer.find(pszOCGName + 1);
|
|
if( oIter != oMapPropertyToLayer.end() )
|
|
{
|
|
poCurLayer = oIter->second;
|
|
//CPLDebug("PDF", "Cur layer : %s", poCurLayer->GetName());
|
|
}
|
|
}
|
|
|
|
oLayerStack.push(poCurLayer);
|
|
//CPLDebug("PDF", "%s %s BDC", osOC.c_str(), osOCGName.c_str());
|
|
}
|
|
else if (EQUAL3(szToken, "EMC"))
|
|
{
|
|
//CPLDebug("PDF", "EMC");
|
|
if( !oLayerStack.empty() )
|
|
{
|
|
oLayerStack.pop();
|
|
if( !oLayerStack.empty() )
|
|
poCurLayer = oLayerStack.top();
|
|
else
|
|
poCurLayer = NULL;
|
|
|
|
/*if (poCurLayer)
|
|
{
|
|
CPLDebug("PDF", "Cur layer : %s", poCurLayer->GetName());
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
poCurLayer = NULL;
|
|
//return NULL;
|
|
}
|
|
|
|
nBDCLevel --;
|
|
if (nBDCLevel == 0 && bInitBDCStack)
|
|
break;
|
|
}
|
|
|
|
/* Ignore any text stuff */
|
|
else if (EQUAL2(szToken, "BT"))
|
|
nBTLevel ++;
|
|
else if (EQUAL2(szToken, "ET"))
|
|
{
|
|
nBTLevel --;
|
|
if (nBTLevel < 0)
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (!nArrayLevel && !nBTLevel)
|
|
{
|
|
int bEmitFeature = FALSE;
|
|
|
|
if( szToken[0] < 'A' )
|
|
{
|
|
PUSH(aszTokenStack, szToken, nTokenSize);
|
|
}
|
|
else if (EQUAL1(szToken, "q"))
|
|
{
|
|
oGSStack.push(oGS);
|
|
}
|
|
else if (EQUAL1(szToken, "Q"))
|
|
{
|
|
if (oGSStack.empty())
|
|
{
|
|
CPLDebug("PDF", "not enough arguments for %s", szToken);
|
|
return NULL;
|
|
}
|
|
|
|
oGS = oGSStack.top();
|
|
oGSStack.pop();
|
|
|
|
if (oGSStack.empty() && bMatchQ)
|
|
break;
|
|
}
|
|
else if (EQUAL2(szToken, "cm"))
|
|
{
|
|
double adfMatrix[6];
|
|
if (!UnstackTokens(szToken, 6, aszTokenStack, nTokenStackSize, adfMatrix))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
oGS.MultiplyBy(adfMatrix);
|
|
}
|
|
else if (EQUAL1(szToken, "b") || /* closepath, fill, stroke */
|
|
EQUAL2(szToken, "b*") /* closepath, eofill, stroke */)
|
|
{
|
|
if (!(oCoords.size() > 0 &&
|
|
oCoords[oCoords.size() - 2] == CLOSE_SUBPATH &&
|
|
oCoords[oCoords.size() - 1] == CLOSE_SUBPATH))
|
|
{
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
}
|
|
oCoords.push_back(FILL_SUBPATH);
|
|
oCoords.push_back(FILL_SUBPATH);
|
|
bHasFoundFill = TRUE;
|
|
|
|
bEmitFeature = TRUE;
|
|
}
|
|
else if (EQUAL1(szToken, "B") || /* fill, stroke */
|
|
EQUAL2(szToken, "B*") || /* eofill, stroke */
|
|
EQUAL1(szToken, "f") || /* fill */
|
|
EQUAL1(szToken, "F") || /* fill */
|
|
EQUAL2(szToken, "f*") /* eofill */ )
|
|
{
|
|
oCoords.push_back(FILL_SUBPATH);
|
|
oCoords.push_back(FILL_SUBPATH);
|
|
bHasFoundFill = TRUE;
|
|
|
|
bEmitFeature = TRUE;
|
|
}
|
|
else if (EQUAL1(szToken, "h")) /* close subpath */
|
|
{
|
|
if (!(oCoords.size() > 0 &&
|
|
oCoords[oCoords.size() - 2] == CLOSE_SUBPATH &&
|
|
oCoords[oCoords.size() - 1] == CLOSE_SUBPATH))
|
|
{
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
}
|
|
}
|
|
else if (EQUAL1(szToken, "n")) /* new subpath without stroking or filling */
|
|
{
|
|
oCoords.resize(0);
|
|
}
|
|
else if (EQUAL1(szToken, "s")) /* close and stroke */
|
|
{
|
|
if (!(oCoords.size() > 0 &&
|
|
oCoords[oCoords.size() - 2] == CLOSE_SUBPATH &&
|
|
oCoords[oCoords.size() - 1] == CLOSE_SUBPATH))
|
|
{
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
}
|
|
|
|
bEmitFeature = TRUE;
|
|
}
|
|
else if (EQUAL1(szToken, "S")) /* stroke */
|
|
{
|
|
bEmitFeature = TRUE;
|
|
}
|
|
else if (EQUAL1(szToken, "m") || EQUAL1(szToken, "l"))
|
|
{
|
|
double adfCoords[2];
|
|
if (!UnstackTokens(szToken, 2, aszTokenStack, nTokenStackSize, adfCoords))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
if (EQUAL1(szToken, "m"))
|
|
{
|
|
if (oCoords.size() != 0)
|
|
bHasMultiPart = TRUE;
|
|
oCoords.push_back(NEW_SUBPATH);
|
|
oCoords.push_back(NEW_SUBPATH);
|
|
}
|
|
|
|
oGS.ApplyMatrix(adfCoords);
|
|
oCoords.push_back(adfCoords[0]);
|
|
oCoords.push_back(adfCoords[1]);
|
|
}
|
|
else if (EQUAL1(szToken, "c")) /* Bezier curve */
|
|
{
|
|
double adfCoords[6];
|
|
if (!UnstackTokens(szToken, 6, aszTokenStack, nTokenStackSize, adfCoords))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
oGS.ApplyMatrix(adfCoords + 4);
|
|
oCoords.push_back(adfCoords[4]);
|
|
oCoords.push_back(adfCoords[5]);
|
|
}
|
|
else if (EQUAL1(szToken, "v") || EQUAL1(szToken, "y")) /* Bezier curve */
|
|
{
|
|
double adfCoords[4];
|
|
if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
oGS.ApplyMatrix(adfCoords + 2);
|
|
oCoords.push_back(adfCoords[2]);
|
|
oCoords.push_back(adfCoords[3]);
|
|
}
|
|
else if (EQUAL2(szToken, "re")) /* Rectangle */
|
|
{
|
|
double adfCoords[4];
|
|
if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
adfCoords[2] += adfCoords[0];
|
|
adfCoords[3] += adfCoords[1];
|
|
|
|
oGS.ApplyMatrix(adfCoords);
|
|
oGS.ApplyMatrix(adfCoords + 2);
|
|
|
|
if (oCoords.size() != 0)
|
|
bHasMultiPart = TRUE;
|
|
oCoords.push_back(NEW_SUBPATH);
|
|
oCoords.push_back(NEW_SUBPATH);
|
|
oCoords.push_back(adfCoords[0]);
|
|
oCoords.push_back(adfCoords[1]);
|
|
oCoords.push_back(adfCoords[2]);
|
|
oCoords.push_back(adfCoords[1]);
|
|
oCoords.push_back(adfCoords[2]);
|
|
oCoords.push_back(adfCoords[3]);
|
|
oCoords.push_back(adfCoords[0]);
|
|
oCoords.push_back(adfCoords[3]);
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
oCoords.push_back(CLOSE_SUBPATH);
|
|
}
|
|
|
|
else if (EQUAL2(szToken, "Do"))
|
|
{
|
|
if (nTokenStackSize == 0)
|
|
{
|
|
CPLDebug("PDF",
|
|
"not enough arguments for %s",
|
|
szToken);
|
|
return NULL;
|
|
}
|
|
|
|
CPLString osObjectName = aszTokenStack[--nTokenStackSize];
|
|
|
|
if (osObjectName[0] != '/')
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
if (poResources == NULL)
|
|
{
|
|
if (osObjectName.find("/SymImage") == 0)
|
|
{
|
|
oCoords.push_back(oGS.adfCM[4] + oGS.adfCM[0] / 2);
|
|
oCoords.push_back(oGS.adfCM[5] + oGS.adfCM[3] / 2);
|
|
|
|
szToken[0] = '\0';
|
|
nTokenSize = 0;
|
|
|
|
if( poCurLayer != NULL)
|
|
bEmitFeature = TRUE;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if( !bEmitFeature )
|
|
{
|
|
GDALPDFObject* poXObject =
|
|
poResources->GetDictionary()->Get("XObject");
|
|
if (poXObject == NULL ||
|
|
poXObject->GetType() != PDFObjectType_Dictionary)
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
GDALPDFObject* poObject =
|
|
poXObject->GetDictionary()->Get(osObjectName.c_str() + 1);
|
|
if (poObject == NULL)
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
int bParseStream = TRUE;
|
|
/* Check if the object is an image. If so, no need to try to parse */
|
|
/* it. */
|
|
if (poObject->GetType() == PDFObjectType_Dictionary)
|
|
{
|
|
GDALPDFObject* poSubtype = poObject->GetDictionary()->Get("Subtype");
|
|
if (poSubtype != NULL &&
|
|
poSubtype->GetType() == PDFObjectType_Name &&
|
|
poSubtype->GetName() == "Image" )
|
|
{
|
|
bParseStream = FALSE;
|
|
}
|
|
}
|
|
|
|
if( bParseStream )
|
|
{
|
|
GDALPDFStream* poStream = poObject->GetStream();
|
|
if (!poStream)
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
char* pszStr = poStream->GetBytes();
|
|
if( pszStr )
|
|
{
|
|
OGRGeometry* poGeom = ParseContent(pszStr, NULL, FALSE, FALSE,
|
|
oMapPropertyToLayer, poCurLayer);
|
|
CPLFree(pszStr);
|
|
if (poGeom && !bCollectAllObjects)
|
|
return poGeom;
|
|
delete poGeom;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( EQUAL2(szToken, "RG") || EQUAL2(szToken, "rg") )
|
|
{
|
|
double* padf = ( EQUAL2(szToken, "RG") ) ? oGS.adfStrokeColor : oGS.adfFillColor;
|
|
if (!UnstackTokens(szToken, 3, aszTokenStack, nTokenStackSize, padf))
|
|
{
|
|
CPLDebug("PDF", "Should not happen at line %d", __LINE__);
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (oMapOperators.find(szToken) != oMapOperators.end())
|
|
{
|
|
int nArgs = oMapOperators[szToken];
|
|
if (nArgs < 0)
|
|
{
|
|
while( nTokenStackSize != 0 )
|
|
{
|
|
CPLString osTopToken = aszTokenStack[--nTokenStackSize];
|
|
if (oMapOperators.find(osTopToken) != oMapOperators.end())
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( nArgs > nTokenStackSize )
|
|
{
|
|
CPLDebug("PDF",
|
|
"not enough arguments for %s",
|
|
szToken);
|
|
return NULL;
|
|
}
|
|
nTokenStackSize -= nArgs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PUSH(aszTokenStack, szToken, nTokenSize);
|
|
}
|
|
|
|
if( bEmitFeature && poCurLayer != NULL)
|
|
{
|
|
OGRGeometry* poGeom = BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart);
|
|
bHasFoundFill = bHasMultiPart = FALSE;
|
|
if (poGeom)
|
|
{
|
|
OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
|
|
if( bSetStyle )
|
|
{
|
|
OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
|
|
if( eType == wkbLineString || eType == wkbMultiLineString )
|
|
{
|
|
poFeature->SetStyleString(CPLSPrintf("PEN(c:#%02X%02X%02X)",
|
|
(int)(oGS.adfStrokeColor[0] * 255 + 0.5),
|
|
(int)(oGS.adfStrokeColor[1] * 255 + 0.5),
|
|
(int)(oGS.adfStrokeColor[2] * 255 + 0.5)));
|
|
}
|
|
else if( eType == wkbPolygon || eType == wkbMultiPolygon )
|
|
{
|
|
poFeature->SetStyleString(CPLSPrintf("PEN(c:#%02X%02X%02X);BRUSH(fc:#%02X%02X%02X)",
|
|
(int)(oGS.adfStrokeColor[0] * 255 + 0.5),
|
|
(int)(oGS.adfStrokeColor[1] * 255 + 0.5),
|
|
(int)(oGS.adfStrokeColor[2] * 255 + 0.5),
|
|
(int)(oGS.adfFillColor[0] * 255 + 0.5),
|
|
(int)(oGS.adfFillColor[1] * 255 + 0.5),
|
|
(int)(oGS.adfFillColor[2] * 255 + 0.5)));
|
|
}
|
|
}
|
|
poGeom->assignSpatialReference(poCurLayer->GetSpatialRef());
|
|
poFeature->SetGeometryDirectly(poGeom);
|
|
poCurLayer->CreateFeature(poFeature);
|
|
delete poFeature;
|
|
}
|
|
|
|
oCoords.resize(0);
|
|
}
|
|
}
|
|
|
|
szToken[0] = '\0';
|
|
nTokenSize = 0;
|
|
}
|
|
}
|
|
|
|
if (nTokenStackSize != 0)
|
|
{
|
|
while(nTokenStackSize != 0)
|
|
{
|
|
nTokenStackSize--;
|
|
CPLDebug("PDF",
|
|
"Remaing values in stack : %s",
|
|
aszTokenStack[nTokenStackSize]);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (bCollectAllObjects)
|
|
return NULL;
|
|
|
|
return BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* BuildGeometry() */
|
|
/************************************************************************/
|
|
|
|
OGRGeometry* PDFDataset::BuildGeometry(std::vector<double>& oCoords,
|
|
int bHasFoundFill,
|
|
int bHasMultiPart)
|
|
{
|
|
OGRGeometry* poGeom = NULL;
|
|
|
|
if (!oCoords.size())
|
|
return NULL;
|
|
|
|
if (oCoords.size() == 2)
|
|
{
|
|
double X, Y;
|
|
PDFCoordsToSRSCoords(oCoords[0], oCoords[1], X, Y);
|
|
poGeom = new OGRPoint(X, Y);
|
|
}
|
|
else if (!bHasFoundFill)
|
|
{
|
|
OGRLineString* poLS = NULL;
|
|
OGRMultiLineString* poMLS = NULL;
|
|
if (bHasMultiPart)
|
|
{
|
|
poMLS = new OGRMultiLineString();
|
|
poGeom = poMLS;
|
|
}
|
|
|
|
for(size_t i=0;i<oCoords.size();i+=2)
|
|
{
|
|
if (oCoords[i] == NEW_SUBPATH && oCoords[i+1] == NEW_SUBPATH)
|
|
{
|
|
poLS = new OGRLineString();
|
|
if (poMLS)
|
|
poMLS->addGeometryDirectly(poLS);
|
|
else
|
|
poGeom = poLS;
|
|
}
|
|
else if (oCoords[i] == CLOSE_SUBPATH && oCoords[i+1] == CLOSE_SUBPATH)
|
|
{
|
|
if (poLS && poLS->getNumPoints() >= 2 &&
|
|
!(poLS->getX(0) == poLS->getX(poLS->getNumPoints()-1) &&
|
|
poLS->getY(0) == poLS->getY(poLS->getNumPoints()-1)))
|
|
{
|
|
poLS->addPoint(poLS->getX(0), poLS->getY(0));
|
|
}
|
|
}
|
|
else if (oCoords[i] == FILL_SUBPATH && oCoords[i+1] == FILL_SUBPATH)
|
|
{
|
|
/* Should not happen */
|
|
}
|
|
else
|
|
{
|
|
if (poLS)
|
|
{
|
|
double X, Y;
|
|
PDFCoordsToSRSCoords(oCoords[i], oCoords[i+1], X, Y);
|
|
|
|
poLS->addPoint(X, Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-2 : circle (not filled)) */
|
|
OGRGeometry* poCenter = NULL;
|
|
if (poCenter == NULL && poLS != NULL && poLS->getNumPoints() == 5)
|
|
{
|
|
poCenter = PDFGetCircleCenter(poLS);
|
|
}
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-4: square (not filled)) */
|
|
if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 4 || poLS->getNumPoints() == 5))
|
|
{
|
|
poCenter = PDFGetSquareCenter(poLS);
|
|
}
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-6: triangle (not filled)) */
|
|
if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 3 || poLS->getNumPoints() == 4))
|
|
{
|
|
poCenter = PDFGetTriangleCenter(poLS);
|
|
}
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-8: star (not filled)) */
|
|
if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 10 || poLS->getNumPoints() == 11))
|
|
{
|
|
poCenter = PDFGetStarCenter(poLS);
|
|
}
|
|
|
|
if (poCenter == NULL && poMLS != NULL && poMLS->getNumGeometries() == 2)
|
|
{
|
|
OGRLineString* poLS1 = (OGRLineString* )poMLS->getGeometryRef(0);
|
|
OGRLineString* poLS2 = (OGRLineString* )poMLS->getGeometryRef(1);
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-0: cross (+) ) */
|
|
if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 &&
|
|
poLS1->getY(0) == poLS1->getY(1) &&
|
|
poLS2->getX(0) == poLS2->getX(1) &&
|
|
fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - fabs(poLS2->getY(0) - poLS2->getY(1))) < EPSILON &&
|
|
fabs((poLS1->getX(0) + poLS1->getX(1)) / 2 - poLS2->getX(0)) < EPSILON &&
|
|
fabs((poLS2->getY(0) + poLS2->getY(1)) / 2 - poLS1->getY(0)) < EPSILON)
|
|
{
|
|
poCenter = new OGRPoint(poLS2->getX(0), poLS1->getY(0));
|
|
}
|
|
/* Recognize points as outputed by GDAL (ogr-sym-1: diagcross (X) ) */
|
|
else if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 &&
|
|
poLS1->getX(0) == poLS2->getX(0) &&
|
|
poLS1->getY(0) == poLS2->getY(1) &&
|
|
poLS1->getX(1) == poLS2->getX(1) &&
|
|
poLS1->getY(1) == poLS2->getY(0) &&
|
|
fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - fabs(poLS1->getY(0) - poLS1->getY(1))) < EPSILON)
|
|
{
|
|
poCenter = new OGRPoint((poLS1->getX(0) + poLS1->getX(1)) / 2,
|
|
(poLS1->getY(0) + poLS1->getY(1)) / 2);
|
|
}
|
|
}
|
|
|
|
if (poCenter)
|
|
{
|
|
delete poGeom;
|
|
poGeom = poCenter;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OGRLinearRing* poLS = NULL;
|
|
int nPolys = 0;
|
|
OGRGeometry** papoPoly = NULL;
|
|
|
|
for(size_t i=0;i<oCoords.size();i+=2)
|
|
{
|
|
if (oCoords[i] == NEW_SUBPATH && oCoords[i+1] == NEW_SUBPATH)
|
|
{
|
|
delete poLS;
|
|
poLS = new OGRLinearRing();
|
|
}
|
|
else if ((oCoords[i] == CLOSE_SUBPATH && oCoords[i+1] == CLOSE_SUBPATH) ||
|
|
(oCoords[i] == FILL_SUBPATH && oCoords[i+1] == FILL_SUBPATH))
|
|
{
|
|
if (poLS)
|
|
{
|
|
poLS->closeRings();
|
|
|
|
OGRPoint* poCenter = NULL;
|
|
|
|
if (nPolys == 0 &&
|
|
poLS &&
|
|
poLS->getNumPoints() == 5)
|
|
{
|
|
/* Recognize points as outputed by GDAL (ogr-sym-3 : circle (filled)) */
|
|
poCenter = PDFGetCircleCenter(poLS);
|
|
|
|
/* Recognize points as outputed by GDAL (ogr-sym-5: square (filled)) */
|
|
if (poCenter == NULL)
|
|
poCenter = PDFGetSquareCenter(poLS);
|
|
|
|
/* ESRI points */
|
|
if (poCenter == NULL &&
|
|
oCoords.size() == 14 &&
|
|
poLS->getY(0) == poLS->getY(1) &&
|
|
poLS->getX(1) == poLS->getX(2) &&
|
|
poLS->getY(2) == poLS->getY(3) &&
|
|
poLS->getX(3) == poLS->getX(0))
|
|
{
|
|
poCenter = new OGRPoint((poLS->getX(0) + poLS->getX(1)) / 2,
|
|
(poLS->getY(0) + poLS->getY(2)) / 2);
|
|
}
|
|
}
|
|
/* Recognize points as outputed by GDAL (ogr-sym-7: triangle (filled)) */
|
|
else if (nPolys == 0 &&
|
|
poLS &&
|
|
poLS->getNumPoints() == 4)
|
|
{
|
|
poCenter = PDFGetTriangleCenter(poLS);
|
|
}
|
|
/* Recognize points as outputed by GDAL (ogr-sym-9: star (filled)) */
|
|
else if (nPolys == 0 &&
|
|
poLS &&
|
|
poLS->getNumPoints() == 11)
|
|
{
|
|
poCenter = PDFGetStarCenter(poLS);
|
|
}
|
|
|
|
if (poCenter)
|
|
{
|
|
poGeom = poCenter;
|
|
break;
|
|
}
|
|
|
|
if (poLS->getNumPoints() >= 3)
|
|
{
|
|
OGRPolygon* poPoly = new OGRPolygon();
|
|
poPoly->addRingDirectly(poLS);
|
|
poLS = NULL;
|
|
|
|
papoPoly = (OGRGeometry**) CPLRealloc(papoPoly, (nPolys + 1) * sizeof(OGRGeometry*));
|
|
papoPoly[nPolys ++] = poPoly;
|
|
}
|
|
else
|
|
{
|
|
delete poLS;
|
|
poLS = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (poLS)
|
|
{
|
|
double X, Y;
|
|
PDFCoordsToSRSCoords(oCoords[i], oCoords[i+1], X, Y);
|
|
|
|
poLS->addPoint(X, Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete poLS;
|
|
|
|
int bIsValidGeometry;
|
|
if (nPolys == 2 &&
|
|
((OGRPolygon*)papoPoly[0])->getNumInteriorRings() == 0 &&
|
|
((OGRPolygon*)papoPoly[1])->getNumInteriorRings() == 0)
|
|
{
|
|
OGRLinearRing* poRing0 = ((OGRPolygon*)papoPoly[0])->getExteriorRing();
|
|
OGRLinearRing* poRing1 = ((OGRPolygon*)papoPoly[1])->getExteriorRing();
|
|
if (poRing0->getNumPoints() == poRing1->getNumPoints())
|
|
{
|
|
int bSameRing = TRUE;
|
|
for(int i=0;i<poRing0->getNumPoints();i++)
|
|
{
|
|
if (poRing0->getX(i) != poRing1->getX(i))
|
|
{
|
|
bSameRing = FALSE;
|
|
break;
|
|
}
|
|
if (poRing0->getY(i) != poRing1->getY(i))
|
|
{
|
|
bSameRing = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Just keep on ring if they are identical */
|
|
if (bSameRing)
|
|
{
|
|
delete papoPoly[1];
|
|
nPolys = 1;
|
|
}
|
|
}
|
|
}
|
|
if (nPolys)
|
|
{
|
|
poGeom = OGRGeometryFactory::organizePolygons(
|
|
papoPoly, nPolys, &bIsValidGeometry, NULL);
|
|
}
|
|
CPLFree(papoPoly);
|
|
}
|
|
|
|
return poGeom;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ExploreContents() */
|
|
/************************************************************************/
|
|
|
|
void PDFDataset::ExploreContents(GDALPDFObject* poObj,
|
|
GDALPDFObject* poResources)
|
|
{
|
|
std::map<CPLString, OGRPDFLayer*> oMapPropertyToLayer;
|
|
|
|
if (poObj->GetType() == PDFObjectType_Array)
|
|
{
|
|
GDALPDFArray* poArray = poObj->GetArray();
|
|
for(int i=0;i<poArray->GetLength();i++)
|
|
ExploreContents(poArray->Get(i), poResources);
|
|
}
|
|
|
|
if (poObj->GetType() != PDFObjectType_Dictionary)
|
|
return;
|
|
|
|
GDALPDFStream* poStream = poObj->GetStream();
|
|
if (!poStream)
|
|
return;
|
|
|
|
char* pszStr = poStream->GetBytes();
|
|
if (!pszStr)
|
|
return;
|
|
|
|
const char* pszMCID = (const char*) pszStr;
|
|
while((pszMCID = strstr(pszMCID, "/MCID")) != NULL)
|
|
{
|
|
const char* pszBDC = strstr(pszMCID, "BDC");
|
|
if (pszBDC)
|
|
{
|
|
/* Hack for http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */
|
|
/* FIXME: that logic is too fragile. */
|
|
const char* pszStartParsing = pszBDC;
|
|
const char* pszAfterBDC = pszBDC + 3;
|
|
int bMatchQ = FALSE;
|
|
while (pszAfterBDC[0] == ' ' || pszAfterBDC[0] == '\r' || pszAfterBDC[0] == '\n')
|
|
pszAfterBDC ++;
|
|
if (strncmp(pszAfterBDC, "0 0 m", 5) == 0)
|
|
{
|
|
const char* pszLastq = pszBDC;
|
|
while(pszLastq > pszStr && *pszLastq != 'q')
|
|
pszLastq --;
|
|
|
|
if (pszLastq > pszStr && *pszLastq == 'q' &&
|
|
(pszLastq[-1] == ' ' || pszLastq[-1] == '\r' || pszLastq[-1] == '\n') &&
|
|
(pszLastq[1] == ' ' || pszLastq[1] == '\r' || pszLastq[1] == '\n'))
|
|
{
|
|
pszStartParsing = pszLastq;
|
|
bMatchQ = TRUE;
|
|
}
|
|
}
|
|
|
|
int nMCID = atoi(pszMCID + 6);
|
|
if (GetGeometryFromMCID(nMCID) == NULL)
|
|
{
|
|
OGRGeometry* poGeom = ParseContent(pszStartParsing, poResources,
|
|
!bMatchQ, bMatchQ, oMapPropertyToLayer, NULL);
|
|
if( poGeom != NULL )
|
|
{
|
|
/* Save geometry in map */
|
|
oMapMCID[nMCID] = poGeom;
|
|
}
|
|
}
|
|
}
|
|
pszMCID += 5;
|
|
}
|
|
CPLFree(pszStr);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ExploreContentsNonStructured() */
|
|
/************************************************************************/
|
|
|
|
void PDFDataset::ExploreContentsNonStructuredInternal(GDALPDFObject* poContents,
|
|
GDALPDFObject* poResources,
|
|
std::map<CPLString, OGRPDFLayer*>& oMapPropertyToLayer)
|
|
{
|
|
if (poContents->GetType() == PDFObjectType_Array)
|
|
{
|
|
GDALPDFArray* poArray = poContents->GetArray();
|
|
char* pszConcatStr = NULL;
|
|
int nConcatLen = 0;
|
|
for(int i=0;i<poArray->GetLength();i++)
|
|
{
|
|
GDALPDFObject* poObj = poArray->Get(i);
|
|
if( poObj->GetType() != PDFObjectType_Dictionary)
|
|
break;
|
|
GDALPDFStream* poStream = poObj->GetStream();
|
|
if (!poStream)
|
|
break;
|
|
char* pszStr = poStream->GetBytes();
|
|
if (!pszStr)
|
|
break;
|
|
int nLen = (int)strlen(pszStr);
|
|
char* pszConcatStrNew = (char*)CPLRealloc(pszConcatStr, nConcatLen + nLen + 1);
|
|
if( pszConcatStrNew == NULL )
|
|
{
|
|
CPLFree(pszStr);
|
|
break;
|
|
}
|
|
pszConcatStr = pszConcatStrNew;
|
|
memcpy(pszConcatStr + nConcatLen, pszStr, nLen+1);
|
|
nConcatLen += nLen;
|
|
CPLFree(pszStr);
|
|
}
|
|
if( pszConcatStr )
|
|
ParseContent(pszConcatStr, poResources, FALSE, FALSE, oMapPropertyToLayer, NULL);
|
|
CPLFree(pszConcatStr);
|
|
return;
|
|
}
|
|
|
|
if (poContents->GetType() != PDFObjectType_Dictionary)
|
|
return;
|
|
|
|
GDALPDFStream* poStream = poContents->GetStream();
|
|
if (!poStream)
|
|
return;
|
|
|
|
char* pszStr = poStream->GetBytes();
|
|
if( !pszStr )
|
|
return;
|
|
ParseContent(pszStr, poResources, FALSE, FALSE, oMapPropertyToLayer, NULL);
|
|
CPLFree(pszStr);
|
|
}
|
|
|
|
void PDFDataset::ExploreContentsNonStructured(GDALPDFObject* poContents,
|
|
GDALPDFObject* poResources)
|
|
{
|
|
std::map<CPLString, OGRPDFLayer*> oMapPropertyToLayer;
|
|
if (poResources != NULL &&
|
|
poResources->GetType() == PDFObjectType_Dictionary)
|
|
{
|
|
GDALPDFObject* poProperties =
|
|
poResources->GetDictionary()->Get("Properties");
|
|
if (poProperties != NULL &&
|
|
poProperties->GetType() == PDFObjectType_Dictionary)
|
|
{
|
|
char** papszLayersWithRef = osLayerWithRefList.List();
|
|
char** papszIter = papszLayersWithRef;
|
|
std::map< std::pair<int, int>, OGRPDFLayer *> oMapNumGenToLayer;
|
|
while(papszIter && *papszIter)
|
|
{
|
|
char** papszTokens = CSLTokenizeString(*papszIter);
|
|
|
|
if( CSLCount(papszTokens) != 3 ) {
|
|
CSLDestroy(papszTokens);
|
|
CPLDebug("PDF", "Ignore '%s', unparsable.", *papszIter);
|
|
papszIter ++;
|
|
continue;
|
|
}
|
|
|
|
const char* pszLayerName = papszTokens[0];
|
|
int nNum = atoi(papszTokens[1]);
|
|
int nGen = atoi(papszTokens[2]);
|
|
|
|
CPLString osSanitizedName(PDFSanitizeLayerName(pszLayerName));
|
|
|
|
OGRPDFLayer* poLayer = (OGRPDFLayer*) GetLayerByName(osSanitizedName.c_str());
|
|
if (poLayer == NULL)
|
|
{
|
|
const char* pszWKT = GetProjectionRef();
|
|
OGRSpatialReference* poSRS = NULL;
|
|
if (pszWKT && pszWKT[0] != '\0')
|
|
{
|
|
poSRS = new OGRSpatialReference();
|
|
poSRS->importFromWkt((char**) &pszWKT);
|
|
}
|
|
|
|
poLayer =
|
|
new OGRPDFLayer(this, osSanitizedName.c_str(), poSRS, wkbUnknown);
|
|
delete poSRS;
|
|
|
|
papoLayers = (OGRLayer**)
|
|
CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
|
|
papoLayers[nLayers] = poLayer;
|
|
nLayers ++;
|
|
}
|
|
|
|
oMapNumGenToLayer[ std::pair<int,int>(nNum, nGen) ] = poLayer;
|
|
|
|
CSLDestroy(papszTokens);
|
|
papszIter ++;
|
|
}
|
|
|
|
std::map<CPLString, GDALPDFObject*>& oMap =
|
|
poProperties->GetDictionary()->GetValues();
|
|
std::map<CPLString, GDALPDFObject*>::iterator oIter = oMap.begin();
|
|
std::map<CPLString, GDALPDFObject*>::iterator oEnd = oMap.end();
|
|
|
|
for(; oIter != oEnd; ++oIter)
|
|
{
|
|
const char* pszKey = oIter->first.c_str();
|
|
GDALPDFObject* poObj = oIter->second;
|
|
if( poObj->GetRefNum() != 0 )
|
|
{
|
|
std::map< std::pair<int, int>, OGRPDFLayer *>::iterator
|
|
oIterNumGenToLayer = oMapNumGenToLayer.find(
|
|
std::pair<int,int>(poObj->GetRefNum(), poObj->GetRefGen()) );
|
|
if( oIterNumGenToLayer != oMapNumGenToLayer.end() )
|
|
{
|
|
oMapPropertyToLayer[pszKey] = oIterNumGenToLayer->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( nLayers == 0 )
|
|
return;
|
|
|
|
ExploreContentsNonStructuredInternal(poContents,
|
|
poResources,
|
|
oMapPropertyToLayer);
|
|
|
|
/* Remove empty layers */
|
|
int i = 0;
|
|
while(i < nLayers)
|
|
{
|
|
if (papoLayers[i]->GetFeatureCount() == 0)
|
|
{
|
|
delete papoLayers[i];
|
|
if (i < nLayers - 1)
|
|
{
|
|
memmove(papoLayers + i, papoLayers + i + 1,
|
|
(nLayers - 1 - i) * sizeof(OGRPDFLayer*));
|
|
}
|
|
nLayers --;
|
|
}
|
|
else
|
|
i ++;
|
|
}
|
|
}
|
|
|
|
#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) */
|