pdfviewer: gs operator implementation: dashing and font

Review URL: https://codereview.chromium.org/20810002

git-svn-id: http://skia.googlecode.com/svn/trunk@10403 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/SkPdfBasics.cpp b/experimental/PdfViewer/SkPdfBasics.cpp
index e689b14..dc320a5 100644
--- a/experimental/PdfViewer/SkPdfBasics.cpp
+++ b/experimental/PdfViewer/SkPdfBasics.cpp
@@ -1,6 +1,8 @@
 #include "SkPdfBasics.h"
 #include "SkPdfNativeTokenizer.h"
 
+#include "SkDashPathEffect.h"
+
 PdfContext::PdfContext(SkNativeParsedPDF* doc)
     : fPdfDoc(doc)
     , fTmpPageAllocator(new SkPdfAllocator()) {
@@ -9,3 +11,25 @@
 PdfContext::~PdfContext() {
     delete fTmpPageAllocator;
 }
+
+void SkPdfGraphicsState::applyGraphicsState(SkPaint* paint, bool stroking) {
+    if (stroking) {
+        fStroking.applyGraphicsState(paint);
+    } else {
+        fNonStroking.applyGraphicsState(paint);
+    }
+
+    // TODO(edisonn): get this from pdfContext->options,
+    // or pdfContext->addPaintOptions(&paint);
+    paint->setAntiAlias(true);
+
+    // TODO(edisonn): miter, ...
+    if (stroking) {
+        paint->setStrokeWidth(SkDoubleToScalar(fLineWidth));
+        // TODO(edisonn): perf, two sets of allocs, create SkDashPathEffect constr that takes ownership
+        // of the intervals
+        if (fDashArrayLength > 0 && fDashPhase > 0) {
+            paint->setPathEffect(new SkDashPathEffect(fDashArray, fDashArrayLength, fDashPhase))->unref();
+        }
+    }
+}
diff --git a/experimental/PdfViewer/SkPdfBasics.h b/experimental/PdfViewer/SkPdfBasics.h
index 31fc2cd..752a714 100644
--- a/experimental/PdfViewer/SkPdfBasics.h
+++ b/experimental/PdfViewer/SkPdfBasics.h
@@ -20,8 +20,28 @@
 
 // TODO(edisonn): better class design.
 struct SkPdfColorOperator {
+
+    /*
+    color space   name or array     The current color space in which color values are to be interpreted
+                                    (see Section 4.5, “Color Spaces”). There are two separate color space
+                                    parameters: one for stroking and one for all other painting opera-
+                                    tions. Initial value: DeviceGray.
+     */
+
+    // TODO(edisonn): implement the array part too
     // does not own the char*
     NotOwnedString fColorSpace;
+
+
+    /*
+    color         (various)         The current color to be used during painting operations (see Section
+                                    4.5, “Color Spaces”). The type and interpretation of this parameter
+                                    depend on the current color space; for most color spaces, a color
+                                    value consists of one to four numbers. There are two separate color
+                                    parameters: one for stroking and one for all other painting opera-
+                                    tions. Initial value: black.
+     */
+
     SkColor fColor;
     double fOpacity;  // ca or CA
     // TODO(edisonn): add here other color space options.
@@ -42,10 +62,7 @@
 
 // TODO(edisonn): better class design.
 struct SkPdfGraphicsState {
-    SkMatrix            fMatrix;
-    SkMatrix            fMatrixTm;
-    SkMatrix            fMatrixTlm;
-
+    // TODO(edisonn): deprecate and remove these!
     double              fCurPosX;
     double              fCurPosY;
 
@@ -55,6 +72,35 @@
     SkPath              fPath;
     bool                fPathClosed;
 
+
+
+    double              fTextLeading;
+    double              fWordSpace;
+    double              fCharSpace;
+
+    SkPdfResourceDictionary* fResources;
+
+
+    // TODO(edisonn): move most of these in canvas/paint?
+    // we could have some in canvas (matrixes?),
+    // some in 2 paints (stroking paint and non stroking paint)
+
+//    TABLE 4.2 Device-independent graphics state parameters
+/*
+ * CTM           array             The current transformation matrix, which maps positions from user
+                                coordinates to device coordinates (see Section 4.2, “Coordinate Sys-
+                                tems”). This matrix is modified by each application of the coordi-
+                                nate transformation operator, cm. Initial value: a matrix that
+                                transforms default user coordinates to device coordinates.
+ */
+    SkMatrix fCTM;
+
+/*
+clipping path (internal)        The current clipping path, which defines the boundary against
+                                which all output is to be cropped (see Section 4.4.3, “Clipping Path
+                                Operators”). Initial value: the boundary of the entire imageable
+                                portion of the output page.
+ */
     // Clip that is applied after the drawing is done!!!
     bool                fHasClipPathToApply;
     SkPath              fClipPath;
@@ -62,21 +108,208 @@
     SkPdfColorOperator  fStroking;
     SkPdfColorOperator  fNonStroking;
 
+/*
+text state    (various)         A set of nine graphics state parameters that pertain only to the
+                                painting of text. These include parameters that select the font, scale
+                                the glyphs to an appropriate size, and accomplish other effects. The
+                                text state parameters are described in Section 5.2, “Text State
+                                Parameters and Operators.”
+ */
+
+    // TODO(edisonn): add SkPdfTextState class. remove these two existing fields
+    SkMatrix            fMatrixTm;
+    SkMatrix            fMatrixTlm;
+
+
+/*
+line width    number            The thickness, in user space units, of paths to be stroked (see “Line
+                                Width” on page 152). Initial value: 1.0.
+ */
     double              fLineWidth;
-    double              fTextLeading;
-    double              fWordSpace;
-    double              fCharSpace;
 
-    SkPdfResourceDictionary* fResources;
 
+/*
+line cap      integer           A code specifying the shape of the endpoints for any open path that
+                                is stroked (see “Line Cap Style” on page 153). Initial value: 0, for
+                                square butt caps.
+ */
+    // TODO (edisonn): implement defaults - page 153
+    int fLineCap;
+
+/*
+line join     integer           A code specifying the shape of joints between connected segments
+                                of a stroked path (see “Line Join Style” on page 153). Initial value: 0,
+                                for mitered joins.
+ */
+    // TODO (edisonn): implement defaults - page 153
+    int fLineJoin;
+
+/*
+miter limit   number            The maximum length of mitered line joins for stroked paths (see
+                                “Miter Limit” on page 153). This parameter limits the length of
+                                “spikes” produced when line segments join at sharp angles. Initial
+                                value: 10.0, for a miter cutoff below approximately 11.5 degrees.
+ */
+    // TODO (edisonn): implement defaults - page 153
+    double fMiterLimit;
+
+/*
+dash pattern      array and     A description of the dash pattern to be used when paths are
+                  number        stroked (see “Line Dash Pattern” on page 155). Initial value: a solid
+                                line.
+ */
+    SkScalar fDashArray[256]; // TODO(edisonn): allocate array?
+    int fDashArrayLength;
+    SkScalar fDashPhase;
+
+
+/*
+rendering intent  name          The rendering intent to be used when converting CIE-based colors
+                                to device colors (see “Rendering Intents” on page 197). Default
+                                value: RelativeColorimetric.
+ */
+    // TODO(edisonn): seems paper only. Verify.
+
+/*
+stroke adjustment boolean       (PDF 1.2) A flag specifying whether to compensate for possible ras-
+                                terization effects when stroking a path with a line width that is
+                                small relative to the pixel resolution of the output device (see Sec-
+                                tion 6.5.4, “Automatic Stroke Adjustment”). Note that this is con-
+                                sidered a device-independent parameter, even though the details of
+                                its effects are device-dependent. Initial value: false.
+ */
+    // TODO(edisonn): stroke adjustment low priority.
+
+
+/*
+blend mode        name or array (PDF 1.4) The current blend mode to be used in the transparent
+                                imaging model (see Sections 7.2.4, “Blend Mode,” and 7.5.2, “Spec-
+                                ifying Blending Color Space and Blend Mode”). This parameter is
+                                implicitly reset to its initial value at the beginning of execution of a
+                                transparency group XObject (see Section 7.5.5, “Transparency
+                                Group XObjects”). Initial value: Normal.
+ */
+    SkXfermode::Mode fBlendMode;
+
+/*
+soft mask         dictionary    (PDF 1.4) A soft-mask dictionary (see “Soft-Mask Dictionaries” on
+                  or name       page 445) specifying the mask shape or mask opacity values to be
+                                used in the transparent imaging model (see “Source Shape and
+                                Opacity” on page 421 and “Mask Shape and Opacity” on page 443),
+                                or the name None if no such mask is specified. This parameter is
+                                implicitly reset to its initial value at the beginning of execution of a
+                                transparency group XObject (see Section 7.5.5, “Transparency
+                                Group XObjects”). Initial value: None.
+ */
     SkBitmap            fSMask;
 
+
+/*
+alpha constant    number        (PDF 1.4) The constant shape or constant opacity value to be used
+                                in the transparent imaging model (see “Source Shape and Opacity”
+                                on page 421 and “Constant Shape and Opacity” on page 444).
+                                There are two separate alpha constant parameters: one for stroking
+                                and one for all other painting operations. This parameter is implic-
+                                itly reset to its initial value at the beginning of execution of a trans-
+                                parency group XObject (see Section 7.5.5, “Transparency Group
+                                XObjects”). Initial value: 1.0.
+ */
+    double fAphaConstant;
+
+/*
+alpha source      boolean       (PDF 1.4) A flag specifying whether the current soft mask and alpha
+                                constant parameters are to be interpreted as shape values (true) or
+                                opacity values (false). This flag also governs the interpretation of
+                                the SMask entry, if any, in an image dictionary (see Section 4.8.4,
+                                “Image Dictionaries”). Initial value: false.
+ */
+    bool fAlphaSource;
+
+
+// TODO(edisonn): Device-dependent seem to be required only on the actual physical printer?
+//                       TABLE 4.3 Device-dependent graphics state parameters
+/*
+overprint          boolean            (PDF 1.2) A flag specifying (on output devices that support the
+                                      overprint control feature) whether painting in one set of colorants
+                                      should cause the corresponding areas of other colorants to be
+                                      erased (false) or left unchanged (true); see Section 4.5.6, “Over-
+                                      print Control.” In PDF 1.3, there are two separate overprint param-
+                                      eters: one for stroking and one for all other painting operations.
+                                      Initial value: false.
+ */
+
+
+/*
+overprint mode     number             (PDF 1.3) A code specifying whether a color component value of 0
+                                      in a DeviceCMYK color space should erase that component (0) or
+                                      leave it unchanged (1) when overprinting (see Section 4.5.6, “Over-
+                                      print Control”). Initial value: 0.
+ */
+
+
+/*
+black generation   function           (PDF 1.2) A function that calculates the level of the black color
+                   or name            component to use when converting RGB colors to CMYK (see Sec-
+                                      tion 6.2.3, “Conversion from DeviceRGB to DeviceCMYK”). Initial
+                                      value: installation-dependent.
+ */
+
+
+/*
+undercolor removal function           (PDF 1.2) A function that calculates the reduction in the levels of
+                   or name            the cyan, magenta, and yellow color components to compensate for
+                                      the amount of black added by black generation (see Section 6.2.3,
+                                      “Conversion from DeviceRGB to DeviceCMYK”). Initial value: in-
+                                      stallation-dependent.
+ */
+
+
+/*
+transfer           function,          (PDF 1.2) A function that adjusts device gray or color component
+                   array, or name     levels to compensate for nonlinear response in a particular out-
+                                      put device (see Section 6.3, “Transfer Functions”). Initial value:
+                                      installation-dependent.
+ */
+
+
+/*
+halftone           dictionary,        (PDF 1.2) A halftone screen for gray and color rendering, specified
+                   stream, or name    as a halftone dictionary or stream (see Section 6.4, “Halftones”).
+                                      Initial value: installation-dependent.
+ */
+
+
+/*
+flatness            number             The precision with which curves are to be rendered on the output
+                                      device (see Section 6.5.1, “Flatness Tolerance”). The value of this
+                                      parameter gives the maximum error tolerance, measured in output
+                                      device pixels; smaller numbers give smoother curves at the expense
+                                      of more computation and memory use. Initial value: 1.0.
+ */
+
+
+/*
+smoothness             number             (PDF 1.3) The precision with which color gradients are to be ren-
+                                          dered on the output device (see Section 6.5.2, “Smoothness Toler-
+                                          ance”). The value of this parameter gives the maximum error
+                                          tolerance, expressed as a fraction of the range of each color compo-
+                                          nent; smaller numbers give smoother color transitions at the
+                                          expense of more computation and memory use. Initial value:
+                                          installation-dependent.
+ */
+
+
+
+
+
+
+
     SkPdfGraphicsState() {
         fCurPosX      = 0.0;
         fCurPosY      = 0.0;
         fCurFontSize  = 0.0;
         fTextBlock    = false;
-        fMatrix       = SkMatrix::I();
+        fCTM          = SkMatrix::I();
         fMatrixTm     = SkMatrix::I();
         fMatrixTlm    = SkMatrix::I();
         fPathClosed   = true;
@@ -87,22 +320,18 @@
         fHasClipPathToApply = false;
         fResources    = NULL;
         fSkFont       = NULL;
+        fLineCap      = 0;
+        fLineJoin     = 0;
+        fMiterLimit   = 10.0;
+        fAphaConstant = 1.0;
+        fAlphaSource  = false;
+        fDashArrayLength = 0;
+        fDashPhase    = 0;
+        fBlendMode    = SkXfermode::kSrc_Mode;  // PDF: Normal Blend mode
     }
 
-    void applyGraphicsState(SkPaint* paint, bool stroking) {
-        if (stroking) {
-            fStroking.applyGraphicsState(paint);
-        } else {
-            fNonStroking.applyGraphicsState(paint);
-        }
-
-        // TODO(edisonn): get this from pdfContext->options,
-        // or pdfContext->addPaintOptions(&paint);
-        paint->setAntiAlias(true);
-
-        // TODO(edisonn): dashing, miter, ...
-        paint->setStrokeWidth(SkDoubleToScalar(fLineWidth));
-    }
+    // TODO(edisonn): make two functons instead, stroking and non stoking, avoid branching
+    void applyGraphicsState(SkPaint* paint, bool stroking);
 };
 
 // TODO(edisonn): better class design.
diff --git a/experimental/PdfViewer/SkPdfRenderer.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp
index c51c239..a36d363 100644
--- a/experimental/PdfViewer/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/SkPdfRenderer.cpp
@@ -598,7 +598,7 @@
     SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
 
     canvas->save();
-    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
+    canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
 
 #if 1
     SkScalar z = SkIntToScalar(0);
@@ -608,7 +608,7 @@
     SkPoint to[4] = {SkPoint::Make(z, one), SkPoint::Make(one, one), SkPoint::Make(one, z), SkPoint::Make(z, z)};
     SkMatrix flip;
     SkAssertResult(flip.setPolyToPoly(from, to, 4));
-    SkMatrix solveImageFlip = pdfContext->fGraphicsState.fMatrix;
+    SkMatrix solveImageFlip = pdfContext->fGraphicsState.fCTM;
     solveImageFlip.preConcat(flip);
     canvas->setMatrix(solveImageFlip);
 #endif
@@ -648,18 +648,18 @@
         pdfContext->fGraphicsState.fResources = skobj->Resources(pdfContext->fPdfDoc);
     }
 
-    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Current matrix");
+    SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Current matrix");
 
     if (skobj->has_Matrix()) {
-        pdfContext->fGraphicsState.fMatrix.preConcat(skobj->Matrix(pdfContext->fPdfDoc));
-        pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
-        pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
+        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.fMatrix, "Total matrix");
+    SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
 
-    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
+    canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
 
     if (skobj->has_BBox()) {
         canvas->clipRect(skobj->BBox(pdfContext->fPdfDoc), SkRegion::kIntersect_Op, true);  // TODO(edisonn): AA from settings.
@@ -700,15 +700,15 @@
     pdfContext->fGraphicsState.fMatrixTm.preConcat(matrix);
     pdfContext->fGraphicsState.fMatrixTm.preScale(SkDoubleToScalar(textSize), SkDoubleToScalar(textSize));
 
-    pdfContext->fGraphicsState.fMatrix = pdfContext->fGraphicsState.fMatrixTm;
-    pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
+    pdfContext->fGraphicsState.fCTM = pdfContext->fGraphicsState.fMatrixTm;
+    pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
 
-    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "Total matrix");
+    SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "Total matrix");
 
