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
diff --git a/experimental/PdfViewer/SkPdfBasics.h b/experimental/PdfViewer/SkPdfBasics.h
index 28a18c5..a681eca 100644
--- a/experimental/PdfViewer/SkPdfBasics.h
+++ b/experimental/PdfViewer/SkPdfBasics.h
@@ -20,7 +20,7 @@
 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 @@
 
     // 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 @@
 
     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 bbd036a..5b8edc3 100644
--- a/experimental/PdfViewer/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/SkPdfRenderer.cpp
@@ -739,6 +739,64 @@
     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 @@
             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 @@
     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 @@
         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 @@
 }
 
 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 9ec5cc0..8dd5d30 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
@@ -28,7 +28,7 @@
     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 5e3bcdb..29780d0 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
@@ -839,7 +839,7 @@
         }
     }
 
-    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 @@
                     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 8f02ab8..ed2ce01 100644
--- a/experimental/PdfViewer/spec2def.py
+++ b/experimental/PdfViewer/spec2def.py
@@ -86,7 +86,7 @@
 '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'],