From e2e01ffb948468d4c3701f7f2a1d0c5ea75657f4 Mon Sep 17 00:00:00 2001 From: "edisonn@google.com" Date: Fri, 2 Aug 2013 20:24:48 +0000 Subject: [PATCH] pdfviewer: implementation of one type of pattern - simple tile patern, colored, with xstep and ystep positive. Review URL: https://codereview.chromium.org/21919003 git-svn-id: http://skia.googlecode.com/svn/trunk@10523 2bbb7eff-a529-9590-31e7-b0007b416f81 --- experimental/PdfViewer/SkPdfBasics.h | 25 ++- experimental/PdfViewer/SkPdfRenderer.cpp | 181 +++++++++++++++--- .../pdfparser/native/SkPdfNativeTokenizer.cpp | 2 +- .../PdfViewer/pdfparser/native/SkPdfObject.h | 12 +- experimental/PdfViewer/spec2def.py | 2 +- 5 files changed, 183 insertions(+), 39 deletions(-) diff --git a/experimental/PdfViewer/SkPdfBasics.h b/experimental/PdfViewer/SkPdfBasics.h index 28a18c5bc5..a681eca710 100644 --- a/experimental/PdfViewer/SkPdfBasics.h +++ b/experimental/PdfViewer/SkPdfBasics.h @@ -20,7 +20,7 @@ class SkNativeParsedPDF; class SkPdfAllocator; // TODO(edisonn): better class design. -struct SkPdfColorOperator { +class SkPdfColorOperator { /* color space name or array The current color space in which color values are to be interpreted @@ -31,8 +31,11 @@ struct SkPdfColorOperator { // TODO(edisonn): implement the array part too // does not own the char* +// TODO(edisonn): remove this public, let fields be private +// TODO(edisonn): make color space an enum! +public: NotOwnedString fColorSpace; - + SkPdfObject* fPattern; /* color (various) The current color to be used during painting operations (see Section @@ -45,15 +48,29 @@ struct SkPdfColorOperator { SkColor fColor; double fOpacity; // ca or CA + // TODO(edisonn): add here other color space options. +public: void setRGBColor(SkColor color) { // TODO(edisonn): ASSERT DeviceRGB is the color space. + fPattern = NULL; fColor = color; } // TODO(edisonn): double check the default values for all fields. - SkPdfColorOperator() : fColor(SK_ColorBLACK), fOpacity(1) { - NotOwnedString::init(&fColorSpace); + SkPdfColorOperator() : fPattern(NULL), fColor(SK_ColorBLACK), fOpacity(1) { + NotOwnedString::init(&fColorSpace, "DeviceRGB"); + } + + void setColorSpace(NotOwnedString* colorSpace) { + fColorSpace = *colorSpace; + fPattern = NULL; + } + + void setPatternColorSpace(SkPdfObject* pattern) { + fColorSpace.fBuffer = (const unsigned char*)"Pattern"; + fColorSpace.fBytes = 7; // strlen("Pattern") + fPattern = pattern; } void applyGraphicsState(SkPaint* paint) { diff --git a/experimental/PdfViewer/SkPdfRenderer.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp index bbd036a00b..5b8edc3ee3 100644 --- a/experimental/PdfViewer/SkPdfRenderer.cpp +++ b/experimental/PdfViewer/SkPdfRenderer.cpp @@ -739,6 +739,64 @@ static PdfResult doXObject_Form(PdfContext* pdfContext, SkCanvas* canvas, SkPdfT return kPartial_PdfResult; } + +// TODO(edisonn): Extract a class like ObjWithStream +static PdfResult doXObject_Pattern(PdfContext* pdfContext, SkCanvas* canvas, SkPdfType1PatternDictionary* skobj) { + if (!skobj || !skobj->hasStream()) { + return kIgnoreError_PdfResult; + } + + if (!skobj->has_BBox()) { + return kIgnoreError_PdfResult; + } + + PdfOp_q(pdfContext, canvas, NULL); + + canvas->save(); + + + if (skobj->Resources(pdfContext->fPdfDoc)) { + pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc); + } + + SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix"); + + if (skobj->has_Matrix()) { + pdfContext->fGraphicsState.fCTM.preConcat(skobj->Matrix(pdfContext->fPdfDoc)); + pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM; + pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM; + // TODO(edisonn) reset matrixTm and matricTlm also? + } + + SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix"); + + canvas->setMatrix(pdfContext->fGraphicsState.fCTM); + + SkRect bbox = skobj->BBox(pdfContext->fPdfDoc); + canvas->clipRect(bbox, SkRegion::kIntersect_Op, true); // TODO(edisonn): AA from settings. + + // TODO(edisonn): iterate smart on the stream even if it is compressed, tokenize it as we go. + // For this PdfContentsTokenizer needs to be extended. + + SkPdfStream* stream = (SkPdfStream*)skobj; + + SkPdfNativeTokenizer* tokenizer = + pdfContext->fPdfDoc->tokenizerOfStream(stream, pdfContext->fTmpPageAllocator); + if (tokenizer != NULL) { + PdfMainLooper looper(NULL, tokenizer, pdfContext, canvas); + looper.loop(); + delete tokenizer; + } + + // TODO(edisonn): should we restore the variable stack at the same state? + // There could be operands left, that could be consumed by a parent tokenizer when we pop. + + canvas->restore(); + PdfOp_Q(pdfContext, canvas, NULL); + return kPartial_PdfResult; +} + + //static PdfResult doXObject_PS(PdfContext* pdfContext, SkCanvas* canvas, const SkPdfObject* obj) { // return kNYI_PdfResult; //} @@ -826,9 +884,14 @@ static PdfResult doXObject(PdfContext* pdfContext, SkCanvas* canvas, const SkPdf return doXObject_Form(pdfContext, canvas, (SkPdfType1FormDictionary*)obj); //case kObjectDictionaryXObjectPS_SkPdfObjectType: //return doXObject_PS(skxobj.asPS()); - default: - return kIgnoreError_PdfResult; + default: { + if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(obj) != kNone_SkPdfObjectType) { + SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)obj; + return doXObject_Pattern(pdfContext, canvas, pattern); + } + } } + return kIgnoreError_PdfResult; } static PdfResult doPage(PdfContext* pdfContext, SkCanvas* canvas, SkPdfPageObjectDictionary* skobj) { @@ -1165,29 +1228,100 @@ static PdfResult PdfOp_fillAndStroke(PdfContext* pdfContext, SkCanvas* canvas, b if (fill && !stroke && path.isLine(line)) { paint.setStyle(SkPaint::kStroke_Style); + // TODO(edisonn): implement this with patterns pdfContext->fGraphicsState.applyGraphicsState(&paint, false); paint.setStrokeWidth(SkDoubleToScalar(0)); canvas->drawPath(path, paint); } else { if (fill) { - paint.setStyle(SkPaint::kFill_Style); - if (evenOdd) { - path.setFillType(SkPath::kEvenOdd_FillType); + if (strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0 && + pdfContext->fGraphicsState.fNonStroking.fPattern != NULL) { + + // TODO(edisonn): we can use a shader here, like imageshader to draw fast. ultimately, + // if this is not possible, and we are in rasper mode, and the cells don't intersect, we could even have multiple cpus. + + canvas->save(); + PdfOp_q(pdfContext, canvas, NULL); + + if (evenOdd) { + path.setFillType(SkPath::kEvenOdd_FillType); + } + canvas->clipPath(path); + + if (pdfContext->fPdfDoc->mapper()->mapType1PatternDictionary(pdfContext->fGraphicsState.fNonStroking.fPattern) != kNone_SkPdfObjectType) { + SkPdfType1PatternDictionary* pattern = (SkPdfType1PatternDictionary*)pdfContext->fGraphicsState.fNonStroking.fPattern; + + // TODO(edisonn): constants + // TODO(edisonn): colored + if (pattern->PaintType(pdfContext->fPdfDoc) == 1) { + int xStep = (int)pattern->XStep(pdfContext->fPdfDoc); + int yStep = (int)pattern->YStep(pdfContext->fPdfDoc); + + SkRect bounds = path.getBounds(); + SkScalar x; + SkScalar y; + + // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse + + y = bounds.top(); + int totalx = 0; + int totaly = 0; + while (y < bounds.bottom()) { + x = bounds.left(); + totalx = 0; + + while (x < bounds.right()) { + doXObject(pdfContext, canvas, pattern); + + pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(xStep), SkIntToScalar(0)); + totalx += xStep; + x += SkIntToScalar(xStep); + } + pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(-totalx), SkIntToScalar(0)); + + pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(-yStep)); + totaly += yStep; + y += SkIntToScalar(yStep); + } + pdfContext->fGraphicsState.fCTM.preTranslate(SkIntToScalar(0), SkIntToScalar(totaly)); + } + } + + // apply matrix + // get xstep, y step, bbox ... for cliping, and bos of the path + + PdfOp_Q(pdfContext, canvas, NULL); + canvas->restore(); + } else { + paint.setStyle(SkPaint::kFill_Style); + if (evenOdd) { + path.setFillType(SkPath::kEvenOdd_FillType); + } + + pdfContext->fGraphicsState.applyGraphicsState(&paint, false); + + canvas->drawPath(path, paint); } - - pdfContext->fGraphicsState.applyGraphicsState(&paint, false); - - canvas->drawPath(path, paint); } if (stroke) { - paint.setStyle(SkPaint::kStroke_Style); + if (false && strncmp((char*)pdfContext->fGraphicsState.fNonStroking.fColorSpace.fBuffer, "Pattern", strlen("Pattern")) == 0) { + // TODO(edisonn): implement Pattern for strokes + paint.setStyle(SkPaint::kStroke_Style); - pdfContext->fGraphicsState.applyGraphicsState(&paint, true); + paint.setColor(SK_ColorGREEN); - path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke - canvas->drawPath(path, paint); + path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke + canvas->drawPath(path, paint); + } else { + paint.setStyle(SkPaint::kStroke_Style); + + pdfContext->fGraphicsState.applyGraphicsState(&paint, true); + + path.setFillType(SkPath::kWinding_FillType); // reset it, just in case it messes up the stroke + canvas->drawPath(path, paint); + } } } @@ -1459,16 +1593,16 @@ static PdfResult PdfOp_SCN_scn(PdfContext* pdfContext, SkCanvas* canvas, SkPdfCo SkPdfObject* name = pdfContext->fObjectStack.top(); pdfContext->fObjectStack.pop(); //Next, get the ExtGState Dictionary from the Resource Dictionary: - SkPdfDictionary* extGStateDictionary = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc); + SkPdfDictionary* patternResources = pdfContext->fGraphicsState.fResources->Pattern(pdfContext->fPdfDoc); - if (extGStateDictionary == NULL) { + if (patternResources == NULL) { #ifdef PDF_TRACE printf("ExtGState is NULL!\n"); #endif return kIgnoreError_PdfResult; } - /*SkPdfObject* value = */pdfContext->fPdfDoc->resolveReference(extGStateDictionary->get(name)); + colorOperator->setPatternColorSpace(pdfContext->fPdfDoc->resolveReference(patternResources->get(name))); } // TODO(edisonn): SCN supports more color spaces than SCN. Read and implement spec. @@ -2262,21 +2396,6 @@ void PdfInlineImageLooper::loop() { } PdfResult PdfInlineImageLooper::done() { - - // TODO(edisonn): long to short names - // TODO(edisonn): set properties in a map - // TODO(edisonn): extract bitmap stream, check if PoDoFo has public utilities to uncompress - // the stream. - - SkBitmap bitmap; - setup_bitmap(&bitmap, 50, 50, SK_ColorRED); - - // TODO(edisonn): matrix use. - // Draw dummy red square, to show the prezence of the inline image. - fCanvas->drawBitmap(bitmap, - SkDoubleToScalar(0), - SkDoubleToScalar(0), - NULL); return kNYI_PdfResult; } diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp index 9ec5cc0925..8dd5d30c5c 100644 --- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp +++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp @@ -28,7 +28,7 @@ static char* strrstrk(char* hayStart, char* hayEnd, const char* needle) { return NULL; } -#ifdef PDF_TRACE +#ifdef PDF_TRACE_TOKENIZER static void TRACE_INDENT(int level, const char* type) { static int id = 0; id++; diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h index 5e3bcdbe1d..29780d07cc 100644 --- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h +++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h @@ -839,7 +839,7 @@ public: } } - SkString toString(int firstRowLevel = 0, int level = 0) const { + SkString toString(int firstRowLevel = 0, int level = 0) { SkString str; appendSpaces(&str, firstRowLevel); switch (fObjectType) { @@ -905,7 +905,15 @@ public: appendSpaces(&str, level); str.append(">>"); if (hasStream()) { - str.append("stream HAS_STREAM endstream"); + const unsigned char* stream = NULL; + size_t length = 0; + if (GetFilteredStreamRef(&stream, &length)) { + str.append("stream"); + str.append((const char*)stream, length > 256 ? 256 : length); + str.append("endstream"); + } else { + str.append("stream STREAM_ERROR endstream"); + } } } break; diff --git a/experimental/PdfViewer/spec2def.py b/experimental/PdfViewer/spec2def.py index 8f02ab8aca..ed2ce0120a 100644 --- a/experimental/PdfViewer/spec2def.py +++ b/experimental/PdfViewer/spec2def.py @@ -86,7 +86,7 @@ tableToClassName = { 'TABLE 4.15': ['LabColorSpaceDictionary', 'Entries in a Lab color space dictionary'], 'TABLE 4.16': ['IccProfileStreamDictionary', 'Additional entries specific to an ICC profile stream dictionary'], 'TABLE 4.20': ['DeviceNColorSpaceDictionary', 'Entry in a DeviceN color space attributes dictionary'], -'TABLE 4.22': ['Type1PatternDictionary', 'Additional entries specific to a type 1 pattern dictionary'], +'TABLE 4.22': ['Type1PatternDictionary', 'Additional entries specific to a type 1 pattern dictionary', '', {'PatternType': '[datatypes.PdfInteger(1)]'}], 'TABLE 4.23': ['Type2PatternDictionary', 'Entries in a type 2 pattern dictionary'], 'TABLE 4.25': ['ShadingDictionary', 'Entries common to all shading dictionaries'], 'TABLE 4.26': ['Type1ShadingDictionary', 'Additional entries specific to a type 1 shading dictionary', 'ShadingDictionary'],