-    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
+    canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
 
     SkRect rm = bBox;
-    pdfContext->fGraphicsState.fMatrix.mapRect(&rm);
+    pdfContext->fGraphicsState.fCTM.mapRect(&rm);
 
     SkTraceRect(rm, "bbox mapped");
 
@@ -810,7 +810,7 @@
     // tx ty
     SkMatrix matrix = SkMatrixFromPdfMatrix(array);
 
-    pdfContext->fGraphicsState.fMatrix.preConcat(matrix);
+    pdfContext->fGraphicsState.fCTM.preConcat(matrix);
 
 #ifdef PDF_TRACE
     printf("cm ");
@@ -818,7 +818,7 @@
         printf("%f ", array[i]);
     }
     printf("\n");
-    SkTraceMatrix(pdfContext->fGraphicsState.fMatrix, "cm");
+    SkTraceMatrix(pdfContext->fGraphicsState.fCTM, "cm");
 #endif
 
     return kOK_PdfResult;
@@ -889,7 +889,7 @@
     array[5] = f;
 
     SkMatrix matrix = SkMatrixFromPdfMatrix(array);
-    matrix.postConcat(pdfContext->fGraphicsState.fMatrix);
+    matrix.postConcat(pdfContext->fGraphicsState.fCTM);
 
     // TODO(edisonn): Text positioning.
     pdfContext->fGraphicsState.fMatrixTm = matrix;
