pdfviewer: load files with missing xref (we need in order to help with testing, as most good pdfx in the whild miss the xref). add period as a valid character to start a real value.
Review URL: https://codereview.chromium.org/21096006 git-svn-id: http://skia.googlecode.com/svn/trunk@10423 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
d49173afc8
commit
4ef4bed00e
@ -14,6 +14,7 @@ class SkPdfFont;
|
||||
class SkPdfDoc;
|
||||
class SkPdfObject;
|
||||
class SkPdfResourceDictionary;
|
||||
class SkPdfSoftMaskDictionary;
|
||||
|
||||
class SkNativeParsedPDF;
|
||||
class SkPdfAllocator;
|
||||
@ -202,7 +203,8 @@ soft mask dictionary (PDF 1.4) A soft-mask dictionary (see “Soft-Ma
|
||||
transparency group XObject (see Section 7.5.5, “Transparency
|
||||
Group XObjects”). Initial value: None.
|
||||
*/
|
||||
SkBitmap fSMask;
|
||||
SkPdfSoftMaskDictionary* fSoftMaskDictionary;
|
||||
SkBitmap fSMask;
|
||||
|
||||
|
||||
/*
|
||||
|
@ -615,6 +615,7 @@ static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdf
|
||||
|
||||
SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
|
||||
|
||||
// TODO(edisonn): soft mask type? alpha/luminosity.
|
||||
if (sMask.empty()) {
|
||||
canvas->drawBitmapRect(image, dst, NULL);
|
||||
} else {
|
||||
@ -622,6 +623,7 @@ static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdf
|
||||
canvas->drawBitmapRect(image, dst, NULL);
|
||||
SkPaint xfer;
|
||||
pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
|
||||
// TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
|
||||
xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
|
||||
canvas->drawBitmapRect(sMask, dst, &xfer);
|
||||
canvas->restore();
|
||||
@ -632,15 +634,15 @@ static PdfResult doXObject_Image(PdfContext* pdfContext, SkCanvas* canvas, SkPdf
|
||||
return kPartial_PdfResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1FormDictionary* skobj) {
|
||||
if (!skobj || !skobj->hasStream()) {
|
||||
return kIgnoreError_PdfResult;
|
||||
}
|
||||
|
||||
PdfOp_q(pdfContext, canvas, NULL);
|
||||
|
||||
|
||||
|
||||
canvas->save();
|
||||
|
||||
|
||||
@ -841,7 +843,12 @@ static PdfResult PdfOp_TL(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLoop
|
||||
}
|
||||
|
||||
static PdfResult PdfOp_Td(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
|
||||
#ifdef PDF_TRACE
|
||||
printf("stack size = %i\n", (int)pdfContext->fObjectStack.size());
|
||||
#endif
|
||||
double ty = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
|
||||
SkPdfObject* obj = pdfContext->fObjectStack.top();
|
||||
obj = obj;
|
||||
double tx = pdfContext->fObjectStack.top()->numberValue(); pdfContext->fObjectStack.pop();
|
||||
|
||||
double array[6] = {1, 0, 0, 1, tx, ty};
|
||||
@ -1665,8 +1672,7 @@ void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendMo
|
||||
void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
|
||||
// TODO(edisonn): verify input
|
||||
if (pdfContext->fPdfDoc->mapper()->mapSoftMaskDictionary(sMask)) {
|
||||
//SkPdfSoftMaskDictionary* smd = (SkPdfSoftMaskDictionary*)sMask;
|
||||
// TODO(edisonn): load soft mask
|
||||
pdfContext->fGraphicsState.fSoftMaskDictionary = (SkPdfSoftMaskDictionary*)sMask;
|
||||
} else if (pdfContext->fPdfDoc->mapper()->mapSoftMaskImageDictionary(sMask)) {
|
||||
SkPdfSoftMaskImageDictionary* smid = (SkPdfSoftMaskImageDictionary*)sMask;
|
||||
pdfContext->fGraphicsState.fSMask = getImageFromObject(pdfContext, smid, true);
|
||||
@ -1676,6 +1682,12 @@ void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary*
|
||||
}
|
||||
|
||||
void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
|
||||
if (sMask == "None") {
|
||||
pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
|
||||
pdfContext->fGraphicsState.fSMask = SkBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
//Next, get the ExtGState Dictionary from the Resource Dictionary:
|
||||
SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->ExtGState(pdfContext->fPdfDoc);
|
||||
|
||||
@ -1692,6 +1704,10 @@ void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string
|
||||
// TODO (edisonn): report error/warning
|
||||
return;
|
||||
}
|
||||
|
||||
pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
|
||||
pdfContext->fGraphicsState.fSMask = SkBitmap();
|
||||
|
||||
skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
|
||||
}
|
||||
|
||||
@ -2051,22 +2067,18 @@ void reportPdfRenderStats() {
|
||||
}
|
||||
|
||||
PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
|
||||
char keyword[256];
|
||||
|
||||
if (token.fType == kKeyword_TokenType && token.fKeywordLength < 256)
|
||||
{
|
||||
strncpy(keyword, token.fKeyword, token.fKeywordLength);
|
||||
keyword[token.fKeywordLength] = '\0';
|
||||
// TODO(edisonn): log trace flag (verbose, error, info, warning, ...)
|
||||
PdfOperatorRenderer pdfOperatorRenderer = NULL;
|
||||
if (gPdfOps.find(keyword, &pdfOperatorRenderer) && pdfOperatorRenderer) {
|
||||
if (gPdfOps.find(token.fKeyword, token.fKeywordLength, &pdfOperatorRenderer) && pdfOperatorRenderer) {
|
||||
// caller, main work is done by pdfOperatorRenderer(...)
|
||||
PdfTokenLooper* childLooper = NULL;
|
||||
PdfResult result = pdfOperatorRenderer(fPdfContext, fCanvas, &childLooper);
|
||||
|
||||
int cnt = 0;
|
||||
gRenderStats[result].find(keyword, &cnt);
|
||||
gRenderStats[result].set(keyword, cnt + 1);
|
||||
gRenderStats[result].find(token.fKeyword, token.fKeywordLength, &cnt);
|
||||
gRenderStats[result].set(token.fKeyword, token.fKeywordLength, cnt + 1);
|
||||
|
||||
if (childLooper) {
|
||||
childLooper->setUp(this);
|
||||
@ -2075,8 +2087,8 @@ PdfResult PdfMainLooper::consumeToken(PdfToken& token) {
|
||||
}
|
||||
} else {
|
||||
int cnt = 0;
|
||||
gRenderStats[kUnsupported_PdfResult].find(keyword, &cnt);
|
||||
gRenderStats[kUnsupported_PdfResult].set(keyword, cnt + 1);
|
||||
gRenderStats[kUnsupported_PdfResult].find(token.fKeyword, token.fKeywordLength, &cnt);
|
||||
gRenderStats[kUnsupported_PdfResult].set(token.fKeyword, token.fKeywordLength, cnt + 1);
|
||||
}
|
||||
}
|
||||
else if (token.fType == kObject_TokenType)
|
||||
|
@ -123,7 +123,7 @@ void SkNativeParsedPDF::init(const void* bytes, size_t length) {
|
||||
bool storeCatalog = true;
|
||||
while (xrefByteOffset >= 0) {
|
||||
const unsigned char* trailerStart = readCrossReferenceSection(fFileContent + xrefByteOffset, xrefstartKeywordLine);
|
||||
xrefByteOffset = readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog);
|
||||
readTrailer(trailerStart, xrefstartKeywordLine, storeCatalog, &xrefByteOffset, false);
|
||||
storeCatalog = false;
|
||||
}
|
||||
|
||||
@ -141,6 +141,12 @@ void SkNativeParsedPDF::init(const void* bytes, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(edisonn): clean up this doc, or better, let the caller call again and build a new doc
|
||||
// caller should be a static function.
|
||||
if (pages() == 0) {
|
||||
loadWithoutXRef();
|
||||
}
|
||||
|
||||
// TODO(edisonn): corrupted pdf, read it from beginning and rebuild (xref, trailer, or just reall all objects)
|
||||
// 0 pages
|
||||
|
||||
@ -148,6 +154,67 @@ void SkNativeParsedPDF::init(const void* bytes, size_t length) {
|
||||
// and resolve references?... or not ...
|
||||
}
|
||||
|
||||
void SkNativeParsedPDF::loadWithoutXRef() {
|
||||
const unsigned char* current = fFileContent;
|
||||
const unsigned char* end = fFileContent + fContentLength;
|
||||
|
||||
// TODO(edisonn): read pdf version
|
||||
current = ignoreLine(current, end);
|
||||
|
||||
current = skipPdfWhiteSpaces(0, current, end);
|
||||
while (current < end) {
|
||||
SkPdfObject token;
|
||||
current = nextObject(0, current, end, &token, NULL, NULL);
|
||||
if (token.isInteger()) {
|
||||
int id = (int)token.intValue();
|
||||
|
||||
token.reset();
|
||||
current = nextObject(0, current, end, &token, NULL, NULL);
|
||||
// int generation = (int)token.intValue(); // TODO(edisonn): ignored for now
|
||||
|
||||
token.reset();
|
||||
current = nextObject(0, current, end, &token, NULL, NULL);
|
||||
// TODO(edisonn): must be obj, return error if not? ignore ?
|
||||
if (!token.isKeyword("obj")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (fObjects.count() < id + 1) {
|
||||
reset(fObjects.append());
|
||||
}
|
||||
|
||||
fObjects[id].fOffset = current - fFileContent;
|
||||
|
||||
SkPdfObject* obj = fAllocator->allocObject();
|
||||
current = nextObject(0, current, end, obj, fAllocator, this);
|
||||
|
||||
fObjects[id].fResolvedReference = obj;
|
||||
fObjects[id].fObj = obj;
|
||||
|
||||
// set objects
|
||||
} else if (token.isKeyword("trailer")) {
|
||||
long dummy;
|
||||
current = readTrailer(current, end, true, &dummy, true);
|
||||
} else if (token.isKeyword("startxref")) {
|
||||
token.reset();
|
||||
current = nextObject(0, current, end, &token, NULL, NULL); // ignore
|
||||
}
|
||||
|
||||
current = skipPdfWhiteSpaces(0, current, end);
|
||||
}
|
||||
|
||||
if (fRootCatalogRef) {
|
||||
fRootCatalog = (SkPdfCatalogDictionary*)resolveReference(fRootCatalogRef);
|
||||
if (fRootCatalog->isDictionary() && fRootCatalog->valid()) {
|
||||
SkPdfPageTreeNodeDictionary* tree = fRootCatalog->Pages(this);
|
||||
if (tree && tree->isDictionary() && tree->valid()) {
|
||||
fillPages(tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(edisonn): NYI
|
||||
SkNativeParsedPDF::~SkNativeParsedPDF() {
|
||||
sk_free((void*)fFileContent);
|
||||
@ -208,43 +275,47 @@ const unsigned char* SkNativeParsedPDF::readCrossReferenceSection(const unsigned
|
||||
return current;
|
||||
}
|
||||
|
||||
long SkNativeParsedPDF::readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog) {
|
||||
SkPdfObject trailerKeyword;
|
||||
// TODO(edisonn): use null allocator, and let it just fail if memory
|
||||
// needs allocated (but no crash)!
|
||||
const unsigned char* current =
|
||||
nextObject(0, trailerStart, trailerEnd, &trailerKeyword, NULL, NULL);
|
||||
const unsigned char* SkNativeParsedPDF::readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog, long* prev, bool skipKeyword) {
|
||||
*prev = -1;
|
||||
|
||||
if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
|
||||
strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
|
||||
// TODO(edisonn): report warning, rebuild trailer from objects.
|
||||
return -1;
|
||||
const unsigned char* current = trailerStart;
|
||||
if (!skipKeyword) {
|
||||
SkPdfObject trailerKeyword;
|
||||
// TODO(edisonn): use null allocator, and let it just fail if memory
|
||||
// needs allocated (but no crash)!
|
||||
current = nextObject(0, current, trailerEnd, &trailerKeyword, NULL, NULL);
|
||||
|
||||
if (!trailerKeyword.isKeyword() || strlen("trailer") != trailerKeyword.lenstr() ||
|
||||
strncmp(trailerKeyword.c_str(), "trailer", strlen("trailer")) != 0) {
|
||||
// TODO(edisonn): report warning, rebuild trailer from objects.
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
SkPdfObject token;
|
||||
current = nextObject(0, current, trailerEnd, &token, fAllocator, NULL);
|
||||
if (!token.isDictionary()) {
|
||||
return -1;
|
||||
return current;
|
||||
}
|
||||
SkPdfFileTrailerDictionary* trailer = (SkPdfFileTrailerDictionary*)&token;
|
||||
if (!trailer->valid()) {
|
||||
return -1;
|
||||
return current;
|
||||
}
|
||||
|
||||
if (storeCatalog) {
|
||||
const SkPdfObject* ref = trailer->Root(NULL);
|
||||
if (ref == NULL || !ref->isReference()) {
|
||||
// TODO(edisonn): oops, we have to fix the corrup pdf file
|
||||
return -1;
|
||||
return current;
|
||||
}
|
||||
fRootCatalogRef = ref;
|
||||
}
|
||||
|
||||
if (trailer->has_Prev()) {
|
||||
return (long)trailer->Prev(NULL);
|
||||
*prev = (long)trailer->Prev(NULL);
|
||||
}
|
||||
|
||||
return -1;
|
||||
return current;
|
||||
}
|
||||
|
||||
void SkNativeParsedPDF::addCrossSectionInfo(int id, int generation, int offset, bool isFreed) {
|
||||
@ -255,6 +326,7 @@ void SkNativeParsedPDF::addCrossSectionInfo(int id, int generation, int offset,
|
||||
|
||||
fObjects[id].fOffset = offset;
|
||||
fObjects[id].fObj = NULL;
|
||||
fObjects[id].fResolvedReference = NULL;
|
||||
}
|
||||
|
||||
SkPdfObject* SkNativeParsedPDF::readObject(int id/*, int expectedGeneration*/) {
|
||||
|
@ -72,9 +72,10 @@ private:
|
||||
|
||||
// Takes ownership of bytes.
|
||||
void init(const void* bytes, size_t length);
|
||||
void loadWithoutXRef();
|
||||
|
||||
const unsigned char* readCrossReferenceSection(const unsigned char* xrefStart, const unsigned char* trailerEnd);
|
||||
long readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog);
|
||||
const unsigned char* readTrailer(const unsigned char* trailerStart, const unsigned char* trailerEnd, bool storeCatalog, long* prev, bool skipKeyword);
|
||||
|
||||
// TODO(edisonn): updates not supported right now, generation ignored
|
||||
void addCrossSectionInfo(int id, int generation, int offset, bool isFreed);
|
||||
|
@ -83,9 +83,9 @@ static void TRACE_HEXSTRING(const unsigned char* start, const unsigned char* end
|
||||
#define TRACE_HEXSTRING(start,end)
|
||||
#endif
|
||||
|
||||
static const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* start, const unsigned char* end) {
|
||||
const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* start, const unsigned char* end) {
|
||||
TRACE_INDENT(level, "White Space");
|
||||
while (start < end && isPdfWhiteSpace(*start)) {
|
||||
while (start < end && (isPdfWhiteSpace(*start) || *start == kComment_PdfDelimiter)) {
|
||||
TRACE_COMMENT(*start);
|
||||
if (*start == kComment_PdfDelimiter) {
|
||||
// skip the comment until end of line
|
||||
@ -103,7 +103,7 @@ static const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* s
|
||||
}
|
||||
|
||||
// TODO(edisonn) '(' can be used, will it break the string a delimiter or space inside () ?
|
||||
static const unsigned char* endOfPdfToken(int level, const unsigned char* start, const unsigned char* end) {
|
||||
const unsigned char* endOfPdfToken(int level, const unsigned char* start, const unsigned char* end) {
|
||||
//int opened brackets
|
||||
//TODO(edisonn): what out for special chars, like \n, \032
|
||||
TRACE_INDENT(level, "Token");
|
||||
@ -636,6 +636,21 @@ static const unsigned char* readStream(int level, const unsigned char* start, co
|
||||
// TODO(edisonn): laod external streams
|
||||
// TODO(edisonn): look at the last filter, to determione how to deal with possible issue
|
||||
|
||||
|
||||
if (length >= 0) {
|
||||
const unsigned char* endstream = start + length;
|
||||
|
||||
if (endstream[0] == kCR_PdfWhiteSpace && endstream[1] == kLF_PdfWhiteSpace) {
|
||||
endstream += 2;
|
||||
} else if (endstream[0] == kLF_PdfWhiteSpace) {
|
||||
endstream += 1;
|
||||
}
|
||||
|
||||
if (strncmp((const char*)endstream, "endstream", strlen("endstream")) != 0) {
|
||||
length = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
// scan the buffer, until we find first endstream
|
||||
// TODO(edisonn): all buffers must have a 0 at the end now,
|
||||
|
@ -60,11 +60,10 @@ class SkPdfImageDictionary;
|
||||
#define isPdfWhiteSpaceOrPdfDelimiter(ch) (isPdfWhiteSpace(ch)||isPdfDelimiter(ch))
|
||||
|
||||
#define isPdfDigit(ch) ((ch)>='0'&&(ch)<='9')
|
||||
#define isPdfNumeric(ch) (isPdfDigit(ch)||(ch)=='+'||(ch)=='-')
|
||||
#define isPdfNumeric(ch) (isPdfDigit(ch)||(ch)=='+'||(ch)=='-'||(ch)=='.')
|
||||
|
||||
const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* buffer, size_t len);
|
||||
const unsigned char* endOfPdfToken(int level, const unsigned char* start, size_t len);
|
||||
const unsigned char* skipPdfComment(int level, const unsigned char* start, size_t len);
|
||||
const unsigned char* skipPdfWhiteSpaces(int level, const unsigned char* buffer, const unsigned char* end);
|
||||
const unsigned char* endOfPdfToken(int level, const unsigned char* start, const unsigned char* end);
|
||||
|
||||
// TODO(edisonn): typedef read and integer tyepes? make less readable...
|
||||
//typedef double SkPdfReal;
|
||||
|
@ -527,6 +527,22 @@ public:
|
||||
return fObjectType == kKeyword_PdfObjectType;
|
||||
}
|
||||
|
||||
bool isKeyword(const char* keyword) const {
|
||||
if (!isKeyword()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(keyword) != fStr.fBytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncmp(keyword, (const char*)fStr.fBuffer, fStr.fBytes) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isName() const {
|
||||
return fObjectType == kName_PdfObjectType;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user