gfx/skia/trunk/src/pdf/SkPDFDevice.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /*
     2  * Copyright 2011 Google Inc.
     3  *
     4  * Use of this source code is governed by a BSD-style license that can be
     5  * found in the LICENSE file.
     6  */
     8 #include "SkPDFDevice.h"
    10 #include "SkAnnotation.h"
    11 #include "SkColor.h"
    12 #include "SkClipStack.h"
    13 #include "SkData.h"
    14 #include "SkDraw.h"
    15 #include "SkFontHost.h"
    16 #include "SkGlyphCache.h"
    17 #include "SkPaint.h"
    18 #include "SkPath.h"
    19 #include "SkPathOps.h"
    20 #include "SkPDFFont.h"
    21 #include "SkPDFFormXObject.h"
    22 #include "SkPDFGraphicState.h"
    23 #include "SkPDFImage.h"
    24 #include "SkPDFResourceDict.h"
    25 #include "SkPDFShader.h"
    26 #include "SkPDFStream.h"
    27 #include "SkPDFTypes.h"
    28 #include "SkPDFUtils.h"
    29 #include "SkRect.h"
    30 #include "SkRRect.h"
    31 #include "SkString.h"
    32 #include "SkTextFormatParams.h"
    33 #include "SkTemplates.h"
    34 #include "SkTypefacePriv.h"
    35 #include "SkTSet.h"
    37 #ifdef SK_BUILD_FOR_ANDROID
    38 #include "SkTypeface_android.h"
    40 struct TypefaceFallbackData {
    41     SkTypeface* typeface;
    42     int lowerBounds;
    43     int upperBounds;
    45     bool operator==(const TypefaceFallbackData& b) const {
    46         return typeface == b.typeface &&
    47                lowerBounds == b.lowerBounds &&
    48                upperBounds == b.upperBounds;
    49     }
    50 };
    51 #endif
    53 #define DPI_FOR_RASTER_SCALE_ONE 72
    55 // Utility functions
    57 static void emit_pdf_color(SkColor color, SkWStream* result) {
    58     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
    59     SkScalar colorMax = SkIntToScalar(0xFF);
    60     SkPDFScalar::Append(
    61             SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
    62     result->writeText(" ");
    63     SkPDFScalar::Append(
    64             SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
    65     result->writeText(" ");
    66     SkPDFScalar::Append(
    67             SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
    68     result->writeText(" ");
    69 }
    71 static SkPaint calculate_text_paint(const SkPaint& paint) {
    72     SkPaint result = paint;
    73     if (result.isFakeBoldText()) {
    74         SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
    75                                                     kStdFakeBoldInterpKeys,
    76                                                     kStdFakeBoldInterpValues,
    77                                                     kStdFakeBoldInterpLength);
    78         SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
    79         if (result.getStyle() == SkPaint::kFill_Style) {
    80             result.setStyle(SkPaint::kStrokeAndFill_Style);
    81         } else {
    82             width += result.getStrokeWidth();
    83         }
    84         result.setStrokeWidth(width);
    85     }
    86     return result;
    87 }
    89 // Stolen from measure_text in SkDraw.cpp and then tweaked.
    90 static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
    91                        const uint16_t* glyphs, size_t len,
    92                        SkScalar* x, SkScalar* y) {
    93     if (paint.getTextAlign() == SkPaint::kLeft_Align) {
    94         return;
    95     }
    97     SkMatrix ident;
    98     ident.reset();
    99     SkAutoGlyphCache autoCache(paint, NULL, &ident);
   100     SkGlyphCache* cache = autoCache.getCache();
   102     const char* start = reinterpret_cast<const char*>(glyphs);
   103     const char* stop = reinterpret_cast<const char*>(glyphs + len);
   104     SkFixed xAdv = 0, yAdv = 0;
   106     // TODO(vandebo): This probably needs to take kerning into account.
   107     while (start < stop) {
   108         const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
   109         xAdv += glyph.fAdvanceX;
   110         yAdv += glyph.fAdvanceY;
   111     };
   112     if (paint.getTextAlign() == SkPaint::kLeft_Align) {
   113         return;
   114     }
   116     SkScalar xAdj = SkFixedToScalar(xAdv);
   117     SkScalar yAdj = SkFixedToScalar(yAdv);
   118     if (paint.getTextAlign() == SkPaint::kCenter_Align) {
   119         xAdj = SkScalarHalf(xAdj);
   120         yAdj = SkScalarHalf(yAdj);
   121     }
   122     *x = *x - xAdj;
   123     *y = *y - yAdj;
   124 }
   126 static int max_glyphid_for_typeface(SkTypeface* typeface) {
   127     SkAutoResolveDefaultTypeface autoResolve(typeface);
   128     typeface = autoResolve.get();
   129     return typeface->countGlyphs() - 1;
   130 }
   132 typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage;
   134 static size_t force_glyph_encoding(const SkPaint& paint, const void* text,
   135                                    size_t len, SkGlyphStorage* storage,
   136                                    uint16_t** glyphIDs) {
   137     // Make sure we have a glyph id encoding.
   138     if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
   139         size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
   140         storage->reset(numGlyphs);
   141         paint.textToGlyphs(text, len, storage->get());
   142         *glyphIDs = storage->get();
   143         return numGlyphs;
   144     }
   146     // For user supplied glyph ids we need to validate them.
   147     SkASSERT((len & 1) == 0);
   148     size_t numGlyphs = len / 2;
   149     const uint16_t* input =
   150         reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
   152     int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface());
   153     size_t validated;
   154     for (validated = 0; validated < numGlyphs; ++validated) {
   155         if (input[validated] > maxGlyphID) {
   156             break;
   157         }
   158     }
   159     if (validated >= numGlyphs) {
   160         *glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
   161         return numGlyphs;
   162     }
   164     // Silently drop anything out of range.
   165     storage->reset(numGlyphs);
   166     if (validated > 0) {
   167         memcpy(storage->get(), input, validated * sizeof(uint16_t));
   168     }
   170     for (size_t i = validated; i < numGlyphs; ++i) {
   171         storage->get()[i] = input[i];
   172         if (input[i] > maxGlyphID) {
   173             storage->get()[i] = 0;
   174         }
   175     }
   176     *glyphIDs = storage->get();
   177     return numGlyphs;
   178 }
   180 static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
   181                                SkWStream* content) {
   182     // Flip the text about the x-axis to account for origin swap and include
   183     // the passed parameters.
   184     content->writeText("1 0 ");
   185     SkPDFScalar::Append(0 - textSkewX, content);
   186     content->writeText(" -1 ");
   187     SkPDFScalar::Append(x, content);
   188     content->writeText(" ");
   189     SkPDFScalar::Append(y, content);
   190     content->writeText(" Tm\n");
   191 }
   193 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
   194 // later being our representation of an object in the PDF file.
   195 struct GraphicStateEntry {
   196     GraphicStateEntry();
   198     // Compare the fields we care about when setting up a new content entry.
   199     bool compareInitialState(const GraphicStateEntry& b);
   201     SkMatrix fMatrix;
   202     // We can't do set operations on Paths, though PDF natively supports
   203     // intersect.  If the clip stack does anything other than intersect,
   204     // we have to fall back to the region.  Treat fClipStack as authoritative.
   205     // See http://code.google.com/p/skia/issues/detail?id=221
   206     SkClipStack fClipStack;
   207     SkRegion fClipRegion;
   209     // When emitting the content entry, we will ensure the graphic state
   210     // is set to these values first.
   211     SkColor fColor;
   212     SkScalar fTextScaleX;  // Zero means we don't care what the value is.
   213     SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero.
   214     int fShaderIndex;
   215     int fGraphicStateIndex;
   217     // We may change the font (i.e. for Type1 support) within a
   218     // ContentEntry.  This is the one currently in effect, or NULL if none.
   219     SkPDFFont* fFont;
   220     // In PDF, text size has no default value. It is only valid if fFont is
   221     // not NULL.
   222     SkScalar fTextSize;
   223 };
   225 GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
   226                                          fTextScaleX(SK_Scalar1),
   227                                          fTextFill(SkPaint::kFill_Style),
   228                                          fShaderIndex(-1),
   229                                          fGraphicStateIndex(-1),
   230                                          fFont(NULL),
   231                                          fTextSize(SK_ScalarNaN) {
   232     fMatrix.reset();
   233 }
   235 bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) {
   236     return fColor == cur.fColor &&
   237            fShaderIndex == cur.fShaderIndex &&
   238            fGraphicStateIndex == cur.fGraphicStateIndex &&
   239            fMatrix == cur.fMatrix &&
   240            fClipStack == cur.fClipStack &&
   241            (fTextScaleX == 0 ||
   242                (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill));
   243 }
   245 class GraphicStackState {
   246 public:
   247     GraphicStackState(const SkClipStack& existingClipStack,
   248                       const SkRegion& existingClipRegion,
   249                       SkWStream* contentStream)
   250             : fStackDepth(0),
   251               fContentStream(contentStream) {
   252         fEntries[0].fClipStack = existingClipStack;
   253         fEntries[0].fClipRegion = existingClipRegion;
   254     }
   256     void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
   257                     const SkPoint& translation);
   258     void updateMatrix(const SkMatrix& matrix);
   259     void updateDrawingState(const GraphicStateEntry& state);
   261     void drainStack();
   263 private:
   264     void push();
   265     void pop();
   266     GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
   268     // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
   269     static const int kMaxStackDepth = 12;
   270     GraphicStateEntry fEntries[kMaxStackDepth + 1];
   271     int fStackDepth;
   272     SkWStream* fContentStream;
   273 };
   275 void GraphicStackState::drainStack() {
   276     while (fStackDepth) {
   277         pop();
   278     }
   279 }
   281 void GraphicStackState::push() {
   282     SkASSERT(fStackDepth < kMaxStackDepth);
   283     fContentStream->writeText("q\n");
   284     fStackDepth++;
   285     fEntries[fStackDepth] = fEntries[fStackDepth - 1];
   286 }
   288 void GraphicStackState::pop() {
   289     SkASSERT(fStackDepth > 0);
   290     fContentStream->writeText("Q\n");
   291     fStackDepth--;
   292 }
   294 // This function initializes iter to be an iterator on the "stack" argument
   295 // and then skips over the leading entries as specified in prefix.  It requires
   296 // and asserts that "prefix" will be a prefix to "stack."
   297 static void skip_clip_stack_prefix(const SkClipStack& prefix,
   298                                    const SkClipStack& stack,
   299                                    SkClipStack::Iter* iter) {
   300     SkClipStack::B2TIter prefixIter(prefix);
   301     iter->reset(stack, SkClipStack::Iter::kBottom_IterStart);
   303     const SkClipStack::Element* prefixEntry;
   304     const SkClipStack::Element* iterEntry;
   306     for (prefixEntry = prefixIter.next(); prefixEntry;
   307             prefixEntry = prefixIter.next()) {
   308         iterEntry = iter->next();
   309         SkASSERT(iterEntry);
   310         // Because of SkClipStack does internal intersection, the last clip
   311         // entry may differ.
   312         if (*prefixEntry != *iterEntry) {
   313             SkASSERT(prefixEntry->getOp() == SkRegion::kIntersect_Op);
   314             SkASSERT(iterEntry->getOp() == SkRegion::kIntersect_Op);
   315             SkASSERT(iterEntry->getType() == prefixEntry->getType());
   316             // back up the iterator by one
   317             iter->prev();
   318             prefixEntry = prefixIter.next();
   319             break;
   320         }
   321     }
   323     SkASSERT(prefixEntry == NULL);
   324 }
   326 static void emit_clip(SkPath* clipPath, SkRect* clipRect,
   327                       SkWStream* contentStream) {
   328     SkASSERT(clipPath || clipRect);
   330     SkPath::FillType clipFill;
   331     if (clipPath) {
   332         SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream);
   333         clipFill = clipPath->getFillType();
   334     } else {
   335         SkPDFUtils::AppendRectangle(*clipRect, contentStream);
   336         clipFill = SkPath::kWinding_FillType;
   337     }
   339     NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
   340     NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
   341     if (clipFill == SkPath::kEvenOdd_FillType) {
   342         contentStream->writeText("W* n\n");
   343     } else {
   344         contentStream->writeText("W n\n");
   345     }
   346 }
   348 #ifdef SK_PDF_USE_PATHOPS
   349 /* Calculate an inverted path's equivalent non-inverted path, given the
   350  * canvas bounds.
   351  * outPath may alias with invPath (since this is supported by PathOps).
   352  */
   353 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
   354                                    SkPath* outPath) {
   355     SkASSERT(invPath.isInverseFillType());
   357     SkPath clipPath;
   358     clipPath.addRect(bounds);
   360     return Op(clipPath, invPath, kIntersect_PathOp, outPath);
   361 }
   363 // Sanity check the numerical values of the SkRegion ops and PathOps ops
   364 // enums so region_op_to_pathops_op can do a straight passthrough cast.
   365 // If these are failing, it may be necessary to make region_op_to_pathops_op
   366 // do more.
   367 SK_COMPILE_ASSERT(SkRegion::kDifference_Op == (int)kDifference_PathOp,
   368                   region_pathop_mismatch);
   369 SK_COMPILE_ASSERT(SkRegion::kIntersect_Op == (int)kIntersect_PathOp,
   370                   region_pathop_mismatch);
   371 SK_COMPILE_ASSERT(SkRegion::kUnion_Op == (int)kUnion_PathOp,
   372                   region_pathop_mismatch);
   373 SK_COMPILE_ASSERT(SkRegion::kXOR_Op == (int)kXOR_PathOp,
   374                   region_pathop_mismatch);
   375 SK_COMPILE_ASSERT(SkRegion::kReverseDifference_Op ==
   376                   (int)kReverseDifference_PathOp,
   377                   region_pathop_mismatch);
   379 static SkPathOp region_op_to_pathops_op(SkRegion::Op op) {
   380     SkASSERT(op >= 0);
   381     SkASSERT(op <= SkRegion::kReverseDifference_Op);
   382     return (SkPathOp)op;
   383 }
   385 /* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
   386  * Returns true if successful, or false if not successful.
   387  * If successful, the resulting clip is stored in outClipPath.
   388  * If not successful, outClipPath is undefined, and a fallback method
   389  * should be used.
   390  */
   391 static bool get_clip_stack_path(const SkMatrix& transform,
   392                                 const SkClipStack& clipStack,
   393                                 const SkRegion& clipRegion,
   394                                 SkPath* outClipPath) {
   395     outClipPath->reset();
   396     outClipPath->setFillType(SkPath::kInverseWinding_FillType);
   398     const SkClipStack::Element* clipEntry;
   399     SkClipStack::Iter iter;
   400     iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
   401     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
   402         SkPath entryPath;
   403         if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
   404             outClipPath->reset();
   405             outClipPath->setFillType(SkPath::kInverseWinding_FillType);
   406             continue;
   407         } else {
   408             clipEntry->asPath(&entryPath);
   409         }
   410         entryPath.transform(transform);
   412         if (SkRegion::kReplace_Op == clipEntry->getOp()) {
   413             *outClipPath = entryPath;
   414         } else {
   415             SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
   416             if (!Op(*outClipPath, entryPath, op, outClipPath)) {
   417                 return false;
   418             }
   419         }
   420     }
   422     if (outClipPath->isInverseFillType()) {
   423         // The bounds are slightly outset to ensure this is correct in the
   424         // face of floating-point accuracy and possible SkRegion bitmap
   425         // approximations.
   426         SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
   427         clipBounds.outset(SK_Scalar1, SK_Scalar1);
   428         if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
   429             return false;
   430         }
   431     }
   432     return true;
   433 }
   434 #endif
   436 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
   437 // graphic state stack, and the fact that we can know all the clips used
   438 // on the page to optimize this.
   439 void GraphicStackState::updateClip(const SkClipStack& clipStack,
   440                                    const SkRegion& clipRegion,
   441                                    const SkPoint& translation) {
   442     if (clipStack == currentEntry()->fClipStack) {
   443         return;
   444     }
   446     while (fStackDepth > 0) {
   447         pop();
   448         if (clipStack == currentEntry()->fClipStack) {
   449             return;
   450         }
   451     }
   452     push();
   454     currentEntry()->fClipStack = clipStack;
   455     currentEntry()->fClipRegion = clipRegion;
   457     SkMatrix transform;
   458     transform.setTranslate(translation.fX, translation.fY);
   460 #ifdef SK_PDF_USE_PATHOPS
   461     SkPath clipPath;
   462     if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) {
   463         emit_clip(&clipPath, NULL, fContentStream);
   464         return;
   465     }
   466 #endif
   467     // gsState->initialEntry()->fClipStack/Region specifies the clip that has
   468     // already been applied.  (If this is a top level device, then it specifies
   469     // a clip to the content area.  If this is a layer, then it specifies
   470     // the clip in effect when the layer was created.)  There's no need to
   471     // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
   472     // initial clip on the parent layer.  (This means there's a bug if the user
   473     // expands the clip and then uses any xfer mode that uses dst:
   474     // http://code.google.com/p/skia/issues/detail?id=228 )
   475     SkClipStack::Iter iter;
   476     skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
   478     // If the clip stack does anything other than intersect or if it uses
   479     // an inverse fill type, we have to fall back to the clip region.
   480     bool needRegion = false;
   481     const SkClipStack::Element* clipEntry;
   482     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
   483         if (clipEntry->getOp() != SkRegion::kIntersect_Op ||
   484                 clipEntry->isInverseFilled()) {
   485             needRegion = true;
   486             break;
   487         }
   488     }
   490     if (needRegion) {
   491         SkPath clipPath;
   492         SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
   493         emit_clip(&clipPath, NULL, fContentStream);
   494     } else {
   495         skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
   496         const SkClipStack::Element* clipEntry;
   497         for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
   498             SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
   499             switch (clipEntry->getType()) {
   500                 case SkClipStack::Element::kRect_Type: {
   501                     SkRect translatedClip;
   502                     transform.mapRect(&translatedClip, clipEntry->getRect());
   503                     emit_clip(NULL, &translatedClip, fContentStream);
   504                     break;
   505                 }
   506                 default: {
   507                     SkPath translatedPath;
   508                     clipEntry->asPath(&translatedPath);
   509                     translatedPath.transform(transform, &translatedPath);
   510                     emit_clip(&translatedPath, NULL, fContentStream);
   511                     break;
   512                 }
   513             }
   514         }
   515     }
   516 }
   518 void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
   519     if (matrix == currentEntry()->fMatrix) {
   520         return;
   521     }
   523     if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
   524         SkASSERT(fStackDepth > 0);
   525         SkASSERT(fEntries[fStackDepth].fClipStack ==
   526                  fEntries[fStackDepth -1].fClipStack);
   527         pop();
   529         SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
   530     }
   531     if (matrix.getType() == SkMatrix::kIdentity_Mask) {
   532         return;
   533     }
   535     push();
   536     SkPDFUtils::AppendTransform(matrix, fContentStream);
   537     currentEntry()->fMatrix = matrix;
   538 }
   540 void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
   541     // PDF treats a shader as a color, so we only set one or the other.
   542     if (state.fShaderIndex >= 0) {
   543         if (state.fShaderIndex != currentEntry()->fShaderIndex) {
   544             SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream);
   545             currentEntry()->fShaderIndex = state.fShaderIndex;
   546         }
   547     } else {
   548         if (state.fColor != currentEntry()->fColor ||
   549                 currentEntry()->fShaderIndex >= 0) {
   550             emit_pdf_color(state.fColor, fContentStream);
   551             fContentStream->writeText("RG ");
   552             emit_pdf_color(state.fColor, fContentStream);
   553             fContentStream->writeText("rg\n");
   554             currentEntry()->fColor = state.fColor;
   555             currentEntry()->fShaderIndex = -1;
   556         }
   557     }
   559     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
   560         SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
   561         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
   562     }
   564     if (state.fTextScaleX) {
   565         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
   566             SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
   567                                             SkIntToScalar(100));
   568             SkPDFScalar::Append(pdfScale, fContentStream);
   569             fContentStream->writeText(" Tz\n");
   570             currentEntry()->fTextScaleX = state.fTextScaleX;
   571         }
   572         if (state.fTextFill != currentEntry()->fTextFill) {
   573             SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
   574             SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
   575                               enum_must_match_value);
   576             SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
   577                               enum_must_match_value);
   578             fContentStream->writeDecAsText(state.fTextFill);
   579             fContentStream->writeText(" Tr\n");
   580             currentEntry()->fTextFill = state.fTextFill;
   581         }
   582     }
   583 }
   585 SkBaseDevice* SkPDFDevice::onCreateDevice(const SkImageInfo& info, Usage usage) {
   586     SkMatrix initialTransform;
   587     initialTransform.reset();
   588     SkISize size = SkISize::Make(info.width(), info.height());
   589     return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
   590 }
   593 struct ContentEntry {
   594     GraphicStateEntry fState;
   595     SkDynamicMemoryWStream fContent;
   596     SkAutoTDelete<ContentEntry> fNext;
   598     // If the stack is too deep we could get Stack Overflow.
   599     // So we manually destruct the object.
   600     ~ContentEntry() {
   601         ContentEntry* val = fNext.detach();
   602         while (val != NULL) {
   603             ContentEntry* valNext = val->fNext.detach();
   604             // When the destructor is called, fNext is NULL and exits.
   605             delete val;
   606             val = valNext;
   607         }
   608     }
   609 };
   611 // A helper class to automatically finish a ContentEntry at the end of a
   612 // drawing method and maintain the state needed between set up and finish.
   613 class ScopedContentEntry {
   614 public:
   615     ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw,
   616                        const SkPaint& paint, bool hasText = false)
   617         : fDevice(device),
   618           fContentEntry(NULL),
   619           fXfermode(SkXfermode::kSrcOver_Mode),
   620           fDstFormXObject(NULL) {
   621         init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
   622     }
   623     ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
   624                        const SkRegion& clipRegion, const SkMatrix& matrix,
   625                        const SkPaint& paint, bool hasText = false)
   626         : fDevice(device),
   627           fContentEntry(NULL),
   628           fXfermode(SkXfermode::kSrcOver_Mode),
   629           fDstFormXObject(NULL) {
   630         init(clipStack, clipRegion, matrix, paint, hasText);
   631     }
   633     ~ScopedContentEntry() {
   634         if (fContentEntry) {
   635             SkPath* shape = &fShape;
   636             if (shape->isEmpty()) {
   637                 shape = NULL;
   638             }
   639             fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape);
   640         }
   641         SkSafeUnref(fDstFormXObject);
   642     }
   644     ContentEntry* entry() { return fContentEntry; }
   646     /* Returns true when we explicitly need the shape of the drawing. */
   647     bool needShape() {
   648         switch (fXfermode) {
   649             case SkXfermode::kClear_Mode:
   650             case SkXfermode::kSrc_Mode:
   651             case SkXfermode::kSrcIn_Mode:
   652             case SkXfermode::kSrcOut_Mode:
   653             case SkXfermode::kDstIn_Mode:
   654             case SkXfermode::kDstOut_Mode:
   655             case SkXfermode::kSrcATop_Mode:
   656             case SkXfermode::kDstATop_Mode:
   657             case SkXfermode::kModulate_Mode:
   658                 return true;
   659             default:
   660                 return false;
   661         }
   662     }
   664     /* Returns true unless we only need the shape of the drawing. */
   665     bool needSource() {
   666         if (fXfermode == SkXfermode::kClear_Mode) {
   667             return false;
   668         }
   669         return true;
   670     }
   672     /* If the shape is different than the alpha component of the content, then
   673      * setShape should be called with the shape.  In particular, images and
   674      * devices have rectangular shape.
   675      */
   676     void setShape(const SkPath& shape) {
   677         fShape = shape;
   678     }
   680 private:
   681     SkPDFDevice* fDevice;
   682     ContentEntry* fContentEntry;
   683     SkXfermode::Mode fXfermode;
   684     SkPDFFormXObject* fDstFormXObject;
   685     SkPath fShape;
   687     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
   688               const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
   689         // Shape has to be flatten before we get here.
   690         if (matrix.hasPerspective()) {
   691             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
   692             return;
   693         }
   694         if (paint.getXfermode()) {
   695             paint.getXfermode()->asMode(&fXfermode);
   696         }
   697         fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
   698                                                    matrix, paint, hasText,
   699                                                    &fDstFormXObject);
   700     }
   701 };
   703 ////////////////////////////////////////////////////////////////////////////////
   705 static inline SkBitmap makeContentBitmap(const SkISize& contentSize,
   706                                          const SkMatrix* initialTransform) {
   707     SkImageInfo info;
   708     if (initialTransform) {
   709         // Compute the size of the drawing area.
   710         SkVector drawingSize;
   711         SkMatrix inverse;
   712         drawingSize.set(SkIntToScalar(contentSize.fWidth),
   713                         SkIntToScalar(contentSize.fHeight));
   714         if (!initialTransform->invert(&inverse)) {
   715             // This shouldn't happen, initial transform should be invertible.
   716             SkASSERT(false);
   717             inverse.reset();
   718         }
   719         inverse.mapVectors(&drawingSize, 1);
   720         SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
   721         info = SkImageInfo::MakeUnknown(abs(size.fWidth), abs(size.fHeight));
   722     } else {
   723         info = SkImageInfo::MakeUnknown(abs(contentSize.fWidth),
   724                                         abs(contentSize.fHeight));
   725     }
   727     SkBitmap bitmap;
   728     bitmap.setConfig(info);
   729     return bitmap;
   730 }
   732 // TODO(vandebo) change pageSize to SkSize.
   733 // TODO: inherit from SkBaseDevice instead of SkBitmapDevice
   734 SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
   735                          const SkMatrix& initialTransform)
   736     : SkBitmapDevice(makeContentBitmap(contentSize, &initialTransform)),
   737       fPageSize(pageSize),
   738       fContentSize(contentSize),
   739       fLastContentEntry(NULL),
   740       fLastMarginContentEntry(NULL),
   741       fClipStack(NULL),
   742       fEncoder(NULL),
   743       fRasterDpi(72.0f) {
   744     // Just report that PDF does not supports perspective in the
   745     // initial transform.
   746     NOT_IMPLEMENTED(initialTransform.hasPerspective(), true);
   748     // Skia generally uses the top left as the origin but PDF natively has the
   749     // origin at the bottom left. This matrix corrects for that.  But that only
   750     // needs to be done once, we don't do it when layering.
   751     fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
   752     fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
   753     fInitialTransform.preConcat(initialTransform);
   755     SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height());
   756     fExistingClipRegion.setRect(existingClip);
   758     this->init();
   759 }
   761 // TODO(vandebo) change layerSize to SkSize.
   762 SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
   763                          const SkClipStack& existingClipStack,
   764                          const SkRegion& existingClipRegion)
   765     : SkBitmapDevice(makeContentBitmap(layerSize, NULL)),
   766       fPageSize(layerSize),
   767       fContentSize(layerSize),
   768       fExistingClipStack(existingClipStack),
   769       fExistingClipRegion(existingClipRegion),
   770       fLastContentEntry(NULL),
   771       fLastMarginContentEntry(NULL),
   772       fClipStack(NULL),
   773       fEncoder(NULL),
   774       fRasterDpi(72.0f) {
   775     fInitialTransform.reset();
   776     this->init();
   777 }
   779 SkPDFDevice::~SkPDFDevice() {
   780     this->cleanUp(true);
   781 }
   783 void SkPDFDevice::init() {
   784     fAnnotations = NULL;
   785     fResourceDict = NULL;
   786     fContentEntries.free();
   787     fLastContentEntry = NULL;
   788     fMarginContentEntries.free();
   789     fLastMarginContentEntry = NULL;
   790     fDrawingArea = kContent_DrawingArea;
   791     if (fFontGlyphUsage.get() == NULL) {
   792         fFontGlyphUsage.reset(new SkPDFGlyphSetMap());
   793     }
   794 }
   796 void SkPDFDevice::cleanUp(bool clearFontUsage) {
   797     fGraphicStateResources.unrefAll();
   798     fXObjectResources.unrefAll();
   799     fFontResources.unrefAll();
   800     fShaderResources.unrefAll();
   801     SkSafeUnref(fAnnotations);
   802     SkSafeUnref(fResourceDict);
   803     fNamedDestinations.deleteAll();
   805     if (clearFontUsage) {
   806         fFontGlyphUsage->reset();
   807     }
   808 }
   810 void SkPDFDevice::clear(SkColor color) {
   811     this->cleanUp(true);
   812     this->init();
   814     SkPaint paint;
   815     paint.setColor(color);
   816     paint.setStyle(SkPaint::kFill_Style);
   817     SkMatrix identity;
   818     identity.reset();
   819     ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
   820                                identity, paint);
   821     internalDrawPaint(paint, content.entry());
   822 }
   824 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
   825     SkPaint newPaint = paint;
   826     newPaint.setStyle(SkPaint::kFill_Style);
   827     ScopedContentEntry content(this, d, newPaint);
   828     internalDrawPaint(newPaint, content.entry());
   829 }
   831 void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
   832                                     ContentEntry* contentEntry) {
   833     if (!contentEntry) {
   834         return;
   835     }
   836     SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
   837                                  SkIntToScalar(this->height()));
   838     SkMatrix inverse;
   839     if (!contentEntry->fState.fMatrix.invert(&inverse)) {
   840         return;
   841     }
   842     inverse.mapRect(&bbox);
   844     SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
   845     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
   846                           &contentEntry->fContent);
   847 }
   849 void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
   850                              size_t count, const SkPoint* points,
   851                              const SkPaint& passedPaint) {
   852     if (count == 0) {
   853         return;
   854     }
   856     if (handlePointAnnotation(points, count, *d.fMatrix, passedPaint)) {
   857         return;
   858     }
   860     // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
   861     // We only use this when there's a path effect because of the overhead
   862     // of multiple calls to setUpContentEntry it causes.
   863     if (passedPaint.getPathEffect()) {
   864         if (d.fClip->isEmpty()) {
   865             return;
   866         }
   867         SkDraw pointDraw(d);
   868         pointDraw.fDevice = this;
   869         pointDraw.drawPoints(mode, count, points, passedPaint, true);
   870         return;
   871     }
   873     const SkPaint* paint = &passedPaint;
   874     SkPaint modifiedPaint;
   876     if (mode == SkCanvas::kPoints_PointMode &&
   877             paint->getStrokeCap() != SkPaint::kRound_Cap) {
   878         modifiedPaint = *paint;
   879         paint = &modifiedPaint;
   880         if (paint->getStrokeWidth()) {
   881             // PDF won't draw a single point with square/butt caps because the
   882             // orientation is ambiguous.  Draw a rectangle instead.
   883             modifiedPaint.setStyle(SkPaint::kFill_Style);
   884             SkScalar strokeWidth = paint->getStrokeWidth();
   885             SkScalar halfStroke = SkScalarHalf(strokeWidth);
   886             for (size_t i = 0; i < count; i++) {
   887                 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
   888                 r.inset(-halfStroke, -halfStroke);
   889                 drawRect(d, r, modifiedPaint);
   890             }
   891             return;
   892         } else {
   893             modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
   894         }
   895     }
   897     ScopedContentEntry content(this, d, *paint);
   898     if (!content.entry()) {
   899         return;
   900     }
   902     switch (mode) {
   903         case SkCanvas::kPolygon_PointMode:
   904             SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
   905                                &content.entry()->fContent);
   906             for (size_t i = 1; i < count; i++) {
   907                 SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
   908                                        &content.entry()->fContent);
   909             }
   910             SkPDFUtils::StrokePath(&content.entry()->fContent);
   911             break;
   912         case SkCanvas::kLines_PointMode:
   913             for (size_t i = 0; i < count/2; i++) {
   914                 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
   915                                    &content.entry()->fContent);
   916                 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
   917                                        points[i * 2 + 1].fY,
   918                                        &content.entry()->fContent);
   919                 SkPDFUtils::StrokePath(&content.entry()->fContent);
   920             }
   921             break;
   922         case SkCanvas::kPoints_PointMode:
   923             SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
   924             for (size_t i = 0; i < count; i++) {
   925                 SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
   926                                    &content.entry()->fContent);
   927                 SkPDFUtils::ClosePath(&content.entry()->fContent);
   928                 SkPDFUtils::StrokePath(&content.entry()->fContent);
   929             }
   930             break;
   931         default:
   932             SkASSERT(false);
   933     }
   934 }
   936 void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& rect,
   937                            const SkPaint& paint) {
   938     SkRect r = rect;
   939     r.sort();
   941     if (paint.getPathEffect()) {
   942         if (d.fClip->isEmpty()) {
   943             return;
   944         }
   945         SkPath path;
   946         path.addRect(r);
   947         drawPath(d, path, paint, NULL, true);
   948         return;
   949     }
   951     if (handleRectAnnotation(r, *d.fMatrix, paint)) {
   952         return;
   953     }
   955     ScopedContentEntry content(this, d, paint);
   956     if (!content.entry()) {
   957         return;
   958     }
   959     SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
   960     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
   961                           &content.entry()->fContent);
   962 }
   964 void SkPDFDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect,
   965                             const SkPaint& paint) {
   966     SkPath  path;
   967     path.addRRect(rrect);
   968     this->drawPath(draw, path, paint, NULL, true);
   969 }
   971 void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
   972                            const SkPaint& paint, const SkMatrix* prePathMatrix,
   973                            bool pathIsMutable) {
   974     SkPath modifiedPath;
   975     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
   977     SkMatrix matrix = *d.fMatrix;
   978     if (prePathMatrix) {
   979         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
   980             if (!pathIsMutable) {
   981                 pathPtr = &modifiedPath;
   982                 pathIsMutable = true;
   983             }
   984             origPath.transform(*prePathMatrix, pathPtr);
   985         } else {
   986             if (!matrix.preConcat(*prePathMatrix)) {
   987                 // TODO(edisonn): report somehow why we failed?
   988                 return;
   989             }
   990         }
   991     }
   993     if (paint.getPathEffect()) {
   994         if (d.fClip->isEmpty()) {
   995             return;
   996         }
   997         if (!pathIsMutable) {
   998             pathPtr = &modifiedPath;
   999             pathIsMutable = true;
  1001         bool fill = paint.getFillPath(origPath, pathPtr);
  1003         SkPaint noEffectPaint(paint);
  1004         noEffectPaint.setPathEffect(NULL);
  1005         if (fill) {
  1006             noEffectPaint.setStyle(SkPaint::kFill_Style);
  1007         } else {
  1008             noEffectPaint.setStyle(SkPaint::kStroke_Style);
  1009             noEffectPaint.setStrokeWidth(0);
  1011         drawPath(d, *pathPtr, noEffectPaint, NULL, true);
  1012         return;
  1015 #ifdef SK_PDF_USE_PATHOPS
  1016     if (handleInversePath(d, origPath, paint, pathIsMutable, prePathMatrix)) {
  1017         return;
  1019 #endif
  1021     if (handleRectAnnotation(pathPtr->getBounds(), matrix, paint)) {
  1022         return;
  1025     ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
  1026     if (!content.entry()) {
  1027         return;
  1029     SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
  1030                          &content.entry()->fContent);
  1031     SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
  1032                           &content.entry()->fContent);
  1035 void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
  1036                                  const SkRect* src, const SkRect& dst,
  1037                                  const SkPaint& paint,
  1038                                  SkCanvas::DrawBitmapRectFlags flags) {
  1039     // TODO: this code path must be updated to respect the flags parameter
  1040     SkMatrix    matrix;
  1041     SkRect      bitmapBounds, tmpSrc, tmpDst;
  1042     SkBitmap    tmpBitmap;
  1044     bitmapBounds.isetWH(bitmap.width(), bitmap.height());
  1046     // Compute matrix from the two rectangles
  1047     if (src) {
  1048         tmpSrc = *src;
  1049     } else {
  1050         tmpSrc = bitmapBounds;
  1052     matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
  1054     const SkBitmap* bitmapPtr = &bitmap;
  1056     // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
  1057     // needed (if the src was clipped). No check needed if src==null.
  1058     if (src) {
  1059         if (!bitmapBounds.contains(*src)) {
  1060             if (!tmpSrc.intersect(bitmapBounds)) {
  1061                 return; // nothing to draw
  1063             // recompute dst, based on the smaller tmpSrc
  1064             matrix.mapRect(&tmpDst, tmpSrc);
  1067         // since we may need to clamp to the borders of the src rect within
  1068         // the bitmap, we extract a subset.
  1069         // TODO: make sure this is handled in drawBitmap and remove from here.
  1070         SkIRect srcIR;
  1071         tmpSrc.roundOut(&srcIR);
  1072         if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
  1073             return;
  1075         bitmapPtr = &tmpBitmap;
  1077         // Since we did an extract, we need to adjust the matrix accordingly
  1078         SkScalar dx = 0, dy = 0;
  1079         if (srcIR.fLeft > 0) {
  1080             dx = SkIntToScalar(srcIR.fLeft);
  1082         if (srcIR.fTop > 0) {
  1083             dy = SkIntToScalar(srcIR.fTop);
  1085         if (dx || dy) {
  1086             matrix.preTranslate(dx, dy);
  1089     this->drawBitmap(draw, *bitmapPtr, matrix, paint);
  1092 void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
  1093                              const SkMatrix& matrix, const SkPaint& paint) {
  1094     if (d.fClip->isEmpty()) {
  1095         return;
  1098     SkMatrix transform = matrix;
  1099     transform.postConcat(*d.fMatrix);
  1100     this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL,
  1101                              paint);
  1104 void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
  1105                              int x, int y, const SkPaint& paint) {
  1106     if (d.fClip->isEmpty()) {
  1107         return;
  1110     SkMatrix matrix;
  1111     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
  1112     this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL,
  1113                              paint);
  1116 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
  1117                            SkScalar x, SkScalar y, const SkPaint& paint) {
  1118     NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
  1119     if (paint.getMaskFilter() != NULL) {
  1120         // Don't pretend we support drawing MaskFilters, it makes for artifacts
  1121         // making text unreadable (e.g. same text twice when using CSS shadows).
  1122         return;
  1124     SkPaint textPaint = calculate_text_paint(paint);
  1125     ScopedContentEntry content(this, d, textPaint, true);
  1126     if (!content.entry()) {
  1127         return;
  1130     SkGlyphStorage storage(0);
  1131     uint16_t* glyphIDs = NULL;
  1132     size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
  1133                                             &glyphIDs);
  1134     textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
  1136     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
  1137     align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
  1138     content.entry()->fContent.writeText("BT\n");
  1139     set_text_transform(x, y, textPaint.getTextSkewX(),
  1140                        &content.entry()->fContent);
  1141     size_t consumedGlyphCount = 0;
  1142     while (numGlyphs > consumedGlyphCount) {
  1143         updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry());
  1144         SkPDFFont* font = content.entry()->fState.fFont;
  1145         size_t availableGlyphs =
  1146             font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
  1147                                           numGlyphs - consumedGlyphCount);
  1148         fFontGlyphUsage->noteGlyphUsage(font, glyphIDs + consumedGlyphCount,
  1149                                         availableGlyphs);
  1150         SkString encodedString =
  1151             SkPDFString::FormatString(glyphIDs + consumedGlyphCount,
  1152                                       availableGlyphs, font->multiByteGlyphs());
  1153         content.entry()->fContent.writeText(encodedString.c_str());
  1154         consumedGlyphCount += availableGlyphs;
  1155         content.entry()->fContent.writeText(" Tj\n");
  1157     content.entry()->fContent.writeText("ET\n");
  1160 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
  1161                               const SkScalar pos[], SkScalar constY,
  1162                               int scalarsPerPos, const SkPaint& paint) {
  1163     NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
  1164     if (paint.getMaskFilter() != NULL) {
  1165         // Don't pretend we support drawing MaskFilters, it makes for artifacts
  1166         // making text unreadable (e.g. same text twice when using CSS shadows).
  1167         return;
  1169     SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
  1170     SkPaint textPaint = calculate_text_paint(paint);
  1171     ScopedContentEntry content(this, d, textPaint, true);
  1172     if (!content.entry()) {
  1173         return;
  1176 #ifdef SK_BUILD_FOR_ANDROID
  1177     /*
  1178      * In the case that we have enabled fallback fonts on Android we need to
  1179      * take the following steps to ensure that the PDF draws all characters,
  1180      * regardless of their underlying font file, correctly.
  1182      * 1. Convert input into GlyphID encoding if it currently is not
  1183      * 2. Iterate over the glyphIDs and identify the actual typeface that each
  1184      *    glyph resolves to
  1185      * 3. Iterate over those typefaces and recursively call this function with
  1186      *    only the glyphs (and their positions) that the typeface is capable of
  1187      *    resolving.
  1188      */
  1189     if (paint.getPaintOptionsAndroid().isUsingFontFallbacks()) {
  1190         uint16_t* glyphIDs = NULL;
  1191         SkGlyphStorage tmpStorage(0);
  1192         size_t numGlyphs = 0;
  1194         // convert to glyphIDs
  1195         if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
  1196             numGlyphs = len / 2;
  1197             glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>(text));
  1198         } else {
  1199             numGlyphs = paint.textToGlyphs(text, len, NULL);
  1200             tmpStorage.reset(numGlyphs);
  1201             paint.textToGlyphs(text, len, tmpStorage.get());
  1202             glyphIDs = tmpStorage.get();
  1205         // if no typeface is provided in the paint get the default
  1206         SkAutoTUnref<SkTypeface> origFace(SkSafeRef(paint.getTypeface()));
  1207         if (NULL == origFace.get()) {
  1208             origFace.reset(SkTypeface::RefDefault());
  1210         const uint16_t origGlyphCount = origFace->countGlyphs();
  1212         // keep a list of the already visited typefaces and some data about them
  1213         SkTDArray<TypefaceFallbackData> visitedTypefaces;
  1215         // find all the typefaces needed to resolve this run of text
  1216         bool usesOriginalTypeface = false;
  1217         for (uint16_t x = 0; x < numGlyphs; ++x) {
  1218             // optimization that checks to see if original typeface can resolve
  1219             // the glyph
  1220             if (glyphIDs[x] < origGlyphCount) {
  1221                 usesOriginalTypeface = true;
  1222                 continue;
  1225             // find the fallback typeface that supports this glyph
  1226             TypefaceFallbackData data;
  1227             data.typeface =
  1228                     SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(),
  1229                                             paint.getPaintOptionsAndroid(),
  1230                                             &data.lowerBounds,
  1231                                             &data.upperBounds);
  1232             // add the typeface and its data if we don't have it
  1233             if (data.typeface && !visitedTypefaces.contains(data)) {
  1234                 visitedTypefaces.push(data);
  1238         // if the original font was used then add it to the list as well
  1239         if (usesOriginalTypeface) {
  1240             TypefaceFallbackData* data = visitedTypefaces.push();
  1241             data->typeface = origFace.get();
  1242             data->lowerBounds = 0;
  1243             data->upperBounds = origGlyphCount;
  1246         // keep a scratch glyph and pos storage
  1247         SkAutoTMalloc<SkScalar> posStorage(len * scalarsPerPos);
  1248         SkScalar* tmpPos = posStorage.get();
  1249         SkGlyphStorage glyphStorage(numGlyphs);
  1250         uint16_t* tmpGlyphIDs = glyphStorage.get();
  1252         // loop through all the valid typefaces, trim the glyphs to only those
  1253         // resolved by the typeface, and then draw that run of glyphs
  1254         for (int x = 0; x < visitedTypefaces.count(); ++x) {
  1255             const TypefaceFallbackData& data = visitedTypefaces[x];
  1257             int tmpGlyphCount = 0;
  1258             for (uint16_t y = 0; y < numGlyphs; ++y) {
  1259                 if (glyphIDs[y] >= data.lowerBounds &&
  1260                         glyphIDs[y] < data.upperBounds) {
  1261                     tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds;
  1262                     memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]),
  1263                            &(pos[y * scalarsPerPos]),
  1264                            scalarsPerPos * sizeof(SkScalar));
  1265                     tmpGlyphCount++;
  1269             // recursively call this function with the right typeface
  1270             SkPaint tmpPaint = paint;
  1271             tmpPaint.setTypeface(data.typeface);
  1272             tmpPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
  1274             // turn off fallback chaining
  1275             SkPaintOptionsAndroid paintOpts = tmpPaint.getPaintOptionsAndroid();
  1276             paintOpts.setUseFontFallbacks(false);
  1277             tmpPaint.setPaintOptionsAndroid(paintOpts);
  1279             this->drawPosText(d, tmpGlyphIDs, tmpGlyphCount * 2, tmpPos, constY,
  1280                               scalarsPerPos, tmpPaint);
  1282         return;
  1284 #endif
  1286     SkGlyphStorage storage(0);
  1287     uint16_t* glyphIDs = NULL;
  1288     size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
  1289                                             &glyphIDs);
  1290     textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
  1292     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
  1293     content.entry()->fContent.writeText("BT\n");
  1294     updateFont(textPaint, glyphIDs[0], content.entry());
  1295     for (size_t i = 0; i < numGlyphs; i++) {
  1296         SkPDFFont* font = content.entry()->fState.fFont;
  1297         uint16_t encodedValue = glyphIDs[i];
  1298         if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
  1299             updateFont(textPaint, glyphIDs[i], content.entry());
  1300             i--;
  1301             continue;
  1303         fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1);
  1304         SkScalar x = pos[i * scalarsPerPos];
  1305         SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
  1306         align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y);
  1307         set_text_transform(x, y, textPaint.getTextSkewX(),
  1308                            &content.entry()->fContent);
  1309         SkString encodedString =
  1310             SkPDFString::FormatString(&encodedValue, 1,
  1311                                       font->multiByteGlyphs());
  1312         content.entry()->fContent.writeText(encodedString.c_str());
  1313         content.entry()->fContent.writeText(" Tj\n");
  1315     content.entry()->fContent.writeText("ET\n");
  1318 void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
  1319                                  const SkPath& path, const SkMatrix* matrix,
  1320                                  const SkPaint& paint) {
  1321     if (d.fClip->isEmpty()) {
  1322         return;
  1324     d.drawTextOnPath((const char*)text, len, path, matrix, paint);
  1327 void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
  1328                                int vertexCount, const SkPoint verts[],
  1329                                const SkPoint texs[], const SkColor colors[],
  1330                                SkXfermode* xmode, const uint16_t indices[],
  1331                                int indexCount, const SkPaint& paint) {
  1332     if (d.fClip->isEmpty()) {
  1333         return;
  1335     // TODO: implement drawVertices
  1338 void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device,
  1339                              int x, int y, const SkPaint& paint) {
  1340     // our onCreateDevice() always creates SkPDFDevice subclasses.
  1341     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
  1342     if (pdfDevice->isContentEmpty()) {
  1343         return;
  1346     SkMatrix matrix;
  1347     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
  1348     ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
  1349     if (!content.entry()) {
  1350         return;
  1352     if (content.needShape()) {
  1353         SkPath shape;
  1354         shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
  1355                                        SkIntToScalar(device->width()),
  1356                                        SkIntToScalar(device->height())));
  1357         content.setShape(shape);
  1359     if (!content.needSource()) {
  1360         return;
  1363     SkAutoTUnref<SkPDFFormXObject> xObject(new SkPDFFormXObject(pdfDevice));
  1364     SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
  1365                                 &content.entry()->fContent);
  1367     // Merge glyph sets from the drawn device.
  1368     fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage());
  1371 void SkPDFDevice::onAttachToCanvas(SkCanvas* canvas) {
  1372     INHERITED::onAttachToCanvas(canvas);
  1374     // Canvas promises that this ptr is valid until onDetachFromCanvas is called
  1375     fClipStack = canvas->getClipStack();
  1378 void SkPDFDevice::onDetachFromCanvas() {
  1379     INHERITED::onDetachFromCanvas();
  1381     fClipStack = NULL;
  1384 ContentEntry* SkPDFDevice::getLastContentEntry() {
  1385     if (fDrawingArea == kContent_DrawingArea) {
  1386         return fLastContentEntry;
  1387     } else {
  1388         return fLastMarginContentEntry;
  1392 SkAutoTDelete<ContentEntry>* SkPDFDevice::getContentEntries() {
  1393     if (fDrawingArea == kContent_DrawingArea) {
  1394         return &fContentEntries;
  1395     } else {
  1396         return &fMarginContentEntries;
  1400 void SkPDFDevice::setLastContentEntry(ContentEntry* contentEntry) {
  1401     if (fDrawingArea == kContent_DrawingArea) {
  1402         fLastContentEntry = contentEntry;
  1403     } else {
  1404         fLastMarginContentEntry = contentEntry;
  1408 void SkPDFDevice::setDrawingArea(DrawingArea drawingArea) {
  1409     // A ScopedContentEntry only exists during the course of a draw call, so
  1410     // this can't be called while a ScopedContentEntry exists.
  1411     fDrawingArea = drawingArea;
  1414 SkPDFResourceDict* SkPDFDevice::getResourceDict() {
  1415     if (NULL == fResourceDict) {
  1416         fResourceDict = SkNEW(SkPDFResourceDict);
  1418         if (fGraphicStateResources.count()) {
  1419             for (int i = 0; i < fGraphicStateResources.count(); i++) {
  1420                 fResourceDict->insertResourceAsReference(
  1421                         SkPDFResourceDict::kExtGState_ResourceType,
  1422                         i, fGraphicStateResources[i]);
  1426         if (fXObjectResources.count()) {
  1427             for (int i = 0; i < fXObjectResources.count(); i++) {
  1428                 fResourceDict->insertResourceAsReference(
  1429                         SkPDFResourceDict::kXObject_ResourceType,
  1430                         i, fXObjectResources[i]);
  1434         if (fFontResources.count()) {
  1435             for (int i = 0; i < fFontResources.count(); i++) {
  1436                 fResourceDict->insertResourceAsReference(
  1437                         SkPDFResourceDict::kFont_ResourceType,
  1438                         i, fFontResources[i]);
  1442         if (fShaderResources.count()) {
  1443             SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict());
  1444             for (int i = 0; i < fShaderResources.count(); i++) {
  1445                 fResourceDict->insertResourceAsReference(
  1446                         SkPDFResourceDict::kPattern_ResourceType,
  1447                         i, fShaderResources[i]);
  1451     return fResourceDict;
  1454 const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
  1455     return fFontResources;
  1458 SkPDFArray* SkPDFDevice::copyMediaBox() const {
  1459     // should this be a singleton?
  1460     SkAutoTUnref<SkPDFInt> zero(SkNEW_ARGS(SkPDFInt, (0)));
  1462     SkPDFArray* mediaBox = SkNEW(SkPDFArray);
  1463     mediaBox->reserve(4);
  1464     mediaBox->append(zero.get());
  1465     mediaBox->append(zero.get());
  1466     mediaBox->appendInt(fPageSize.fWidth);
  1467     mediaBox->appendInt(fPageSize.fHeight);
  1468     return mediaBox;
  1471 SkStream* SkPDFDevice::content() const {
  1472     SkMemoryStream* result = new SkMemoryStream;
  1473     result->setData(this->copyContentToData())->unref();
  1474     return result;
  1477 void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry,
  1478         SkWStream* data) const {
  1479     // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the
  1480     // right thing to pass here.
  1481     GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data);
  1482     while (entry != NULL) {
  1483         SkPoint translation;
  1484         translation.iset(this->getOrigin());
  1485         translation.negate();
  1486         gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
  1487                            translation);
  1488         gsState.updateMatrix(entry->fState.fMatrix);
  1489         gsState.updateDrawingState(entry->fState);
  1491         SkAutoDataUnref copy(entry->fContent.copyToData());
  1492         data->write(copy->data(), copy->size());
  1493         entry = entry->fNext.get();
  1495     gsState.drainStack();
  1498 SkData* SkPDFDevice::copyContentToData() const {
  1499     SkDynamicMemoryWStream data;
  1500     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
  1501         SkPDFUtils::AppendTransform(fInitialTransform, &data);
  1504     // TODO(aayushkumar): Apply clip along the margins.  Currently, webkit
  1505     // colors the contentArea white before it starts drawing into it and
  1506     // that currently acts as our clip.
  1507     // Also, think about adding a transform here (or assume that the values
  1508     // sent across account for that)
  1509     SkPDFDevice::copyContentEntriesToData(fMarginContentEntries.get(), &data);
  1511     // If the content area is the entire page, then we don't need to clip
  1512     // the content area (PDF area clips to the page size).  Otherwise,
  1513     // we have to clip to the content area; we've already applied the
  1514     // initial transform, so just clip to the device size.
  1515     if (fPageSize != fContentSize) {
  1516         SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()),
  1517                                   SkIntToScalar(this->height()));
  1518         emit_clip(NULL, &r, &data);
  1521     SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data);
  1523     // potentially we could cache this SkData, and only rebuild it if we
  1524     // see that our state has changed.
  1525     return data.copyToData();
  1528 #ifdef SK_PDF_USE_PATHOPS
  1529 /* Draws an inverse filled path by using Path Ops to compute the positive
  1530  * inverse using the current clip as the inverse bounds.
  1531  * Return true if this was an inverse path and was properly handled,
  1532  * otherwise returns false and the normal drawing routine should continue,
  1533  * either as a (incorrect) fallback or because the path was not inverse
  1534  * in the first place.
  1535  */
  1536 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath,
  1537                                     const SkPaint& paint, bool pathIsMutable,
  1538                                     const SkMatrix* prePathMatrix) {
  1539     if (!origPath.isInverseFillType()) {
  1540         return false;
  1543     if (d.fClip->isEmpty()) {
  1544         return false;
  1547     SkPath modifiedPath;
  1548     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
  1549     SkPaint noInversePaint(paint);
  1551     // Merge stroking operations into final path.
  1552     if (SkPaint::kStroke_Style == paint.getStyle() ||
  1553         SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
  1554         bool doFillPath = paint.getFillPath(origPath, &modifiedPath);
  1555         if (doFillPath) {
  1556             noInversePaint.setStyle(SkPaint::kFill_Style);
  1557             noInversePaint.setStrokeWidth(0);
  1558             pathPtr = &modifiedPath;
  1559         } else {
  1560             // To be consistent with the raster output, hairline strokes
  1561             // are rendered as non-inverted.
  1562             modifiedPath.toggleInverseFillType();
  1563             drawPath(d, modifiedPath, paint, NULL, true);
  1564             return true;
  1568     // Get bounds of clip in current transform space
  1569     // (clip bounds are given in device space).
  1570     SkRect bounds;
  1571     SkMatrix transformInverse;
  1572     SkMatrix totalMatrix = *d.fMatrix;
  1573     if (prePathMatrix) {
  1574         totalMatrix.preConcat(*prePathMatrix);
  1576     if (!totalMatrix.invert(&transformInverse)) {
  1577         return false;
  1579     bounds.set(d.fClip->getBounds());
  1580     transformInverse.mapRect(&bounds);
  1582     // Extend the bounds by the line width (plus some padding)
  1583     // so the edge doesn't cause a visible stroke.
  1584     bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
  1585                   paint.getStrokeWidth() + SK_Scalar1);
  1587     if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
  1588         return false;
  1591     drawPath(d, modifiedPath, noInversePaint, prePathMatrix, true);
  1592     return true;
  1594 #endif
  1596 bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
  1597                                        const SkPaint& p) {
  1598     SkAnnotation* annotationInfo = p.getAnnotation();
  1599     if (!annotationInfo) {
  1600         return false;
  1602     SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key());
  1603     if (urlData) {
  1604         handleLinkToURL(urlData, r, matrix);
  1605         return p.getAnnotation() != NULL;
  1607     SkData* linkToName = annotationInfo->find(
  1608             SkAnnotationKeys::Link_Named_Dest_Key());
  1609     if (linkToName) {
  1610         handleLinkToNamedDest(linkToName, r, matrix);
  1611         return p.getAnnotation() != NULL;
  1613     return false;
  1616 bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
  1617                                         const SkMatrix& matrix,
  1618                                         const SkPaint& paint) {
  1619     SkAnnotation* annotationInfo = paint.getAnnotation();
  1620     if (!annotationInfo) {
  1621         return false;
  1623     SkData* nameData = annotationInfo->find(
  1624             SkAnnotationKeys::Define_Named_Dest_Key());
  1625     if (nameData) {
  1626         for (size_t i = 0; i < count; i++) {
  1627             defineNamedDestination(nameData, points[i], matrix);
  1629         return paint.getAnnotation() != NULL;
  1631     return false;
  1634 SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r,
  1635                                              const SkMatrix& matrix) {
  1636     SkMatrix transform = matrix;
  1637     transform.postConcat(fInitialTransform);
  1638     SkRect translatedRect;
  1639     transform.mapRect(&translatedRect, r);
  1641     if (NULL == fAnnotations) {
  1642         fAnnotations = SkNEW(SkPDFArray);
  1644     SkPDFDict* annotation(SkNEW_ARGS(SkPDFDict, ("Annot")));
  1645     annotation->insertName("Subtype", "Link");
  1646     fAnnotations->append(annotation);
  1648     SkAutoTUnref<SkPDFArray> border(SkNEW(SkPDFArray));
  1649     border->reserve(3);
  1650     border->appendInt(0);  // Horizontal corner radius.
  1651     border->appendInt(0);  // Vertical corner radius.
  1652     border->appendInt(0);  // Width, 0 = no border.
  1653     annotation->insert("Border", border.get());
  1655     SkAutoTUnref<SkPDFArray> rect(SkNEW(SkPDFArray));
  1656     rect->reserve(4);
  1657     rect->appendScalar(translatedRect.fLeft);
  1658     rect->appendScalar(translatedRect.fTop);
  1659     rect->appendScalar(translatedRect.fRight);
  1660     rect->appendScalar(translatedRect.fBottom);
  1661     annotation->insert("Rect", rect.get());
  1663     return annotation;
  1666 void SkPDFDevice::handleLinkToURL(SkData* urlData, const SkRect& r,
  1667                                   const SkMatrix& matrix) {
  1668     SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
  1670     SkString url(static_cast<const char *>(urlData->data()),
  1671                  urlData->size() - 1);
  1672     SkAutoTUnref<SkPDFDict> action(SkNEW_ARGS(SkPDFDict, ("Action")));
  1673     action->insertName("S", "URI");
  1674     action->insert("URI", SkNEW_ARGS(SkPDFString, (url)))->unref();
  1675     annotation->insert("A", action.get());
  1678 void SkPDFDevice::handleLinkToNamedDest(SkData* nameData, const SkRect& r,
  1679                                         const SkMatrix& matrix) {
  1680     SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
  1681     SkString name(static_cast<const char *>(nameData->data()),
  1682                   nameData->size() - 1);
  1683     annotation->insert("Dest", SkNEW_ARGS(SkPDFName, (name)))->unref();
  1686 struct NamedDestination {
  1687     const SkData* nameData;
  1688     SkPoint point;
  1690     NamedDestination(const SkData* nameData, const SkPoint& point)
  1691         : nameData(nameData), point(point) {
  1692         nameData->ref();
  1695     ~NamedDestination() {
  1696         nameData->unref();
  1698 };
  1700 void SkPDFDevice::defineNamedDestination(SkData* nameData, const SkPoint& point,
  1701                                          const SkMatrix& matrix) {
  1702     SkMatrix transform = matrix;
  1703     transform.postConcat(fInitialTransform);
  1704     SkPoint translatedPoint;
  1705     transform.mapXY(point.x(), point.y(), &translatedPoint);
  1706     fNamedDestinations.push(
  1707         SkNEW_ARGS(NamedDestination, (nameData, translatedPoint)));
  1710 void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) {
  1711     int nDest = fNamedDestinations.count();
  1712     for (int i = 0; i < nDest; i++) {
  1713         NamedDestination* dest = fNamedDestinations[i];
  1714         SkAutoTUnref<SkPDFArray> pdfDest(SkNEW(SkPDFArray));
  1715         pdfDest->reserve(5);
  1716         pdfDest->append(SkNEW_ARGS(SkPDFObjRef, (page)))->unref();
  1717         pdfDest->appendName("XYZ");
  1718         pdfDest->appendScalar(dest->point.x());
  1719         pdfDest->appendScalar(dest->point.y());
  1720         pdfDest->appendInt(0);  // Leave zoom unchanged
  1721         dict->insert(static_cast<const char *>(dest->nameData->data()),
  1722                      pdfDest);
  1726 SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
  1727     SkPDFFormXObject* xobject = SkNEW_ARGS(SkPDFFormXObject, (this));
  1728     // We always draw the form xobjects that we create back into the device, so
  1729     // we simply preserve the font usage instead of pulling it out and merging
  1730     // it back in later.
  1731     cleanUp(false);  // Reset this device to have no content.
  1732     init();
  1733     return xobject;
  1736 void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
  1737                                           SkPDFFormXObject* mask,
  1738                                           const SkClipStack* clipStack,
  1739                                           const SkRegion& clipRegion,
  1740                                           SkXfermode::Mode mode,
  1741                                           bool invertClip) {
  1742     if (clipRegion.isEmpty() && !invertClip) {
  1743         return;
  1746     SkAutoTUnref<SkPDFGraphicState> sMaskGS(
  1747         SkPDFGraphicState::GetSMaskGraphicState(
  1748             mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
  1750     SkMatrix identity;
  1751     identity.reset();
  1752     SkPaint paint;
  1753     paint.setXfermodeMode(mode);
  1754     ScopedContentEntry content(this, clipStack, clipRegion, identity, paint);
  1755     if (!content.entry()) {
  1756         return;
  1758     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
  1759                                   &content.entry()->fContent);
  1760     SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
  1762     sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
  1763     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
  1764                                   &content.entry()->fContent);
  1767 ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
  1768                                              const SkRegion& clipRegion,
  1769                                              const SkMatrix& matrix,
  1770                                              const SkPaint& paint,
  1771                                              bool hasText,
  1772                                              SkPDFFormXObject** dst) {
  1773     *dst = NULL;
  1774     if (clipRegion.isEmpty()) {
  1775         return NULL;
  1778     // The clip stack can come from an SkDraw where it is technically optional.
  1779     SkClipStack synthesizedClipStack;
  1780     if (clipStack == NULL) {
  1781         if (clipRegion == fExistingClipRegion) {
  1782             clipStack = &fExistingClipStack;
  1783         } else {
  1784             // GraphicStackState::updateClip expects the clip stack to have
  1785             // fExistingClip as a prefix, so start there, then set the clip
  1786             // to the passed region.
  1787             synthesizedClipStack = fExistingClipStack;
  1788             SkPath clipPath;
  1789             clipRegion.getBoundaryPath(&clipPath);
  1790             synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op,
  1791                                              false);
  1792             clipStack = &synthesizedClipStack;
  1796     SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
  1797     if (paint.getXfermode()) {
  1798         paint.getXfermode()->asMode(&xfermode);
  1801     // For the following modes, we want to handle source and destination
  1802     // separately, so make an object of what's already there.
  1803     if (xfermode == SkXfermode::kClear_Mode       ||
  1804             xfermode == SkXfermode::kSrc_Mode     ||
  1805             xfermode == SkXfermode::kSrcIn_Mode   ||
  1806             xfermode == SkXfermode::kDstIn_Mode   ||
  1807             xfermode == SkXfermode::kSrcOut_Mode  ||
  1808             xfermode == SkXfermode::kDstOut_Mode  ||
  1809             xfermode == SkXfermode::kSrcATop_Mode ||
  1810             xfermode == SkXfermode::kDstATop_Mode ||
  1811             xfermode == SkXfermode::kModulate_Mode) {
  1812         if (!isContentEmpty()) {
  1813             *dst = createFormXObjectFromDevice();
  1814             SkASSERT(isContentEmpty());
  1815         } else if (xfermode != SkXfermode::kSrc_Mode &&
  1816                    xfermode != SkXfermode::kSrcOut_Mode) {
  1817             // Except for Src and SrcOut, if there isn't anything already there,
  1818             // then we're done.
  1819             return NULL;
  1822     // TODO(vandebo): Figure out how/if we can handle the following modes:
  1823     // Xor, Plus.
  1825     // Dst xfer mode doesn't draw source at all.
  1826     if (xfermode == SkXfermode::kDst_Mode) {
  1827         return NULL;
  1830     ContentEntry* entry;
  1831     SkAutoTDelete<ContentEntry> newEntry;
  1833     ContentEntry* lastContentEntry = getLastContentEntry();
  1834     if (lastContentEntry && lastContentEntry->fContent.getOffset() == 0) {
  1835         entry = lastContentEntry;
  1836     } else {
  1837         newEntry.reset(new ContentEntry);
  1838         entry = newEntry.get();
  1841     populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
  1842                                        hasText, &entry->fState);
  1843     if (lastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
  1844             entry->fState.compareInitialState(lastContentEntry->fState)) {
  1845         return lastContentEntry;
  1848     SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries();
  1849     if (!lastContentEntry) {
  1850         contentEntries->reset(entry);
  1851         setLastContentEntry(entry);
  1852     } else if (xfermode == SkXfermode::kDstOver_Mode) {
  1853         entry->fNext.reset(contentEntries->detach());
  1854         contentEntries->reset(entry);
  1855     } else {
  1856         lastContentEntry->fNext.reset(entry);
  1857         setLastContentEntry(entry);
  1859     newEntry.detach();
  1860     return entry;
  1863 void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
  1864                                      SkPDFFormXObject* dst,
  1865                                      SkPath* shape) {
  1866     if (xfermode != SkXfermode::kClear_Mode       &&
  1867             xfermode != SkXfermode::kSrc_Mode     &&
  1868             xfermode != SkXfermode::kDstOver_Mode &&
  1869             xfermode != SkXfermode::kSrcIn_Mode   &&
  1870             xfermode != SkXfermode::kDstIn_Mode   &&
  1871             xfermode != SkXfermode::kSrcOut_Mode  &&
  1872             xfermode != SkXfermode::kDstOut_Mode  &&
  1873             xfermode != SkXfermode::kSrcATop_Mode &&
  1874             xfermode != SkXfermode::kDstATop_Mode &&
  1875             xfermode != SkXfermode::kModulate_Mode) {
  1876         SkASSERT(!dst);
  1877         return;
  1879     if (xfermode == SkXfermode::kDstOver_Mode) {
  1880         SkASSERT(!dst);
  1881         ContentEntry* firstContentEntry = getContentEntries()->get();
  1882         if (firstContentEntry->fContent.getOffset() == 0) {
  1883             // For DstOver, an empty content entry was inserted before the rest
  1884             // of the content entries. If nothing was drawn, it needs to be
  1885             // removed.
  1886             SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries();
  1887             contentEntries->reset(firstContentEntry->fNext.detach());
  1889         return;
  1891     if (!dst) {
  1892         SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
  1893                  xfermode == SkXfermode::kSrcOut_Mode);
  1894         return;
  1897     ContentEntry* contentEntries = getContentEntries()->get();
  1898     SkASSERT(dst);
  1899     SkASSERT(!contentEntries->fNext.get());
  1900     // Changing the current content into a form-xobject will destroy the clip
  1901     // objects which is fine since the xobject will already be clipped. However
  1902     // if source has shape, we need to clip it too, so a copy of the clip is
  1903     // saved.
  1904     SkClipStack clipStack = contentEntries->fState.fClipStack;
  1905     SkRegion clipRegion = contentEntries->fState.fClipRegion;
  1907     SkMatrix identity;
  1908     identity.reset();
  1909     SkPaint stockPaint;
  1911     SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
  1912     if (isContentEmpty()) {
  1913         // If nothing was drawn and there's no shape, then the draw was a
  1914         // no-op, but dst needs to be restored for that to be true.
  1915         // If there is shape, then an empty source with Src, SrcIn, SrcOut,
  1916         // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
  1917         // reduces to Dst.
  1918         if (shape == NULL || xfermode == SkXfermode::kDstOut_Mode ||
  1919                 xfermode == SkXfermode::kSrcATop_Mode) {
  1920             ScopedContentEntry content(this, &fExistingClipStack,
  1921                                        fExistingClipRegion, identity,
  1922                                        stockPaint);
  1923             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
  1924                                         &content.entry()->fContent);
  1925             return;
  1926         } else {
  1927             xfermode = SkXfermode::kClear_Mode;
  1929     } else {
  1930         SkASSERT(!fContentEntries->fNext.get());
  1931         srcFormXObject.reset(createFormXObjectFromDevice());
  1934     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
  1935     // without alpha.
  1936     if (xfermode == SkXfermode::kSrcATop_Mode) {
  1937         // TODO(vandebo): In order to properly support SrcATop we have to track
  1938         // the shape of what's been drawn at all times. It's the intersection of
  1939         // the non-transparent parts of the device and the outlines (shape) of
  1940         // all images and devices drawn.
  1941         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
  1942                                 &fExistingClipStack, fExistingClipRegion,
  1943                                 SkXfermode::kSrcOver_Mode, true);
  1944     } else {
  1945         SkAutoTUnref<SkPDFFormXObject> dstMaskStorage;
  1946         SkPDFFormXObject* dstMask = srcFormXObject.get();
  1947         if (shape != NULL) {
  1948             // Draw shape into a form-xobject.
  1949             SkDraw d;
  1950             d.fMatrix = &identity;
  1951             d.fClip = &clipRegion;
  1952             d.fClipStack = &clipStack;
  1953             SkPaint filledPaint;
  1954             filledPaint.setColor(SK_ColorBLACK);
  1955             filledPaint.setStyle(SkPaint::kFill_Style);
  1956             this->drawPath(d, *shape, filledPaint, NULL, true);
  1958             dstMaskStorage.reset(createFormXObjectFromDevice());
  1959             dstMask = dstMaskStorage.get();
  1961         drawFormXObjectWithMask(addXObjectResource(dst), dstMask,
  1962                                 &fExistingClipStack, fExistingClipRegion,
  1963                                 SkXfermode::kSrcOver_Mode, true);
  1966     if (xfermode == SkXfermode::kClear_Mode) {
  1967         return;
  1968     } else if (xfermode == SkXfermode::kSrc_Mode ||
  1969             xfermode == SkXfermode::kDstATop_Mode) {
  1970         ScopedContentEntry content(this, &fExistingClipStack,
  1971                                    fExistingClipRegion, identity, stockPaint);
  1972         if (content.entry()) {
  1973             SkPDFUtils::DrawFormXObject(
  1974                     this->addXObjectResource(srcFormXObject.get()),
  1975                     &content.entry()->fContent);
  1977         if (xfermode == SkXfermode::kSrc_Mode) {
  1978             return;
  1980     } else if (xfermode == SkXfermode::kSrcATop_Mode) {
  1981         ScopedContentEntry content(this, &fExistingClipStack,
  1982                                    fExistingClipRegion, identity, stockPaint);
  1983         if (content.entry()) {
  1984             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
  1985                                         &content.entry()->fContent);
  1989     SkASSERT(xfermode == SkXfermode::kSrcIn_Mode   ||
  1990              xfermode == SkXfermode::kDstIn_Mode   ||
  1991              xfermode == SkXfermode::kSrcOut_Mode  ||
  1992              xfermode == SkXfermode::kDstOut_Mode  ||
  1993              xfermode == SkXfermode::kSrcATop_Mode ||
  1994              xfermode == SkXfermode::kDstATop_Mode ||
  1995              xfermode == SkXfermode::kModulate_Mode);
  1997     if (xfermode == SkXfermode::kSrcIn_Mode ||
  1998             xfermode == SkXfermode::kSrcOut_Mode ||
  1999             xfermode == SkXfermode::kSrcATop_Mode) {
  2000         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
  2001                                 &fExistingClipStack, fExistingClipRegion,
  2002                                 SkXfermode::kSrcOver_Mode,
  2003                                 xfermode == SkXfermode::kSrcOut_Mode);
  2004     } else {
  2005         SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
  2006         if (xfermode == SkXfermode::kModulate_Mode) {
  2007             drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
  2008                                     dst, &fExistingClipStack,
  2009                                     fExistingClipRegion,
  2010                                     SkXfermode::kSrcOver_Mode, false);
  2011             mode = SkXfermode::kMultiply_Mode;
  2013         drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(),
  2014                                 &fExistingClipStack, fExistingClipRegion, mode,
  2015                                 xfermode == SkXfermode::kDstOut_Mode);
  2019 bool SkPDFDevice::isContentEmpty() {
  2020     ContentEntry* contentEntries = getContentEntries()->get();
  2021     if (!contentEntries || contentEntries->fContent.getOffset() == 0) {
  2022         SkASSERT(!contentEntries || !contentEntries->fNext.get());
  2023         return true;
  2025     return false;
  2028 void SkPDFDevice::populateGraphicStateEntryFromPaint(
  2029         const SkMatrix& matrix,
  2030         const SkClipStack& clipStack,
  2031         const SkRegion& clipRegion,
  2032         const SkPaint& paint,
  2033         bool hasText,
  2034         GraphicStateEntry* entry) {
  2035     SkASSERT(paint.getPathEffect() == NULL);
  2037     NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
  2038     NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
  2040     entry->fMatrix = matrix;
  2041     entry->fClipStack = clipStack;
  2042     entry->fClipRegion = clipRegion;
  2043     entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
  2044     entry->fShaderIndex = -1;
  2046     // PDF treats a shader as a color, so we only set one or the other.
  2047     SkAutoTUnref<SkPDFObject> pdfShader;
  2048     const SkShader* shader = paint.getShader();
  2049     SkColor color = paint.getColor();
  2050     if (shader) {
  2051         // PDF positions patterns relative to the initial transform, so
  2052         // we need to apply the current transform to the shader parameters.
  2053         SkMatrix transform = matrix;
  2054         transform.postConcat(fInitialTransform);
  2056         // PDF doesn't support kClamp_TileMode, so we simulate it by making
  2057         // a pattern the size of the current clip.
  2058         SkIRect bounds = clipRegion.getBounds();
  2060         // We need to apply the initial transform to bounds in order to get
  2061         // bounds in a consistent coordinate system.
  2062         SkRect boundsTemp;
  2063         boundsTemp.set(bounds);
  2064         fInitialTransform.mapRect(&boundsTemp);
  2065         boundsTemp.roundOut(&bounds);
  2067         pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds));
  2069         if (pdfShader.get()) {
  2070             // pdfShader has been canonicalized so we can directly compare
  2071             // pointers.
  2072             int resourceIndex = fShaderResources.find(pdfShader.get());
  2073             if (resourceIndex < 0) {
  2074                 resourceIndex = fShaderResources.count();
  2075                 fShaderResources.push(pdfShader.get());
  2076                 pdfShader.get()->ref();
  2078             entry->fShaderIndex = resourceIndex;
  2079         } else {
  2080             // A color shader is treated as an invalid shader so we don't have
  2081             // to set a shader just for a color.
  2082             SkShader::GradientInfo gradientInfo;
  2083             SkColor gradientColor;
  2084             gradientInfo.fColors = &gradientColor;
  2085             gradientInfo.fColorOffsets = NULL;
  2086             gradientInfo.fColorCount = 1;
  2087             if (shader->asAGradient(&gradientInfo) ==
  2088                     SkShader::kColor_GradientType) {
  2089                 entry->fColor = SkColorSetA(gradientColor, 0xFF);
  2090                 color = gradientColor;
  2095     SkAutoTUnref<SkPDFGraphicState> newGraphicState;
  2096     if (color == paint.getColor()) {
  2097         newGraphicState.reset(
  2098                 SkPDFGraphicState::GetGraphicStateForPaint(paint));
  2099     } else {
  2100         SkPaint newPaint = paint;
  2101         newPaint.setColor(color);
  2102         newGraphicState.reset(
  2103                 SkPDFGraphicState::GetGraphicStateForPaint(newPaint));
  2105     int resourceIndex = addGraphicStateResource(newGraphicState.get());
  2106     entry->fGraphicStateIndex = resourceIndex;
  2108     if (hasText) {
  2109         entry->fTextScaleX = paint.getTextScaleX();
  2110         entry->fTextFill = paint.getStyle();
  2111     } else {
  2112         entry->fTextScaleX = 0;
  2116 int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
  2117     // Assumes that gs has been canonicalized (so we can directly compare
  2118     // pointers).
  2119     int result = fGraphicStateResources.find(gs);
  2120     if (result < 0) {
  2121         result = fGraphicStateResources.count();
  2122         fGraphicStateResources.push(gs);
  2123         gs->ref();
  2125     return result;
  2128 int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
  2129     // Assumes that xobject has been canonicalized (so we can directly compare
  2130     // pointers).
  2131     int result = fXObjectResources.find(xObject);
  2132     if (result < 0) {
  2133         result = fXObjectResources.count();
  2134         fXObjectResources.push(xObject);
  2135         xObject->ref();
  2137     return result;
  2140 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
  2141                              ContentEntry* contentEntry) {
  2142     SkTypeface* typeface = paint.getTypeface();
  2143     if (contentEntry->fState.fFont == NULL ||
  2144             contentEntry->fState.fTextSize != paint.getTextSize() ||
  2145             !contentEntry->fState.fFont->hasGlyph(glyphID)) {
  2146         int fontIndex = getFontResourceIndex(typeface, glyphID);
  2147         contentEntry->fContent.writeText("/");
  2148         contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName(
  2149                 SkPDFResourceDict::kFont_ResourceType,
  2150                 fontIndex).c_str());
  2151         contentEntry->fContent.writeText(" ");
  2152         SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent);
  2153         contentEntry->fContent.writeText(" Tf\n");
  2154         contentEntry->fState.fFont = fFontResources[fontIndex];
  2158 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
  2159     SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface,
  2160                                                                glyphID));
  2161     int resourceIndex = fFontResources.find(newFont.get());
  2162     if (resourceIndex < 0) {
  2163         resourceIndex = fFontResources.count();
  2164         fFontResources.push(newFont.get());
  2165         newFont.get()->ref();
  2167     return resourceIndex;
  2170 void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
  2171                                      const SkClipStack* clipStack,
  2172                                      const SkRegion& origClipRegion,
  2173                                      const SkBitmap& origBitmap,
  2174                                      const SkIRect* srcRect,
  2175                                      const SkPaint& paint) {
  2176     SkMatrix matrix = origMatrix;
  2177     SkRegion perspectiveBounds;
  2178     const SkRegion* clipRegion = &origClipRegion;
  2179     SkBitmap perspectiveBitmap;
  2180     const SkBitmap* bitmap = &origBitmap;
  2181     SkBitmap tmpSubsetBitmap;
  2183     // Rasterize the bitmap using perspective in a new bitmap.
  2184     if (origMatrix.hasPerspective()) {
  2185         if (fRasterDpi == 0) {
  2186             return;
  2188         SkBitmap* subsetBitmap;
  2189         if (srcRect) {
  2190             if (!origBitmap.extractSubset(&tmpSubsetBitmap, *srcRect)) {
  2191                return;
  2193             subsetBitmap = &tmpSubsetBitmap;
  2194         } else {
  2195             subsetBitmap = &tmpSubsetBitmap;
  2196             *subsetBitmap = origBitmap;
  2198         srcRect = NULL;
  2200         // Transform the bitmap in the new space, without taking into
  2201         // account the initial transform.
  2202         SkPath perspectiveOutline;
  2203         perspectiveOutline.addRect(
  2204                 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()),
  2205                                SkIntToScalar(subsetBitmap->height())));
  2206         perspectiveOutline.transform(origMatrix);
  2208         // TODO(edisonn): perf - use current clip too.
  2209         // Retrieve the bounds of the new shape.
  2210         SkRect bounds = perspectiveOutline.getBounds();
  2212         // Transform the bitmap in the new space, taking into
  2213         // account the initial transform.
  2214         SkMatrix total = origMatrix;
  2215         total.postConcat(fInitialTransform);
  2216         total.postScale(SkIntToScalar(fRasterDpi) /
  2217                             SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE),
  2218                         SkIntToScalar(fRasterDpi) /
  2219                             SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE));
  2220         SkPath physicalPerspectiveOutline;
  2221         physicalPerspectiveOutline.addRect(
  2222                 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()),
  2223                                SkIntToScalar(subsetBitmap->height())));
  2224         physicalPerspectiveOutline.transform(total);
  2226         SkScalar scaleX = physicalPerspectiveOutline.getBounds().width() /
  2227                               bounds.width();
  2228         SkScalar scaleY = physicalPerspectiveOutline.getBounds().height() /
  2229                               bounds.height();
  2231         // TODO(edisonn): A better approach would be to use a bitmap shader
  2232         // (in clamp mode) and draw a rect over the entire bounding box. Then
  2233         // intersect perspectiveOutline to the clip. That will avoid introducing
  2234         // alpha to the image while still giving good behavior at the edge of
  2235         // the image.  Avoiding alpha will reduce the pdf size and generation
  2236         // CPU time some.
  2238         const int w = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().width());
  2239         const int h = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().height());
  2240         if (!perspectiveBitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) {
  2241             return;
  2243         perspectiveBitmap.eraseColor(SK_ColorTRANSPARENT);
  2245         SkBitmapDevice device(perspectiveBitmap);
  2246         SkCanvas canvas(&device);
  2248         SkScalar deltaX = bounds.left();
  2249         SkScalar deltaY = bounds.top();
  2251         SkMatrix offsetMatrix = origMatrix;
  2252         offsetMatrix.postTranslate(-deltaX, -deltaY);
  2253         offsetMatrix.postScale(scaleX, scaleY);
  2255         // Translate the draw in the new canvas, so we perfectly fit the
  2256         // shape in the bitmap.
  2257         canvas.setMatrix(offsetMatrix);
  2259         canvas.drawBitmap(*subsetBitmap, SkIntToScalar(0), SkIntToScalar(0));
  2261         // Make sure the final bits are in the bitmap.
  2262         canvas.flush();
  2264         // In the new space, we use the identity matrix translated
  2265         // and scaled to reflect DPI.
  2266         matrix.setScale(1 / scaleX, 1 / scaleY);
  2267         matrix.postTranslate(deltaX, deltaY);
  2269         perspectiveBounds.setRect(
  2270                 SkIRect::MakeXYWH(SkScalarFloorToInt(bounds.x()),
  2271                                   SkScalarFloorToInt(bounds.y()),
  2272                                   SkScalarCeilToInt(bounds.width()),
  2273                                   SkScalarCeilToInt(bounds.height())));
  2274         clipRegion = &perspectiveBounds;
  2275         srcRect = NULL;
  2276         bitmap = &perspectiveBitmap;
  2279     SkMatrix scaled;
  2280     // Adjust for origin flip.
  2281     scaled.setScale(SK_Scalar1, -SK_Scalar1);
  2282     scaled.postTranslate(0, SK_Scalar1);
  2283     // Scale the image up from 1x1 to WxH.
  2284     SkIRect subset = SkIRect::MakeWH(bitmap->width(), bitmap->height());
  2285     scaled.postScale(SkIntToScalar(subset.width()),
  2286                      SkIntToScalar(subset.height()));
  2287     scaled.postConcat(matrix);
  2288     ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint);
  2289     if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) {
  2290         return;
  2292     if (content.needShape()) {
  2293         SkPath shape;
  2294         shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()),
  2295                                      SkIntToScalar( subset.height())));
  2296         shape.transform(matrix);
  2297         content.setShape(shape);
  2299     if (!content.needSource()) {
  2300         return;
  2303     SkAutoTUnref<SkPDFImage> image(
  2304         SkPDFImage::CreateImage(*bitmap, subset, fEncoder));
  2305     if (!image) {
  2306         return;
  2309     SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()),
  2310                                 &content.entry()->fContent);
  2313 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
  2314                                SkCanvas::Config8888) {
  2315     return false;
  2318 bool SkPDFDevice::allowImageFilter(const SkImageFilter*) {
  2319     return false;

mercurial