@@ -1044,7 +1044,7 @@
         path.close();
     }
 
-    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
+    canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
 
     SkPaint paint;
 
@@ -1131,7 +1131,7 @@
 }
 
 static PdfResult PdfOp_n(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
-    canvas->setMatrix(pdfContext->fGraphicsState.fMatrix);
+    canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
     if (pdfContext->fGraphicsState.fHasClipPathToApply) {
 #ifndef PDF_DEBUG_NO_CLIPING
         canvas->clipPath(pdfContext->fGraphicsState.fClipPath, SkRegion::kIntersect_Op, true);
@@ -1148,8 +1148,8 @@
 
 static PdfResult PdfOp_BT(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
     pdfContext->fGraphicsState.fTextBlock   = true;
-    pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fMatrix;
-    pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fMatrix;
+    pdfContext->fGraphicsState.fMatrixTm = pdfContext->fGraphicsState.fCTM;
+    pdfContext->fGraphicsState.fMatrixTlm = pdfContext->fGraphicsState.fCTM;
 
     return kPartial_PdfResult;
 }
@@ -1162,15 +1162,7 @@
     return kPartial_PdfResult;
 }
 
-//font size Tf Set the text font, Tf
-//, to font and the text font size, Tfs, to size. font is the name of a
-//font resource in the Fontsubdictionary of the current resource dictionary; size is
-//a number representing a scale factor. There is no initial value for either font or
-//size; they must be specified explicitly using Tf before any text is shown.
-static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
-    pdfContext->fGraphicsState.fCurFontSize = pdfContext->fObjectStack.top()->numberValue();     pdfContext->fObjectStack.pop();
-    SkPdfObject* fontName = pdfContext->fObjectStack.top();                           pdfContext->fObjectStack.pop();
-
+PdfResult skpdfGraphicsStateApplyFontCore(PdfContext* pdfContext, const SkPdfObject* fontName, double fontSize) {
 #ifdef PDF_TRACE
     printf("font name: %s\n", fontName->nameValue2().c_str());
 #endif
@@ -1190,9 +1182,21 @@
             pdfContext->fGraphicsState.fSkFont = skfont;
         }
     }
+    pdfContext->fGraphicsState.fCurFontSize = fontSize;
     return kIgnoreError_PdfResult;
 }
 
+//font size Tf Set the text font, Tf
+//, to font and the text font size, Tfs, to size. font is the name of a
+//font resource in the Fontsubdictionary of the current resource dictionary; size is
+//a number representing a scale factor. There is no initial value for either font or
+//size; they must be specified explicitly using Tf before any text is shown.
+static PdfResult PdfOp_Tf(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
+    double fontSize = pdfContext->fObjectStack.top()->numberValue();     pdfContext->fObjectStack.pop();
+    SkPdfObject* fontName = pdfContext->fObjectStack.top();                           pdfContext->fObjectStack.pop();
+    return skpdfGraphicsStateApplyFontCore(pdfContext, fontName, fontSize);
+}
+
 static PdfResult PdfOp_Tj(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
     if (!pdfContext->fGraphicsState.fTextBlock) {
         // TODO(edisonn): try to recover and draw it any way?
@@ -1519,6 +1523,89 @@
     return kNYI_PdfResult;
 }
 
+
+// TODO(edisonn): security review here, make sure all parameters are valid, and safe.
+void skpdfGraphicsStateApply_ca(PdfContext* pdfContext, double ca) {
+    pdfContext->fGraphicsState.fNonStroking.fOpacity = ca;
+}
+
+void skpdfGraphicsStateApply_CA(PdfContext* pdfContext, double CA) {
+    pdfContext->fGraphicsState.fStroking.fOpacity = CA;
+}
+
+void skpdfGraphicsStateApplyLW(PdfContext* pdfContext, double lineWidth) {
+    pdfContext->fGraphicsState.fLineWidth = lineWidth;
+}
+
+void skpdfGraphicsStateApplyLC(PdfContext* pdfContext, int64_t lineCap) {
+    pdfContext->fGraphicsState.fLineCap = (int)lineCap;
+}
+
+void skpdfGraphicsStateApplyLJ(PdfContext* pdfContext, int64_t lineJoin) {
+    pdfContext->fGraphicsState.fLineJoin = (int)lineJoin;
+}
+
+void skpdfGraphicsStateApplyML(PdfContext* pdfContext, double miterLimit) {
+    pdfContext->fGraphicsState.fMiterLimit = miterLimit;
+}
+
+void skpdfGraphicsStateApplyD(PdfContext* pdfContext, SkPdfArray* dash) {
+    // TODO(edisonn): verify input
+    if (!dash || dash->isArray() || dash->size() != 2 || !dash->objAtAIndex(0)->isArray() || !dash->objAtAIndex(1)->isNumber()) {
+        // TODO(edisonn): report error/warning
+        return;
+    }
+
+    SkPdfArray* intervals = (SkPdfArray*)dash->objAtAIndex(0);
+    int cnt = intervals->size();
+    if (cnt >= 256) {
+        // TODO(edisonn): report error/warning, unsuported;
+        // TODO(edisonn): alloc memory
+        return;
+    }
+    for (int i = 0; i < cnt; i++) {
+        if (!intervals->objAtAIndex(i)->isNumber()) {
+            // TODO(edisonn): report error/warning
+            return;
+        }
+    }
+
+    pdfContext->fGraphicsState.fDashPhase = dash->objAtAIndex(1)->scalarValue();
+    pdfContext->fGraphicsState.fDashArrayLength = cnt;
+    for (int i = 0 ; i < cnt; i++) {
+        pdfContext->fGraphicsState.fDashArray[i] = intervals->objAtAIndex(i)->scalarValue();
+    }
+}
+
+void skpdfGraphicsStateApplyFont(PdfContext* pdfContext, SkPdfArray* fontAndSize) {
+    if (!fontAndSize || fontAndSize->isArray() || fontAndSize->size() != 2 || !fontAndSize->objAtAIndex(0)->isName() || !fontAndSize->objAtAIndex(1)->isNumber()) {
+        // TODO(edisonn): report error/warning
+        return;
+    }
+    skpdfGraphicsStateApplyFontCore(pdfContext, fontAndSize->objAtAIndex(0), fontAndSize->objAtAIndex(1)->numberValue());
+}
+
+void skpdfGraphicsStateApplyBM_name(PdfContext* pdfContext, const std::string& blendMode) {
+    // TODO(edisonn): verify input
+}
+
+void skpdfGraphicsStateApplyBM_array(PdfContext* pdfContext, SkPdfArray* blendModes) {
+    // TODO(edisonn): verify input
+}
+
+void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
+    // TODO(edisonn): verify input
+}
+
+void skpdfGraphicsStateApplySMask_dict(PdfContext* pdfContext, SkPdfDictionary* sMask) {
+    // TODO(edisonn): verify input
+}
+
+void skpdfGraphicsStateApplyAIS(PdfContext* pdfContext, bool alphaSource) {
+    pdfContext->fGraphicsState.fAlphaSource = alphaSource;
+}
+
+
 //dictName gs (PDF 1.2) Set the specified parameters in the graphics state. dictName is
 //the name of a graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary (see the next section).
 static PdfResult PdfOp_gs(PdfContext* pdfContext, SkCanvas* canvas, PdfTokenLooper** looper) {
@@ -1550,16 +1637,60 @@
         return kIgnoreError_PdfResult;
     }
 
-    if (gs->has_CA()) {
-        pdfContext->fGraphicsState.fStroking.fOpacity = gs->CA(pdfContext->fPdfDoc);
+    if (gs->has_LW()) {
+        skpdfGraphicsStateApplyLW(pdfContext, gs->LW(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_LC()) {
+        skpdfGraphicsStateApplyLC(pdfContext, gs->LC(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_LJ()) {
+        skpdfGraphicsStateApplyLJ(pdfContext, gs->LJ(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_ML()) {
+        skpdfGraphicsStateApplyML(pdfContext, gs->ML(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_D()) {
+        skpdfGraphicsStateApplyD(pdfContext, gs->D(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_Font()) {
+        skpdfGraphicsStateApplyFont(pdfContext, gs->Font(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_BM()) {
+        if (gs->isBMAName(pdfContext->fPdfDoc)) {
+            skpdfGraphicsStateApplyBM_name(pdfContext, gs->getBMAsName(pdfContext->fPdfDoc));
+        } else if (gs->isBMAArray(pdfContext->fPdfDoc)) {
+            skpdfGraphicsStateApplyBM_array(pdfContext, gs->getBMAsArray(pdfContext->fPdfDoc));
+        } else {
+            // TODO(edisonn): report/warn
+        }
+    }
+
+    if (gs->has_SMask()) {
+        if (gs->isSMaskAName(pdfContext->fPdfDoc)) {
+            skpdfGraphicsStateApplySMask_name(pdfContext, gs->getSMaskAsName(pdfContext->fPdfDoc));
+        } else if (gs->isSMaskADictionary(pdfContext->fPdfDoc)) {
+            skpdfGraphicsStateApplySMask_dict(pdfContext, gs->getSMaskAsDictionary(pdfContext->fPdfDoc));
+        } else {
+            // TODO(edisonn): report/warn
+        }
     }
 
     if (gs->has_ca()) {
-        pdfContext->fGraphicsState.fNonStroking.fOpacity = gs->ca(pdfContext->fPdfDoc);
+        skpdfGraphicsStateApply_ca(pdfContext, gs->ca(pdfContext->fPdfDoc));
     }
 
-    if (gs->has_LW()) {
-        pdfContext->fGraphicsState.fLineWidth = gs->LW(pdfContext->fPdfDoc);
+    if (gs->has_CA()) {
+        skpdfGraphicsStateApply_CA(pdfContext, gs->CA(pdfContext->fPdfDoc));
+    }
+
+    if (gs->has_AIS()) {
+        skpdfGraphicsStateApplyAIS(pdfContext, gs->AIS(pdfContext->fPdfDoc));
     }
 
     return kNYI_PdfResult;
@@ -1993,9 +2124,9 @@
     SkTraceMatrix(pdfContext.fOriginalMatrix, "Original matrix");
 
 
-    pdfContext.fGraphicsState.fMatrix = pdfContext.fOriginalMatrix;
-    pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fMatrix;
-    pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fMatrix;
+    pdfContext.fGraphicsState.fCTM = pdfContext.fOriginalMatrix;
+    pdfContext.fGraphicsState.fMatrixTm = pdfContext.fGraphicsState.fCTM;
+    pdfContext.fGraphicsState.fMatrixTlm = pdfContext.fGraphicsState.fCTM;
 
 #ifndef PDF_DEBUG_NO_PAGE_CLIPING
     canvas->clipRect(dst, SkRegion::kIntersect_Op, true);
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
index c670ac8..5525f7b 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
@@ -607,6 +607,17 @@
         return fObjectType == kReal_PdfObjectType ? fRealValue : fIntegerValue;
     }
 
+    inline SkScalar scalarValue() const {
+        SkASSERT(isNumber());
+
+        if (!isNumber()) {
+            // TODO(edisonn): log err
+            return SkIntToScalar(0);
+        }
+        return fObjectType == kReal_PdfObjectType ? SkDoubleToScalar(fRealValue) :
+                                                    SkIntToScalar(fIntegerValue);
+    }
+
     int referenceId() const {
         SkASSERT(fObjectType == kReference_PdfObjectType);
         return fRef.fId;