LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pdf - SkPDFDevice.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1280 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 100 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             :  */
       7             : 
       8             : #include "SkPDFDevice.h"
       9             : 
      10             : #include "SkAdvancedTypefaceMetrics.h"
      11             : #include "SkAnnotationKeys.h"
      12             : #include "SkBitmapDevice.h"
      13             : #include "SkBitmapKey.h"
      14             : #include "SkColor.h"
      15             : #include "SkColorFilter.h"
      16             : #include "SkDraw.h"
      17             : #include "SkDrawFilter.h"
      18             : #include "SkGlyphCache.h"
      19             : #include "SkImageFilterCache.h"
      20             : #include "SkMakeUnique.h"
      21             : #include "SkPath.h"
      22             : #include "SkPathEffect.h"
      23             : #include "SkPathOps.h"
      24             : #include "SkPDFBitmap.h"
      25             : #include "SkPDFCanon.h"
      26             : #include "SkPDFDocument.h"
      27             : #include "SkPDFFont.h"
      28             : #include "SkPDFFormXObject.h"
      29             : #include "SkPDFGraphicState.h"
      30             : #include "SkPDFResourceDict.h"
      31             : #include "SkPDFShader.h"
      32             : #include "SkPDFTypes.h"
      33             : #include "SkPDFUtils.h"
      34             : #include "SkPixelRef.h"
      35             : #include "SkRasterClip.h"
      36             : #include "SkRRect.h"
      37             : #include "SkScopeExit.h"
      38             : #include "SkString.h"
      39             : #include "SkSurface.h"
      40             : #include "SkTemplates.h"
      41             : #include "SkTextBlobRunIterator.h"
      42             : #include "SkTextFormatParams.h"
      43             : #include "SkUtils.h"
      44             : #include "SkXfermodeInterpretation.h"
      45             : #include "SkClipOpPriv.h"
      46             : 
      47             : #define DPI_FOR_RASTER_SCALE_ONE 72
      48             : 
      49             : // Utility functions
      50             : 
      51           0 : static void draw_points(SkCanvas::PointMode mode,
      52             :                         size_t count,
      53             :                         const SkPoint* points,
      54             :                         const SkPaint& paint,
      55             :                         const SkIRect& bounds,
      56             :                         const SkMatrix& ctm,
      57             :                         SkBaseDevice* device) {
      58           0 :     SkRasterClip rc(bounds);
      59           0 :     SkDraw draw;
      60           0 :     draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0);
      61           0 :     draw.fMatrix = &ctm;
      62           0 :     draw.fRC = &rc;
      63           0 :     draw.drawPoints(mode, count, points, paint, device);
      64           0 : }
      65             : 
      66           0 : static SkIRect size(const SkBaseDevice& dev) { return {0, 0, dev.width(), dev.height()}; }
      67             : 
      68             : // If the paint will definitely draw opaquely, replace kSrc with
      69             : // kSrcOver.  http://crbug.com/473572
      70           0 : static void replace_srcmode_on_opaque_paint(SkPaint* paint) {
      71           0 :     if (kSrcOver_SkXfermodeInterpretation == SkInterpretXfermode(*paint, false)) {
      72           0 :         paint->setBlendMode(SkBlendMode::kSrcOver);
      73             :     }
      74           0 : }
      75             : 
      76           0 : static void emit_pdf_color(SkColor color, SkWStream* result) {
      77           0 :     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
      78           0 :     SkPDFUtils::AppendColorComponent(SkColorGetR(color), result);
      79           0 :     result->writeText(" ");
      80           0 :     SkPDFUtils::AppendColorComponent(SkColorGetG(color), result);
      81           0 :     result->writeText(" ");
      82           0 :     SkPDFUtils::AppendColorComponent(SkColorGetB(color), result);
      83           0 :     result->writeText(" ");
      84           0 : }
      85             : 
      86           0 : static SkPaint calculate_text_paint(const SkPaint& paint) {
      87           0 :     SkPaint result = paint;
      88           0 :     if (result.isFakeBoldText()) {
      89           0 :         SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
      90             :                                                     kStdFakeBoldInterpKeys,
      91             :                                                     kStdFakeBoldInterpValues,
      92           0 :                                                     kStdFakeBoldInterpLength);
      93           0 :         SkScalar width = result.getTextSize() * fakeBoldScale;
      94           0 :         if (result.getStyle() == SkPaint::kFill_Style) {
      95           0 :             result.setStyle(SkPaint::kStrokeAndFill_Style);
      96             :         } else {
      97           0 :             width += result.getStrokeWidth();
      98             :         }
      99           0 :         result.setStrokeWidth(width);
     100             :     }
     101           0 :     return result;
     102             : }
     103             : 
     104           0 : static SkImageSubset make_image_subset(const SkBitmap& bitmap) {
     105           0 :     SkASSERT(!bitmap.drawsNothing());
     106           0 :     SkIRect subset = bitmap.getSubset();
     107           0 :     SkAutoLockPixels autoLockPixels(bitmap);
     108           0 :     SkASSERT(bitmap.pixelRef());
     109           0 :     SkBitmap tmp;
     110           0 :     tmp.setInfo(bitmap.pixelRef()->info(), bitmap.rowBytes());
     111           0 :     tmp.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
     112           0 :     tmp.lockPixels();
     113           0 :     auto img = SkImage::MakeFromBitmap(tmp);
     114           0 :     if (img) {
     115           0 :         SkASSERT(!bitmap.isImmutable() || img->uniqueID() == bitmap.getGenerationID());
     116           0 :         SkASSERT(img->bounds().contains(subset));
     117             :     }
     118           0 :     SkImageSubset imageSubset(std::move(img), subset);
     119             :     // SkImage::MakeFromBitmap only preserves genID for immutable
     120             :     // bitmaps.  Use the bitmap's original ID for de-duping.
     121           0 :     imageSubset.setID(bitmap.getGenerationID());
     122           0 :     return imageSubset;
     123             : }
     124             : 
     125           0 : SkPDFDevice::GraphicStateEntry::GraphicStateEntry()
     126             :     : fColor(SK_ColorBLACK)
     127             :     , fTextScaleX(SK_Scalar1)
     128             :     , fTextFill(SkPaint::kFill_Style)
     129             :     , fShaderIndex(-1)
     130           0 :     , fGraphicStateIndex(-1) {
     131           0 :     fMatrix.reset();
     132           0 : }
     133             : 
     134           0 : bool SkPDFDevice::GraphicStateEntry::compareInitialState(
     135             :         const GraphicStateEntry& cur) {
     136           0 :     return fColor == cur.fColor &&
     137           0 :            fShaderIndex == cur.fShaderIndex &&
     138           0 :            fGraphicStateIndex == cur.fGraphicStateIndex &&
     139           0 :            fMatrix == cur.fMatrix &&
     140           0 :            fClipStack == cur.fClipStack &&
     141           0 :            (fTextScaleX == 0 ||
     142           0 :                (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill));
     143             : }
     144             : 
     145           0 : class GraphicStackState {
     146             : public:
     147           0 :     GraphicStackState(const SkClipStack& existingClipStack,
     148             :                       SkWStream* contentStream)
     149           0 :             : fStackDepth(0),
     150           0 :               fContentStream(contentStream) {
     151           0 :         fEntries[0].fClipStack = existingClipStack;
     152           0 :     }
     153             : 
     154             :     void updateClip(const SkClipStack& clipStack,
     155             :                     const SkPoint& translation, const SkRect& bounds);
     156             :     void updateMatrix(const SkMatrix& matrix);
     157             :     void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
     158             : 
     159             :     void drainStack();
     160             : 
     161             : private:
     162             :     void push();
     163             :     void pop();
     164           0 :     SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
     165             : 
     166             :     // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
     167             :     static const int kMaxStackDepth = 12;
     168             :     SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1];
     169             :     int fStackDepth;
     170             :     SkWStream* fContentStream;
     171             : };
     172             : 
     173           0 : void GraphicStackState::drainStack() {
     174           0 :     while (fStackDepth) {
     175           0 :         pop();
     176             :     }
     177           0 : }
     178             : 
     179           0 : void GraphicStackState::push() {
     180           0 :     SkASSERT(fStackDepth < kMaxStackDepth);
     181           0 :     fContentStream->writeText("q\n");
     182           0 :     fStackDepth++;
     183           0 :     fEntries[fStackDepth] = fEntries[fStackDepth - 1];
     184           0 : }
     185             : 
     186           0 : void GraphicStackState::pop() {
     187           0 :     SkASSERT(fStackDepth > 0);
     188           0 :     fContentStream->writeText("Q\n");
     189           0 :     fStackDepth--;
     190           0 : }
     191             : 
     192             : /* Calculate an inverted path's equivalent non-inverted path, given the
     193             :  * canvas bounds.
     194             :  * outPath may alias with invPath (since this is supported by PathOps).
     195             :  */
     196           0 : static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
     197             :                                    SkPath* outPath) {
     198           0 :     SkASSERT(invPath.isInverseFillType());
     199             : 
     200           0 :     SkPath clipPath;
     201           0 :     clipPath.addRect(bounds);
     202             : 
     203           0 :     return Op(clipPath, invPath, kIntersect_SkPathOp, outPath);
     204             : }
     205             : 
     206           0 : bool apply_clip(SkClipOp op, const SkPath& u, const SkPath& v, SkPath* r)  {
     207           0 :     switch (op) {
     208             :         case SkClipOp::kDifference:
     209           0 :             return Op(u, v, kDifference_SkPathOp, r);
     210             :         case SkClipOp::kIntersect:
     211           0 :             return Op(u, v, kIntersect_SkPathOp, r);
     212             :         case SkClipOp::kUnion_deprecated:
     213           0 :             return Op(u, v, kUnion_SkPathOp, r);
     214             :         case SkClipOp::kXOR_deprecated:
     215           0 :             return Op(u, v, kXOR_SkPathOp, r);
     216             :         case SkClipOp::kReverseDifference_deprecated:
     217           0 :             return Op(u, v, kReverseDifference_SkPathOp, r);
     218             :         case SkClipOp::kReplace_deprecated:
     219           0 :             *r = v;
     220           0 :             return true;
     221             :         default:
     222           0 :             return false;
     223             :     }
     224             : }
     225             : 
     226             : /* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
     227             :  * Returns true if successful, or false if not successful.
     228             :  * If successful, the resulting clip is stored in outClipPath.
     229             :  * If not successful, outClipPath is undefined, and a fallback method
     230             :  * should be used.
     231             :  */
     232           0 : static bool get_clip_stack_path(const SkMatrix& transform,
     233             :                                 const SkClipStack& clipStack,
     234             :                                 const SkRect& bounds,
     235             :                                 SkPath* outClipPath) {
     236           0 :     outClipPath->reset();
     237           0 :     outClipPath->setFillType(SkPath::kInverseWinding_FillType);
     238             : 
     239             :     const SkClipStack::Element* clipEntry;
     240           0 :     SkClipStack::Iter iter;
     241           0 :     iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
     242           0 :     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
     243           0 :         SkPath entryPath;
     244           0 :         if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
     245           0 :             outClipPath->reset();
     246           0 :             outClipPath->setFillType(SkPath::kInverseWinding_FillType);
     247           0 :             continue;
     248             :         } else {
     249           0 :             clipEntry->asPath(&entryPath);
     250             :         }
     251           0 :         entryPath.transform(transform);
     252           0 :         if (!apply_clip(clipEntry->getOp(), *outClipPath, entryPath, outClipPath)) {
     253           0 :             return false;
     254             :         }
     255             :     }
     256             : 
     257           0 :     if (outClipPath->isInverseFillType()) {
     258             :         // The bounds are slightly outset to ensure this is correct in the
     259             :         // face of floating-point accuracy and possible SkRegion bitmap
     260             :         // approximations.
     261           0 :         SkRect clipBounds = bounds;
     262           0 :         clipBounds.outset(SK_Scalar1, SK_Scalar1);
     263           0 :         if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
     264           0 :             return false;
     265             :         }
     266             :     }
     267           0 :     return true;
     268             : }
     269             : 
     270             : // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
     271             : // graphic state stack, and the fact that we can know all the clips used
     272             : // on the page to optimize this.
     273           0 : void GraphicStackState::updateClip(const SkClipStack& clipStack,
     274             :                                    const SkPoint& translation,
     275             :                                    const SkRect& bounds) {
     276           0 :     if (clipStack == currentEntry()->fClipStack) {
     277           0 :         return;
     278             :     }
     279             : 
     280           0 :     while (fStackDepth > 0) {
     281           0 :         pop();
     282           0 :         if (clipStack == currentEntry()->fClipStack) {
     283           0 :             return;
     284             :         }
     285             :     }
     286           0 :     push();
     287             : 
     288           0 :     currentEntry()->fClipStack = clipStack;
     289             : 
     290             :     SkMatrix transform;
     291           0 :     transform.setTranslate(translation.fX, translation.fY);
     292             : 
     293           0 :     SkPath clipPath;
     294           0 :     if (get_clip_stack_path(transform, clipStack, bounds, &clipPath)) {
     295           0 :         SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream);
     296           0 :         SkPath::FillType clipFill = clipPath.getFillType();
     297             :         NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
     298             :         NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
     299           0 :         if (clipFill == SkPath::kEvenOdd_FillType) {
     300           0 :             fContentStream->writeText("W* n\n");
     301             :         } else {
     302           0 :             fContentStream->writeText("W n\n");
     303             :         }
     304             :     }
     305             :     // If Op() fails (pathological case; e.g. input values are
     306             :     // extremely large or NaN), emit no clip at all.
     307             : }
     308             : 
     309           0 : void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
     310           0 :     if (matrix == currentEntry()->fMatrix) {
     311           0 :         return;
     312             :     }
     313             : 
     314           0 :     if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
     315           0 :         SkASSERT(fStackDepth > 0);
     316           0 :         SkASSERT(fEntries[fStackDepth].fClipStack ==
     317             :                  fEntries[fStackDepth -1].fClipStack);
     318           0 :         pop();
     319             : 
     320           0 :         SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
     321             :     }
     322           0 :     if (matrix.getType() == SkMatrix::kIdentity_Mask) {
     323           0 :         return;
     324             :     }
     325             : 
     326           0 :     push();
     327           0 :     SkPDFUtils::AppendTransform(matrix, fContentStream);
     328           0 :     currentEntry()->fMatrix = matrix;
     329             : }
     330             : 
     331           0 : void GraphicStackState::updateDrawingState(const SkPDFDevice::GraphicStateEntry& state) {
     332             :     // PDF treats a shader as a color, so we only set one or the other.
     333           0 :     if (state.fShaderIndex >= 0) {
     334           0 :         if (state.fShaderIndex != currentEntry()->fShaderIndex) {
     335           0 :             SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream);
     336           0 :             currentEntry()->fShaderIndex = state.fShaderIndex;
     337             :         }
     338             :     } else {
     339           0 :         if (state.fColor != currentEntry()->fColor ||
     340           0 :                 currentEntry()->fShaderIndex >= 0) {
     341           0 :             emit_pdf_color(state.fColor, fContentStream);
     342           0 :             fContentStream->writeText("RG ");
     343           0 :             emit_pdf_color(state.fColor, fContentStream);
     344           0 :             fContentStream->writeText("rg\n");
     345           0 :             currentEntry()->fColor = state.fColor;
     346           0 :             currentEntry()->fShaderIndex = -1;
     347             :         }
     348             :     }
     349             : 
     350           0 :     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
     351           0 :         SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
     352           0 :         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
     353             :     }
     354             : 
     355           0 :     if (state.fTextScaleX) {
     356           0 :         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
     357           0 :             SkScalar pdfScale = state.fTextScaleX * 1000;
     358           0 :             SkPDFUtils::AppendScalar(pdfScale, fContentStream);
     359           0 :             fContentStream->writeText(" Tz\n");
     360           0 :             currentEntry()->fTextScaleX = state.fTextScaleX;
     361             :         }
     362           0 :         if (state.fTextFill != currentEntry()->fTextFill) {
     363             :             static_assert(SkPaint::kFill_Style == 0, "enum_must_match_value");
     364             :             static_assert(SkPaint::kStroke_Style == 1, "enum_must_match_value");
     365             :             static_assert(SkPaint::kStrokeAndFill_Style == 2, "enum_must_match_value");
     366           0 :             fContentStream->writeDecAsText(state.fTextFill);
     367           0 :             fContentStream->writeText(" Tr\n");
     368           0 :             currentEntry()->fTextFill = state.fTextFill;
     369             :         }
     370             :     }
     371           0 : }
     372             : 
     373           0 : static bool not_supported_for_layers(const SkPaint& layerPaint) {
     374             :     // PDF does not support image filters, so render them on CPU.
     375             :     // Note that this rendering is done at "screen" resolution (100dpi), not
     376             :     // printer resolution.
     377             :     // TODO: It may be possible to express some filters natively using PDF
     378             :     // to improve quality and file size (https://bug.skia.org/3043)
     379             : 
     380             :     // TODO: should we return true if there is a colorfilter?
     381           0 :     return layerPaint.getImageFilter() != nullptr;
     382             : }
     383             : 
     384           0 : SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
     385           0 :     if (layerPaint && not_supported_for_layers(*layerPaint)) {
     386             :         // need to return a raster device, which we will detect in drawDevice()
     387           0 :         return SkBitmapDevice::Create(cinfo.fInfo, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
     388             :     }
     389           0 :     SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height());
     390           0 :     return SkPDFDevice::Create(size, fRasterDpi, fDocument);
     391             : }
     392             : 
     393           0 : SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); }
     394             : 
     395             : 
     396             : 
     397             : // A helper class to automatically finish a ContentEntry at the end of a
     398             : // drawing method and maintain the state needed between set up and finish.
     399             : class ScopedContentEntry {
     400             : public:
     401           0 :     ScopedContentEntry(SkPDFDevice* device,
     402             :                        const SkClipStack& clipStack,
     403             :                        const SkMatrix& matrix,
     404             :                        const SkPaint& paint,
     405             :                        bool hasText = false)
     406           0 :         : fDevice(device)
     407             :         , fContentEntry(nullptr)
     408             :         , fBlendMode(SkBlendMode::kSrcOver)
     409           0 :         , fDstFormXObject(nullptr)
     410             :     {
     411           0 :         if (matrix.hasPerspective()) {
     412           0 :             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
     413           0 :             return;
     414             :         }
     415           0 :         fBlendMode = paint.getBlendMode();
     416           0 :         fContentEntry =
     417           0 :             fDevice->setUpContentEntry(clipStack, matrix, paint, hasText, &fDstFormXObject);
     418             :     }
     419           0 :     ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, bool hasText = false)
     420           0 :         : ScopedContentEntry(dev, dev->cs(), dev->ctm(), paint, hasText) {}
     421             : 
     422           0 :     ~ScopedContentEntry() {
     423           0 :         if (fContentEntry) {
     424           0 :             SkPath* shape = &fShape;
     425           0 :             if (shape->isEmpty()) {
     426           0 :                 shape = nullptr;
     427             :             }
     428           0 :             fDevice->finishContentEntry(fBlendMode, std::move(fDstFormXObject), shape);
     429             :         }
     430           0 :     }
     431             : 
     432           0 :     SkPDFDevice::ContentEntry* entry() { return fContentEntry; }
     433             : 
     434             :     /* Returns true when we explicitly need the shape of the drawing. */
     435           0 :     bool needShape() {
     436           0 :         switch (fBlendMode) {
     437             :             case SkBlendMode::kClear:
     438             :             case SkBlendMode::kSrc:
     439             :             case SkBlendMode::kSrcIn:
     440             :             case SkBlendMode::kSrcOut:
     441             :             case SkBlendMode::kDstIn:
     442             :             case SkBlendMode::kDstOut:
     443             :             case SkBlendMode::kSrcATop:
     444             :             case SkBlendMode::kDstATop:
     445             :             case SkBlendMode::kModulate:
     446           0 :                 return true;
     447             :             default:
     448           0 :                 return false;
     449             :         }
     450             :     }
     451             : 
     452             :     /* Returns true unless we only need the shape of the drawing. */
     453           0 :     bool needSource() {
     454           0 :         if (fBlendMode == SkBlendMode::kClear) {
     455           0 :             return false;
     456             :         }
     457           0 :         return true;
     458             :     }
     459             : 
     460             :     /* If the shape is different than the alpha component of the content, then
     461             :      * setShape should be called with the shape.  In particular, images and
     462             :      * devices have rectangular shape.
     463             :      */
     464           0 :     void setShape(const SkPath& shape) {
     465           0 :         fShape = shape;
     466           0 :     }
     467             : 
     468             : private:
     469             :     SkPDFDevice* fDevice;
     470             :     SkPDFDevice::ContentEntry* fContentEntry;
     471             :     SkBlendMode fBlendMode;
     472             :     sk_sp<SkPDFObject> fDstFormXObject;
     473             :     SkPath fShape;
     474             : };
     475             : 
     476             : ////////////////////////////////////////////////////////////////////////////////
     477             : 
     478           0 : SkPDFDevice::SkPDFDevice(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* doc, bool flip)
     479           0 :     : INHERITED(SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()),
     480           0 :                 SkSurfaceProps(0, kUnknown_SkPixelGeometry))
     481             :     , fPageSize(pageSize)
     482             :     , fRasterDpi(rasterDpi)
     483           0 :     , fDocument(doc) {
     484           0 :     SkASSERT(pageSize.width() > 0);
     485           0 :     SkASSERT(pageSize.height() > 0);
     486             : 
     487           0 :     if (flip) {
     488             :         // Skia generally uses the top left as the origin but PDF
     489             :         // natively has the origin at the bottom left. This matrix
     490             :         // corrects for that.  But that only needs to be done once, we
     491             :         // don't do it when layering.
     492           0 :         fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
     493           0 :         fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
     494             :     } else {
     495           0 :         fInitialTransform.setIdentity();
     496             :     }
     497           0 : }
     498             : 
     499           0 : SkPDFDevice::~SkPDFDevice() {
     500           0 :     this->cleanUp();
     501           0 : }
     502             : 
     503           0 : void SkPDFDevice::init() {
     504           0 :     fContentEntries.reset();
     505           0 : }
     506             : 
     507           0 : void SkPDFDevice::cleanUp() {
     508           0 :     fGraphicStateResources.unrefAll();
     509           0 :     fXObjectResources.unrefAll();
     510           0 :     fFontResources.unrefAll();
     511           0 :     fShaderResources.unrefAll();
     512           0 : }
     513             : 
     514           0 : void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
     515           0 :     if (!value) {
     516           0 :         return;
     517             :     }
     518           0 :     if (rect.isEmpty()) {
     519           0 :         if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
     520             :             SkPoint transformedPoint;
     521           0 :             this->ctm().mapXY(rect.x(), rect.y(), &transformedPoint);
     522           0 :             fNamedDestinations.emplace_back(value, transformedPoint);
     523             :         }
     524           0 :         return;
     525             :     }
     526             :     // Convert to path to handle non-90-degree rotations.
     527           0 :     SkPath path;
     528           0 :     path.addRect(rect);
     529           0 :     path.transform(this->ctm(), &path);
     530           0 :     SkPath clip;
     531           0 :     (void)this->cs().asPath(&clip);
     532           0 :     Op(clip, path, kIntersect_SkPathOp, &path);
     533             :     // PDF wants a rectangle only.
     534           0 :     SkRect transformedRect = path.getBounds();
     535           0 :     if (transformedRect.isEmpty()) {
     536           0 :         return;
     537             :     }
     538           0 :     if (!strcmp(SkAnnotationKeys::URL_Key(), key)) {
     539           0 :         fLinkToURLs.emplace_back(transformedRect, value);
     540           0 :     } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
     541           0 :         fLinkToDestinations.emplace_back(transformedRect, value);
     542             :     }
     543             : }
     544             : 
     545           0 : void SkPDFDevice::drawPaint(const SkPaint& paint) {
     546           0 :     SkPaint newPaint = paint;
     547           0 :     replace_srcmode_on_opaque_paint(&newPaint);
     548             : 
     549           0 :     newPaint.setStyle(SkPaint::kFill_Style);
     550           0 :     ScopedContentEntry content(this, newPaint);
     551           0 :     this->internalDrawPaint(newPaint, content.entry());
     552           0 : }
     553             : 
     554           0 : void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
     555             :                                     SkPDFDevice::ContentEntry* contentEntry) {
     556           0 :     if (!contentEntry) {
     557           0 :         return;
     558             :     }
     559           0 :     SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
     560           0 :                                  SkIntToScalar(this->height()));
     561             :     SkMatrix inverse;
     562           0 :     if (!contentEntry->fState.fMatrix.invert(&inverse)) {
     563           0 :         return;
     564             :     }
     565           0 :     inverse.mapRect(&bbox);
     566             : 
     567           0 :     SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
     568           0 :     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
     569           0 :                           &contentEntry->fContent);
     570             : }
     571             : 
     572           0 : void SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
     573             :                              size_t count,
     574             :                              const SkPoint* points,
     575             :                              const SkPaint& srcPaint) {
     576           0 :     SkPaint passedPaint = srcPaint;
     577           0 :     replace_srcmode_on_opaque_paint(&passedPaint);
     578             : 
     579           0 :     if (count == 0) {
     580           0 :         return;
     581             :     }
     582             : 
     583             :     // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
     584             :     // We only use this when there's a path effect because of the overhead
     585             :     // of multiple calls to setUpContentEntry it causes.
     586           0 :     if (passedPaint.getPathEffect()) {
     587           0 :         if (this->cs().isEmpty(size(*this))) {
     588           0 :             return;
     589             :         }
     590           0 :         draw_points(mode, count, points, passedPaint,
     591           0 :                     this->devClipBounds(), this->ctm(), this);
     592           0 :         return;
     593             :     }
     594             : 
     595           0 :     const SkPaint* paint = &passedPaint;
     596           0 :     SkPaint modifiedPaint;
     597             : 
     598           0 :     if (mode == SkCanvas::kPoints_PointMode &&
     599           0 :             paint->getStrokeCap() != SkPaint::kRound_Cap) {
     600           0 :         modifiedPaint = *paint;
     601           0 :         paint = &modifiedPaint;
     602           0 :         if (paint->getStrokeWidth()) {
     603             :             // PDF won't draw a single point with square/butt caps because the
     604             :             // orientation is ambiguous.  Draw a rectangle instead.
     605           0 :             modifiedPaint.setStyle(SkPaint::kFill_Style);
     606           0 :             SkScalar strokeWidth = paint->getStrokeWidth();
     607           0 :             SkScalar halfStroke = SkScalarHalf(strokeWidth);
     608           0 :             for (size_t i = 0; i < count; i++) {
     609           0 :                 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
     610           0 :                 r.inset(-halfStroke, -halfStroke);
     611           0 :                 this->drawRect(r, modifiedPaint);
     612             :             }
     613           0 :             return;
     614             :         } else {
     615           0 :             modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
     616             :         }
     617             :     }
     618             : 
     619           0 :     ScopedContentEntry content(this, *paint);
     620           0 :     if (!content.entry()) {
     621           0 :         return;
     622             :     }
     623             : 
     624           0 :     switch (mode) {
     625             :         case SkCanvas::kPolygon_PointMode:
     626           0 :             SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
     627           0 :                                &content.entry()->fContent);
     628           0 :             for (size_t i = 1; i < count; i++) {
     629           0 :                 SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
     630           0 :                                        &content.entry()->fContent);
     631             :             }
     632           0 :             SkPDFUtils::StrokePath(&content.entry()->fContent);
     633           0 :             break;
     634             :         case SkCanvas::kLines_PointMode:
     635           0 :             for (size_t i = 0; i < count/2; i++) {
     636           0 :                 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
     637           0 :                                    &content.entry()->fContent);
     638           0 :                 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
     639           0 :                                        points[i * 2 + 1].fY,
     640           0 :                                        &content.entry()->fContent);
     641           0 :                 SkPDFUtils::StrokePath(&content.entry()->fContent);
     642             :             }
     643           0 :             break;
     644             :         case SkCanvas::kPoints_PointMode:
     645           0 :             SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
     646           0 :             for (size_t i = 0; i < count; i++) {
     647           0 :                 SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
     648           0 :                                    &content.entry()->fContent);
     649           0 :                 SkPDFUtils::ClosePath(&content.entry()->fContent);
     650           0 :                 SkPDFUtils::StrokePath(&content.entry()->fContent);
     651             :             }
     652           0 :             break;
     653             :         default:
     654           0 :             SkASSERT(false);
     655             :     }
     656             : }
     657             : 
     658           0 : static sk_sp<SkPDFDict> create_link_annotation(const SkRect& translatedRect) {
     659           0 :     auto annotation = sk_make_sp<SkPDFDict>("Annot");
     660           0 :     annotation->insertName("Subtype", "Link");
     661           0 :     annotation->insertInt("F", 4);  // required by ISO 19005
     662             : 
     663           0 :     auto border = sk_make_sp<SkPDFArray>();
     664           0 :     border->reserve(3);
     665           0 :     border->appendInt(0);  // Horizontal corner radius.
     666           0 :     border->appendInt(0);  // Vertical corner radius.
     667           0 :     border->appendInt(0);  // Width, 0 = no border.
     668           0 :     annotation->insertObject("Border", std::move(border));
     669             : 
     670           0 :     auto rect = sk_make_sp<SkPDFArray>();
     671           0 :     rect->reserve(4);
     672           0 :     rect->appendScalar(translatedRect.fLeft);
     673           0 :     rect->appendScalar(translatedRect.fTop);
     674           0 :     rect->appendScalar(translatedRect.fRight);
     675           0 :     rect->appendScalar(translatedRect.fBottom);
     676           0 :     annotation->insertObject("Rect", std::move(rect));
     677             : 
     678           0 :     return annotation;
     679             : }
     680             : 
     681           0 : static sk_sp<SkPDFDict> create_link_to_url(const SkData* urlData, const SkRect& r) {
     682           0 :     sk_sp<SkPDFDict> annotation = create_link_annotation(r);
     683           0 :     SkString url(static_cast<const char *>(urlData->data()),
     684           0 :                  urlData->size() - 1);
     685           0 :     auto action = sk_make_sp<SkPDFDict>("Action");
     686           0 :     action->insertName("S", "URI");
     687           0 :     action->insertString("URI", url);
     688           0 :     annotation->insertObject("A", std::move(action));
     689           0 :     return annotation;
     690             : }
     691             : 
     692           0 : static sk_sp<SkPDFDict> create_link_named_dest(const SkData* nameData,
     693             :                                                const SkRect& r) {
     694           0 :     sk_sp<SkPDFDict> annotation = create_link_annotation(r);
     695           0 :     SkString name(static_cast<const char *>(nameData->data()),
     696           0 :                   nameData->size() - 1);
     697           0 :     annotation->insertName("Dest", name);
     698           0 :     return annotation;
     699             : }
     700             : 
     701           0 : void SkPDFDevice::drawRect(const SkRect& rect,
     702             :                            const SkPaint& srcPaint) {
     703           0 :     SkPaint paint = srcPaint;
     704           0 :     replace_srcmode_on_opaque_paint(&paint);
     705           0 :     SkRect r = rect;
     706           0 :     r.sort();
     707             : 
     708           0 :     if (paint.getPathEffect()) {
     709           0 :         if (this->cs().isEmpty(size(*this))) {
     710           0 :             return;
     711             :         }
     712           0 :         SkPath path;
     713           0 :         path.addRect(r);
     714           0 :         this->drawPath(path, paint, nullptr, true);
     715           0 :         return;
     716             :     }
     717             : 
     718           0 :     ScopedContentEntry content(this, paint);
     719           0 :     if (!content.entry()) {
     720           0 :         return;
     721             :     }
     722           0 :     SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
     723           0 :     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
     724           0 :                           &content.entry()->fContent);
     725             : }
     726             : 
     727           0 : void SkPDFDevice::drawRRect(const SkRRect& rrect,
     728             :                             const SkPaint& srcPaint) {
     729           0 :     SkPaint paint = srcPaint;
     730           0 :     replace_srcmode_on_opaque_paint(&paint);
     731           0 :     SkPath  path;
     732           0 :     path.addRRect(rrect);
     733           0 :     this->drawPath(path, paint, nullptr, true);
     734           0 : }
     735             : 
     736           0 : void SkPDFDevice::drawOval(const SkRect& oval,
     737             :                            const SkPaint& srcPaint) {
     738           0 :     SkPaint paint = srcPaint;
     739           0 :     replace_srcmode_on_opaque_paint(&paint);
     740           0 :     SkPath  path;
     741           0 :     path.addOval(oval);
     742           0 :     this->drawPath(path, paint, nullptr, true);
     743           0 : }
     744             : 
     745           0 : void SkPDFDevice::drawPath(const SkPath& origPath,
     746             :                            const SkPaint& srcPaint,
     747             :                            const SkMatrix* prePathMatrix,
     748             :                            bool pathIsMutable) {
     749           0 :     this->internalDrawPath(
     750           0 :             this->cs(), this->ctm(), origPath, srcPaint, prePathMatrix, pathIsMutable);
     751           0 : }
     752             : 
     753           0 : void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
     754             :                                    const SkMatrix& ctm,
     755             :                                    const SkPath& origPath,
     756             :                                    const SkPaint& srcPaint,
     757             :                                    const SkMatrix* prePathMatrix,
     758             :                                    bool pathIsMutable) {
     759           0 :     SkPaint paint = srcPaint;
     760           0 :     replace_srcmode_on_opaque_paint(&paint);
     761           0 :     SkPath modifiedPath;
     762           0 :     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
     763             : 
     764           0 :     SkMatrix matrix = ctm;
     765           0 :     if (prePathMatrix) {
     766           0 :         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
     767           0 :             if (!pathIsMutable) {
     768           0 :                 pathPtr = &modifiedPath;
     769           0 :                 pathIsMutable = true;
     770             :             }
     771           0 :             origPath.transform(*prePathMatrix, pathPtr);
     772             :         } else {
     773           0 :             matrix.preConcat(*prePathMatrix);
     774             :         }
     775             :     }
     776             : 
     777           0 :     if (paint.getPathEffect()) {
     778           0 :         if (clipStack.isEmpty(size(*this))) {
     779           0 :             return;
     780             :         }
     781           0 :         if (!pathIsMutable) {
     782           0 :             pathPtr = &modifiedPath;
     783           0 :             pathIsMutable = true;
     784             :         }
     785           0 :         bool fill = paint.getFillPath(origPath, pathPtr);
     786             : 
     787           0 :         SkPaint noEffectPaint(paint);
     788           0 :         noEffectPaint.setPathEffect(nullptr);
     789           0 :         if (fill) {
     790           0 :             noEffectPaint.setStyle(SkPaint::kFill_Style);
     791             :         } else {
     792           0 :             noEffectPaint.setStyle(SkPaint::kStroke_Style);
     793           0 :             noEffectPaint.setStrokeWidth(0);
     794             :         }
     795           0 :         this->internalDrawPath(clipStack, ctm, *pathPtr, noEffectPaint, nullptr, true);
     796           0 :         return;
     797             :     }
     798             : 
     799           0 :     if (this->handleInversePath(origPath, paint, pathIsMutable, prePathMatrix)) {
     800           0 :         return;
     801             :     }
     802             : 
     803           0 :     ScopedContentEntry content(this, clipStack, matrix, paint);
     804           0 :     if (!content.entry()) {
     805           0 :         return;
     806             :     }
     807           0 :     SkScalar matrixScale = matrix.mapRadius(1.0f);
     808           0 :     SkScalar tolerance = matrixScale > 0.0f ? 0.25f / matrixScale : 0.25f;
     809             :     bool consumeDegeratePathSegments =
     810           0 :            paint.getStyle() == SkPaint::kFill_Style ||
     811           0 :            (paint.getStrokeCap() != SkPaint::kRound_Cap &&
     812           0 :             paint.getStrokeCap() != SkPaint::kSquare_Cap);
     813           0 :     SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
     814             :                          consumeDegeratePathSegments,
     815           0 :                          &content.entry()->fContent,
     816           0 :                          tolerance);
     817           0 :     SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
     818           0 :                           &content.entry()->fContent);
     819             : }
     820             : 
     821             : 
     822           0 : void SkPDFDevice::drawImageRect(const SkImage* image,
     823             :                                 const SkRect* src,
     824             :                                 const SkRect& dst,
     825             :                                 const SkPaint& srcPaint,
     826             :                                 SkCanvas::SrcRectConstraint) {
     827           0 :     if (!image) {
     828           0 :         return;
     829             :     }
     830           0 :     SkIRect bounds = image->bounds();
     831           0 :     SkPaint paint = srcPaint;
     832           0 :     if (image->isOpaque()) {
     833           0 :         replace_srcmode_on_opaque_paint(&paint);
     834             :     }
     835           0 :     SkRect srcRect = src ? *src : SkRect::Make(bounds);
     836             :     SkMatrix transform;
     837           0 :     transform.setRectToRect(srcRect, dst, SkMatrix::kFill_ScaleToFit);
     838           0 :     if (src) {
     839           0 :         if (!srcRect.intersect(SkRect::Make(bounds))) {
     840           0 :             return;
     841             :         }
     842           0 :         srcRect.roundOut(&bounds);
     843           0 :         transform.preTranslate(SkIntToScalar(bounds.x()),
     844           0 :                                SkIntToScalar(bounds.y()));
     845             :     }
     846           0 :     SkImageSubset imageSubset(sk_ref_sp(const_cast<SkImage*>(image)), bounds);
     847           0 :     if (!imageSubset.isValid()) {
     848           0 :         return;
     849             :     }
     850           0 :     transform.postConcat(this->ctm());
     851           0 :     this->internalDrawImage(transform, this->cs(), std::move(imageSubset), paint);
     852             : }
     853             : 
     854           0 : void SkPDFDevice::drawBitmapRect(const SkBitmap& bitmap,
     855             :                                  const SkRect* src,
     856             :                                  const SkRect& dst,
     857             :                                  const SkPaint& srcPaint,
     858             :                                  SkCanvas::SrcRectConstraint) {
     859           0 :     if (bitmap.drawsNothing()) {
     860           0 :         return;
     861             :     }
     862           0 :     SkIRect bounds = bitmap.bounds();
     863           0 :     SkPaint paint = srcPaint;
     864           0 :     if (bitmap.isOpaque()) {
     865           0 :         replace_srcmode_on_opaque_paint(&paint);
     866             :     }
     867           0 :     SkRect srcRect = src ? *src : SkRect::Make(bounds);
     868             :     SkMatrix transform;
     869           0 :     transform.setRectToRect(srcRect, dst, SkMatrix::kFill_ScaleToFit);
     870           0 :     if (src) {
     871           0 :         if (!srcRect.intersect(SkRect::Make(bounds))) {
     872           0 :             return;
     873             :         }
     874           0 :         srcRect.roundOut(&bounds);
     875           0 :         transform.preTranslate(SkIntToScalar(bounds.x()),
     876           0 :                                SkIntToScalar(bounds.y()));
     877             :     }
     878           0 :     SkBitmap bitmapSubset;
     879           0 :     if (!bitmap.extractSubset(&bitmapSubset, bounds)) {
     880           0 :         return;
     881             :     }
     882           0 :     SkImageSubset imageSubset = make_image_subset(bitmapSubset);
     883           0 :     if (!imageSubset.isValid()) {
     884           0 :         return;
     885             :     }
     886           0 :     transform.postConcat(this->ctm());
     887           0 :     this->internalDrawImage(transform, this->cs(), std::move(imageSubset), paint);
     888             : }
     889             : 
     890           0 : void SkPDFDevice::drawBitmap(const SkBitmap& bitmap,
     891             :                              const SkMatrix& matrix,
     892             :                              const SkPaint& srcPaint) {
     893           0 :     if (bitmap.drawsNothing() || this->cs().isEmpty(size(*this))) {
     894           0 :         return;
     895             :     }
     896           0 :     SkPaint paint = srcPaint;
     897           0 :     if (bitmap.isOpaque()) {
     898           0 :         replace_srcmode_on_opaque_paint(&paint);
     899             :     }
     900           0 :     SkImageSubset imageSubset = make_image_subset(bitmap);
     901           0 :     if (!imageSubset.isValid()) {
     902           0 :         return;
     903             :     }
     904           0 :     SkMatrix transform = matrix;
     905           0 :     transform.postConcat(this->ctm());
     906           0 :     this->internalDrawImage(transform, this->cs(), std::move(imageSubset), paint);
     907             : }
     908             : 
     909           0 : void SkPDFDevice::drawSprite(const SkBitmap& bitmap,
     910             :                              int x,
     911             :                              int y,
     912             :                              const SkPaint& srcPaint) {
     913           0 :     if (bitmap.drawsNothing() || this->cs().isEmpty(size(*this))) {
     914           0 :         return;
     915             :     }
     916           0 :     SkPaint paint = srcPaint;
     917           0 :     if (bitmap.isOpaque()) {
     918           0 :         replace_srcmode_on_opaque_paint(&paint);
     919             :     }
     920           0 :     SkImageSubset imageSubset = make_image_subset(bitmap);
     921           0 :     if (!imageSubset.isValid()) {
     922           0 :         return;
     923             :     }
     924           0 :     SkMatrix transform = SkMatrix::MakeTrans(SkIntToScalar(x), SkIntToScalar(y));
     925           0 :     this->internalDrawImage(transform, this->cs(), std::move(imageSubset), paint);
     926             : }
     927             : 
     928           0 : void SkPDFDevice::drawImage(const SkImage* image,
     929             :                             SkScalar x,
     930             :                             SkScalar y,
     931             :                             const SkPaint& srcPaint) {
     932           0 :     SkPaint paint = srcPaint;
     933           0 :     if (!image) {
     934           0 :         return;
     935             :     }
     936           0 :     if (image->isOpaque()) {
     937           0 :         replace_srcmode_on_opaque_paint(&paint);
     938             :     }
     939           0 :     SkImageSubset imageSubset(sk_ref_sp(const_cast<SkImage*>(image)));
     940           0 :     if (!imageSubset.isValid()) {
     941           0 :         return;
     942             :     }
     943           0 :     SkMatrix transform = SkMatrix::MakeTrans(x, y);
     944           0 :     transform.postConcat(this->ctm());
     945           0 :     this->internalDrawImage(transform, this->cs(), std::move(imageSubset), paint);
     946             : }
     947             : 
     948             : namespace {
     949             : class GlyphPositioner {
     950             : public:
     951           0 :     GlyphPositioner(SkDynamicMemoryWStream* content,
     952             :                     SkScalar textSkewX,
     953             :                     bool wideChars,
     954             :                     bool defaultPositioning,
     955             :                     SkPoint origin)
     956           0 :         : fContent(content)
     957             :         , fCurrentMatrixOrigin(origin)
     958             :         , fTextSkewX(textSkewX)
     959             :         , fWideChars(wideChars)
     960           0 :         , fDefaultPositioning(defaultPositioning) {
     961           0 :     }
     962           0 :     ~GlyphPositioner() { this->flush(); }
     963           0 :     void flush() {
     964           0 :         if (fInText) {
     965           0 :             fContent->writeText("> Tj\n");
     966           0 :             fInText = false;
     967             :         }
     968           0 :     }
     969           0 :     void writeGlyph(SkPoint xy,
     970             :                     SkScalar advanceWidth,
     971             :                     uint16_t glyph) {
     972           0 :         if (!fInitialized) {
     973             :             // Flip the text about the x-axis to account for origin swap and include
     974             :             // the passed parameters.
     975           0 :             fContent->writeText("1 0 ");
     976           0 :             SkPDFUtils::AppendScalar(-fTextSkewX, fContent);
     977           0 :             fContent->writeText(" -1 ");
     978           0 :             SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent);
     979           0 :             fContent->writeText(" ");
     980           0 :             SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent);
     981           0 :             fContent->writeText(" Tm\n");
     982           0 :             fCurrentMatrixOrigin.set(0.0f, 0.0f);
     983           0 :             fInitialized = true;
     984             :         }
     985             : #ifdef SK_BUILD_FOR_WIN
     986             :         const bool kAlwaysPosition = true;
     987             : #else
     988           0 :         const bool kAlwaysPosition = false;
     989             : #endif
     990           0 :         if (!fDefaultPositioning) {
     991           0 :             SkPoint position = xy - fCurrentMatrixOrigin;
     992           0 :             if (kAlwaysPosition || position != SkPoint{fXAdvance, 0}) {
     993           0 :                 this->flush();
     994           0 :                 SkPDFUtils::AppendScalar(position.x(), fContent);
     995           0 :                 fContent->writeText(" ");
     996           0 :                 SkPDFUtils::AppendScalar(-position.y(), fContent);
     997           0 :                 fContent->writeText(" Td ");
     998           0 :                 fCurrentMatrixOrigin = xy;
     999           0 :                 fXAdvance = 0;
    1000             :             }
    1001           0 :             fXAdvance += advanceWidth;
    1002             :         }
    1003           0 :         if (!fInText) {
    1004           0 :             fContent->writeText("<");
    1005           0 :             fInText = true;
    1006             :         }
    1007           0 :         if (fWideChars) {
    1008           0 :             SkPDFUtils::WriteUInt16BE(fContent, glyph);
    1009             :         } else {
    1010           0 :             SkASSERT(0 == glyph >> 8);
    1011           0 :             SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph));
    1012             :         }
    1013           0 :     }
    1014             : 
    1015             : private:
    1016             :     SkDynamicMemoryWStream* fContent;
    1017             :     SkPoint fCurrentMatrixOrigin;
    1018             :     SkScalar fXAdvance = 0.0f;
    1019             :     SkScalar fTextSkewX;
    1020             :     bool fWideChars;
    1021             :     bool fInText = false;
    1022             :     bool fInitialized = false;
    1023             :     const bool fDefaultPositioning;
    1024             : };
    1025             : 
    1026             : /** Given the m-to-n glyph-to-character mapping data (as returned by
    1027             :     harfbuzz), iterate over the clusters. */
    1028             : class Clusterator {
    1029             : public:
    1030           0 :     Clusterator() : fClusters(nullptr), fUtf8Text(nullptr), fGlyphCount(0), fTextByteLength(0) {}
    1031           0 :     explicit Clusterator(uint32_t glyphCount)
    1032           0 :         : fClusters(nullptr)
    1033             :         , fUtf8Text(nullptr)
    1034             :         , fGlyphCount(glyphCount)
    1035           0 :         , fTextByteLength(0) {}
    1036             :     // The clusters[] array is an array of offsets into utf8Text[],
    1037             :     // one offset for each glyph.  See SkTextBlobBuilder for more info.
    1038           0 :     Clusterator(const uint32_t* clusters,
    1039             :                 const char* utf8Text,
    1040             :                 uint32_t glyphCount,
    1041             :                 uint32_t textByteLength)
    1042           0 :         : fClusters(clusters)
    1043             :         , fUtf8Text(utf8Text)
    1044             :         , fGlyphCount(glyphCount)
    1045           0 :         , fTextByteLength(textByteLength) {
    1046             :         // This is a cheap heuristic for /ReversedChars which seems to
    1047             :         // work for clusters produced by HarfBuzz, which either
    1048             :         // increase from zero (LTR) or decrease to zero (RTL).
    1049             :         // "ReversedChars" is how PDF deals with RTL text.
    1050           0 :         fReversedChars =
    1051           0 :             fUtf8Text && fClusters && fGlyphCount && fClusters[0] != 0;
    1052           0 :     }
    1053             :     struct Cluster {
    1054             :         const char* fUtf8Text;
    1055             :         uint32_t fTextByteLength;
    1056             :         uint32_t fGlyphIndex;
    1057             :         uint32_t fGlyphCount;
    1058           0 :         explicit operator bool() const { return fGlyphCount != 0; }
    1059             :     };
    1060             :     // True if this looks like right-to-left text.
    1061           0 :     bool reversedChars() const { return fReversedChars; }
    1062           0 :     Cluster next() {
    1063           0 :         if ((!fUtf8Text || !fClusters) && fGlyphCount) {
    1064             :             // These glyphs have no text.  Treat as one "cluster".
    1065           0 :             uint32_t glyphCount = fGlyphCount;
    1066           0 :             fGlyphCount = 0;
    1067           0 :             return Cluster{nullptr, 0, 0, glyphCount};
    1068             :         }
    1069           0 :         if (fGlyphCount == 0 || fTextByteLength == 0) {
    1070           0 :             return Cluster{nullptr, 0, 0, 0};  // empty
    1071             :         }
    1072           0 :         SkASSERT(fUtf8Text);
    1073           0 :         SkASSERT(fClusters);
    1074           0 :         uint32_t cluster = fClusters[0];
    1075           0 :         if (cluster >= fTextByteLength) {
    1076           0 :             return Cluster{nullptr, 0, 0, 0};  // bad input.
    1077             :         }
    1078           0 :         uint32_t glyphsInCluster = 1;
    1079           0 :         while (glyphsInCluster < fGlyphCount &&
    1080           0 :                fClusters[glyphsInCluster] == cluster) {
    1081           0 :             ++glyphsInCluster;
    1082             :         }
    1083           0 :         SkASSERT(glyphsInCluster <= fGlyphCount);
    1084           0 :         uint32_t textLength = 0;
    1085           0 :         if (glyphsInCluster == fGlyphCount) {
    1086             :             // consumes rest of glyphs and rest of text
    1087           0 :             if (kInvalidCluster == fPreviousCluster) { // LTR text or single cluster
    1088           0 :                 textLength = fTextByteLength - cluster;
    1089             :             } else { // RTL text; last cluster.
    1090           0 :                 SkASSERT(fPreviousCluster < fTextByteLength);
    1091           0 :                 if (fPreviousCluster <= cluster) {  // bad input.
    1092           0 :                     return Cluster{nullptr, 0, 0, 0};
    1093             :                 }
    1094           0 :                 textLength = fPreviousCluster - cluster;
    1095             :             }
    1096           0 :             fGlyphCount = 0;
    1097           0 :             return Cluster{fUtf8Text + cluster,
    1098             :                            textLength,
    1099           0 :                            fGlyphIndex,
    1100           0 :                            glyphsInCluster};
    1101             :         }
    1102           0 :         SkASSERT(glyphsInCluster < fGlyphCount);
    1103           0 :         uint32_t nextCluster = fClusters[glyphsInCluster];
    1104           0 :         if (nextCluster >= fTextByteLength) {
    1105           0 :             return Cluster{nullptr, 0, 0, 0};  // bad input.
    1106             :         }
    1107           0 :         if (nextCluster > cluster) { // LTR text
    1108           0 :             if (kInvalidCluster != fPreviousCluster) {
    1109           0 :                 return Cluster{nullptr, 0, 0, 0};  // bad input.
    1110             :             }
    1111           0 :             textLength = nextCluster - cluster;
    1112             :         } else { // RTL text
    1113           0 :             SkASSERT(nextCluster < cluster);
    1114           0 :             if (kInvalidCluster == fPreviousCluster) { // first cluster
    1115           0 :                 textLength = fTextByteLength - cluster;
    1116             :             } else { // later cluster
    1117           0 :                 if (fPreviousCluster <= cluster) {
    1118           0 :                     return Cluster{nullptr, 0, 0, 0}; // bad input.
    1119             :                 }
    1120           0 :                 textLength = fPreviousCluster - cluster;
    1121             :             }
    1122           0 :             fPreviousCluster = cluster;
    1123             :         }
    1124           0 :         uint32_t glyphIndex = fGlyphIndex;
    1125           0 :         fGlyphCount -= glyphsInCluster;
    1126           0 :         fGlyphIndex += glyphsInCluster;
    1127           0 :         fClusters   += glyphsInCluster;
    1128           0 :         return Cluster{fUtf8Text + cluster,
    1129             :                        textLength,
    1130             :                        glyphIndex,
    1131           0 :                        glyphsInCluster};
    1132             :     }
    1133             : 
    1134             : private:
    1135             :     static constexpr uint32_t kInvalidCluster = 0xFFFFFFFF;
    1136             :     const uint32_t* fClusters;
    1137             :     const char* fUtf8Text;
    1138             :     uint32_t fGlyphCount;
    1139             :     uint32_t fTextByteLength;
    1140             :     uint32_t fGlyphIndex = 0;
    1141             :     uint32_t fPreviousCluster = kInvalidCluster;
    1142             :     bool fReversedChars = false;
    1143             : };
    1144             : 
    1145           0 : struct TextStorage {
    1146             :     SkAutoTMalloc<char> fUtf8textStorage;
    1147             :     SkAutoTMalloc<uint32_t> fClusterStorage;
    1148             :     SkAutoTMalloc<SkGlyphID> fGlyphStorage;
    1149             : };
    1150             : }  // namespace
    1151             : 
    1152             : /** Given some unicode text (as passed to drawText(), convert to
    1153             :     glyphs (via primitive shaping), while preserving
    1154             :     glyph-to-character mapping information. */
    1155           0 : static Clusterator make_clusterator(
    1156             :         const void* sourceText,
    1157             :         size_t sourceByteCount,
    1158             :         const SkPaint& paint,
    1159             :         TextStorage* storage,
    1160             :         int glyphCount) {
    1161           0 :     SkASSERT(SkPaint::kGlyphID_TextEncoding != paint.getTextEncoding());
    1162           0 :     SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
    1163           0 :     SkASSERT(glyphCount > 0);
    1164           0 :     storage->fGlyphStorage.reset(SkToSizeT(glyphCount));
    1165           0 :     (void)paint.textToGlyphs(sourceText, sourceByteCount, storage->fGlyphStorage.get());
    1166           0 :     storage->fClusterStorage.reset(SkToSizeT(glyphCount));
    1167           0 :     uint32_t* clusters = storage->fClusterStorage.get();
    1168           0 :     uint32_t utf8ByteCount = 0;
    1169           0 :     const char* utf8Text = nullptr;
    1170           0 :     switch (paint.getTextEncoding()) {
    1171             :         case SkPaint::kUTF8_TextEncoding: {
    1172           0 :             const char* txtPtr = (const char*)sourceText;
    1173           0 :             for (int i = 0; i < glyphCount; ++i) {
    1174           0 :                 clusters[i] = SkToU32(txtPtr - (const char*)sourceText);
    1175           0 :                 txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr);
    1176           0 :                 SkASSERT(txtPtr <= (const char*)sourceText + sourceByteCount);
    1177             :             }
    1178           0 :             SkASSERT(txtPtr == (const char*)sourceText + sourceByteCount);
    1179           0 :             utf8ByteCount = SkToU32(sourceByteCount);
    1180           0 :             utf8Text = (const char*)sourceText;
    1181           0 :             break;
    1182             :         }
    1183             :         case SkPaint::kUTF16_TextEncoding: {
    1184           0 :             const uint16_t* utf16ptr = (const uint16_t*)sourceText;
    1185           0 :             int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t));
    1186           0 :             utf8ByteCount = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count));
    1187           0 :             storage->fUtf8textStorage.reset(utf8ByteCount);
    1188           0 :             char* txtPtr = storage->fUtf8textStorage.get();
    1189           0 :             utf8Text = txtPtr;
    1190           0 :             int clusterIndex = 0;
    1191           0 :             while (utf16ptr < (const uint16_t*)sourceText + utf16count) {
    1192           0 :                 clusters[clusterIndex++] = SkToU32(txtPtr - utf8Text);
    1193           0 :                 SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr);
    1194           0 :                 txtPtr += SkUTF8_FromUnichar(uni, txtPtr);
    1195             :             }
    1196           0 :             SkASSERT(clusterIndex == glyphCount);
    1197           0 :             SkASSERT(txtPtr == storage->fUtf8textStorage.get() + utf8ByteCount);
    1198           0 :             SkASSERT(utf16ptr == (const uint16_t*)sourceText + utf16count);
    1199           0 :             break;
    1200             :         }
    1201             :         case SkPaint::kUTF32_TextEncoding: {
    1202           0 :             const SkUnichar* utf32 = (const SkUnichar*)sourceText;
    1203           0 :             int utf32count = SkToInt(sourceByteCount / sizeof(SkUnichar));
    1204           0 :             SkASSERT(glyphCount == utf32count);
    1205           0 :             for (int i = 0; i < utf32count; ++i) {
    1206           0 :                 utf8ByteCount += SkToU32(SkUTF8_FromUnichar(utf32[i]));
    1207             :             }
    1208           0 :             storage->fUtf8textStorage.reset(SkToSizeT(utf8ByteCount));
    1209           0 :             char* txtPtr = storage->fUtf8textStorage.get();
    1210           0 :             utf8Text = txtPtr;
    1211           0 :             for (int i = 0; i < utf32count; ++i) {
    1212           0 :                 clusters[i] = SkToU32(txtPtr - utf8Text);
    1213           0 :                 txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr);
    1214             :             }
    1215           0 :             break;
    1216             :         }
    1217             :         default:
    1218           0 :             SkDEBUGFAIL("");
    1219           0 :             break;
    1220             :     }
    1221           0 :     return Clusterator(clusters, utf8Text, SkToU32(glyphCount), utf8ByteCount);
    1222             : }
    1223             : 
    1224           0 : static SkUnichar map_glyph(const SkTDArray<SkUnichar>& glyphToUnicode, SkGlyphID glyph) {
    1225           0 :     return SkToInt(glyph) < glyphToUnicode.count() ? glyphToUnicode[SkToInt(glyph)] : -1;
    1226             : }
    1227             : 
    1228           0 : static void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) {
    1229           0 :     wStream->writeText("/");
    1230           0 :     char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kFont_ResourceType);
    1231           0 :     wStream->write(&prefix, 1);
    1232           0 :     wStream->writeDecAsText(fontIndex);
    1233           0 :     wStream->writeText(" ");
    1234           0 :     SkPDFUtils::AppendScalar(textSize, wStream);
    1235           0 :     wStream->writeText(" Tf\n");
    1236           0 : }
    1237             : 
    1238           0 : void SkPDFDevice::internalDrawText(
    1239             :         const void* sourceText, size_t sourceByteCount,
    1240             :         const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
    1241             :         SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters,
    1242             :         uint32_t textByteLength, const char* utf8Text) {
    1243           0 :     NOT_IMPLEMENTED(srcPaint.getMaskFilter() != nullptr, false);
    1244           0 :     if (srcPaint.getMaskFilter() != nullptr) {
    1245             :         // Don't pretend we support drawing MaskFilters, it makes for artifacts
    1246             :         // making text unreadable (e.g. same text twice when using CSS shadows).
    1247           0 :         return;
    1248             :     }
    1249           0 :     NOT_IMPLEMENTED(srcPaint.isVerticalText(), false);
    1250           0 :     if (srcPaint.isVerticalText()) {
    1251             :         // Don't pretend we support drawing vertical text.  It is not
    1252             :         // clear to me how to switch to "vertical writing" mode in PDF.
    1253             :         // Currently neither Chromium or Android set this flag.
    1254             :         // https://bug.skia.org/5665
    1255           0 :         return;
    1256             :     }
    1257           0 :     if (0 == sourceByteCount || !sourceText) {
    1258           0 :         return;
    1259             :     }
    1260           0 :     SkPaint paint = calculate_text_paint(srcPaint);
    1261           0 :     replace_srcmode_on_opaque_paint(&paint);
    1262           0 :     if (!paint.getTypeface()) {
    1263           0 :         paint.setTypeface(SkTypeface::MakeDefault());
    1264             :     }
    1265           0 :     SkTypeface* typeface = paint.getTypeface();
    1266           0 :     if (!typeface) {
    1267           0 :         SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n");
    1268           0 :         return;
    1269             :     }
    1270             : 
    1271             :     const SkAdvancedTypefaceMetrics* metrics =
    1272           0 :         SkPDFFont::GetMetrics(typeface, fDocument->canon());
    1273           0 :     if (!metrics) {
    1274           0 :         return;
    1275             :     }
    1276           0 :     int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr);
    1277           0 :     if (glyphCount <= 0) {
    1278           0 :         return;
    1279             :     }
    1280             : 
    1281             :     // These three heap buffers are only used in the case where no glyphs
    1282             :     // are passed to drawText() (most clients pass glyphs or a textblob).
    1283           0 :     TextStorage storage;
    1284           0 :     const SkGlyphID* glyphs = nullptr;
    1285           0 :     Clusterator clusterator;
    1286           0 :     if (textByteLength > 0) {
    1287           0 :         SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID)));
    1288           0 :         glyphs = (const SkGlyphID*)sourceText;
    1289           0 :         clusterator = Clusterator(clusters, utf8Text, SkToU32(glyphCount), textByteLength);
    1290           0 :         SkASSERT(clusters);
    1291           0 :         SkASSERT(utf8Text);
    1292           0 :         SkASSERT(srcPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
    1293           0 :         SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
    1294           0 :     } else if (SkPaint::kGlyphID_TextEncoding == srcPaint.getTextEncoding()) {
    1295           0 :         SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID)));
    1296           0 :         glyphs = (const SkGlyphID*)sourceText;
    1297           0 :         clusterator = Clusterator(SkToU32(glyphCount));
    1298           0 :         SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
    1299           0 :         SkASSERT(nullptr == clusters);
    1300           0 :         SkASSERT(nullptr == utf8Text);
    1301             :     } else {
    1302           0 :         SkASSERT(nullptr == clusters);
    1303           0 :         SkASSERT(nullptr == utf8Text);
    1304           0 :         clusterator = make_clusterator(sourceText, sourceByteCount, srcPaint,
    1305           0 :                                        &storage, glyphCount);
    1306           0 :         glyphs = storage.fGlyphStorage;
    1307             :     }
    1308           0 :     bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning);
    1309           0 :     paint.setHinting(SkPaint::kNo_Hinting);
    1310             : 
    1311             :     int emSize;
    1312           0 :     SkAutoGlyphCache glyphCache = SkPDFFont::MakeVectorCache(typeface, &emSize);
    1313             : 
    1314           0 :     SkScalar textSize = paint.getTextSize();
    1315           0 :     SkScalar advanceScale = textSize * paint.getTextScaleX() / emSize;
    1316             : 
    1317           0 :     SkPaint::Align alignment = paint.getTextAlign();
    1318           0 :     float alignmentFactor = SkPaint::kLeft_Align   == alignment ?  0.0f :
    1319           0 :                             SkPaint::kCenter_Align == alignment ? -0.5f :
    1320           0 :                             /* SkPaint::kRight_Align */           -1.0f;
    1321           0 :     if (defaultPositioning && alignment != SkPaint::kLeft_Align) {
    1322           0 :         SkScalar advance = 0;
    1323           0 :         for (int i = 0; i < glyphCount; ++i) {
    1324           0 :             advance += advanceScale * glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX;
    1325             :         }
    1326           0 :         offset.offset(alignmentFactor * advance, 0);
    1327             :     }
    1328           0 :     ScopedContentEntry content(this, paint, true);
    1329           0 :     if (!content.entry()) {
    1330           0 :         return;
    1331             :     }
    1332           0 :     SkDynamicMemoryWStream* out = &content.entry()->fContent;
    1333           0 :     const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode;
    1334             : 
    1335           0 :     out->writeText("BT\n");
    1336           0 :     SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
    1337             : 
    1338           0 :     const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
    1339             : 
    1340           0 :     bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics));
    1341           0 :     if (clusterator.reversedChars()) {
    1342           0 :         out->writeText("/ReversedChars BMC\n");
    1343             :     }
    1344           0 :     SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
    1345             :     GlyphPositioner glyphPositioner(out,
    1346             :                                     paint.getTextSkewX(),
    1347             :                                     multiByteGlyphs,
    1348             :                                     defaultPositioning,
    1349           0 :                                     offset);
    1350           0 :     SkPDFFont* font = nullptr;
    1351             : 
    1352           0 :     while (Clusterator::Cluster c = clusterator.next()) {
    1353           0 :         int index = c.fGlyphIndex;
    1354           0 :         int glyphLimit = index + c.fGlyphCount;
    1355             : 
    1356           0 :         bool actualText = false;
    1357           0 :         SK_AT_SCOPE_EXIT(if (actualText) { glyphPositioner.flush(); out->writeText("EMC\n"); } );
    1358           0 :         if (c.fUtf8Text) {  // real cluster
    1359             :             // Check if `/ActualText` needed.
    1360           0 :             const char* textPtr = c.fUtf8Text;
    1361           0 :             const char* textEnd = c.fUtf8Text + c.fTextByteLength;
    1362           0 :             SkUnichar unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
    1363           0 :             if (unichar < 0) {
    1364           0 :                 return;
    1365             :             }
    1366           0 :             if (textPtr < textEnd ||                                  // more characters left
    1367           0 :                 glyphLimit > index + 1 ||                             // toUnicode wouldn't work
    1368           0 :                 unichar != map_glyph(glyphToUnicode, glyphs[index]))  // test single Unichar map
    1369             :             {
    1370           0 :                 glyphPositioner.flush();
    1371           0 :                 out->writeText("/Span<</ActualText <");
    1372           0 :                 SkPDFUtils::WriteUTF16beHex(out, 0xFEFF);  // U+FEFF = BYTE ORDER MARK
    1373             :                 // the BOM marks this text as UTF-16BE, not PDFDocEncoding.
    1374           0 :                 SkPDFUtils::WriteUTF16beHex(out, unichar);  // first char
    1375           0 :                 while (textPtr < textEnd) {
    1376           0 :                     unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
    1377           0 :                     if (unichar < 0) {
    1378           0 :                         break;
    1379             :                     }
    1380           0 :                     SkPDFUtils::WriteUTF16beHex(out, unichar);
    1381             :                 }
    1382           0 :                 out->writeText("> >> BDC\n");  // begin marked-content sequence
    1383             :                                                // with an associated property list.
    1384           0 :                 actualText = true;
    1385             :             }
    1386             :         }
    1387           0 :         for (; index < glyphLimit; ++index) {
    1388           0 :             SkGlyphID gid = glyphs[index];
    1389           0 :             if (gid > maxGlyphID) {
    1390           0 :                 continue;
    1391             :             }
    1392           0 :             if (!font || !font->hasGlyph(gid)) {
    1393             :                 // Not yet specified font or need to switch font.
    1394           0 :                 int fontIndex = this->getFontResourceIndex(typeface, gid);
    1395             :                 // All preconditions for SkPDFFont::GetFontResource are met.
    1396           0 :                 SkASSERT(fontIndex >= 0);
    1397           0 :                 if (fontIndex < 0) {
    1398           0 :                     return;
    1399             :                 }
    1400           0 :                 glyphPositioner.flush();
    1401           0 :                 update_font(out, fontIndex, textSize);
    1402           0 :                 font = fFontResources[fontIndex];
    1403           0 :                 SkASSERT(font);  // All preconditions for SkPDFFont::GetFontResource are met.
    1404           0 :                 if (!font) {
    1405           0 :                     return;
    1406             :                 }
    1407           0 :                 SkASSERT(font->multiByteGlyphs() == multiByteGlyphs);
    1408             :             }
    1409           0 :             SkPoint xy{0, 0};
    1410           0 :             SkScalar advance{0};
    1411           0 :             if (!defaultPositioning) {
    1412           0 :                 advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
    1413             :                 xy = SkTextBlob::kFull_Positioning == positioning
    1414           0 :                    ? SkPoint{pos[2 * index], pos[2 * index + 1]}
    1415           0 :                    : SkPoint{pos[index], 0};
    1416           0 :                 if (alignment != SkPaint::kLeft_Align) {
    1417           0 :                     xy.offset(alignmentFactor * advance, 0);
    1418             :                 }
    1419             :             }
    1420           0 :             font->noteGlyphUsage(gid);
    1421           0 :             SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid);
    1422           0 :             glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
    1423             :         }
    1424           0 :     }
    1425             : }
    1426             : 
    1427           0 : void SkPDFDevice::drawText(const void* text, size_t len,
    1428             :                            SkScalar x, SkScalar y, const SkPaint& paint) {
    1429             :     this->internalDrawText(text, len, nullptr, SkTextBlob::kDefault_Positioning,
    1430           0 :                            SkPoint{x, y}, paint, nullptr, 0, nullptr);
    1431           0 : }
    1432             : 
    1433           0 : void SkPDFDevice::drawPosText(const void* text, size_t len,
    1434             :                               const SkScalar pos[], int scalarsPerPos,
    1435             :                               const SkPoint& offset, const SkPaint& paint) {
    1436           0 :     this->internalDrawText(text, len, pos, (SkTextBlob::GlyphPositioning)scalarsPerPos,
    1437           0 :                            offset, paint, nullptr, 0, nullptr);
    1438           0 : }
    1439             : 
    1440           0 : void SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
    1441             :                                const SkPaint &paint, SkDrawFilter* drawFilter) {
    1442           0 :     for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
    1443           0 :         SkPaint runPaint(paint);
    1444           0 :         it.applyFontToPaint(&runPaint);
    1445           0 :         if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
    1446           0 :             continue;
    1447             :         }
    1448           0 :         runPaint.setFlags(this->filterTextFlags(runPaint));
    1449           0 :         SkPoint offset = it.offset() + SkPoint{x, y};
    1450           0 :         this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(),
    1451           0 :                                it.pos(), it.positioning(), offset, runPaint,
    1452           0 :                                it.clusters(), it.textSize(), it.text());
    1453             :     }
    1454           0 : }
    1455             : 
    1456           0 : void SkPDFDevice::drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) {
    1457           0 :     if (this->cs().isEmpty(size(*this))) {
    1458           0 :         return;
    1459             :     }
    1460             :     // TODO: implement drawVertices
    1461             : }
    1462             : 
    1463           0 : void SkPDFDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) {
    1464           0 :     SkASSERT(!paint.getImageFilter());
    1465             : 
    1466             :     // Check if the source device is really a bitmapdevice (because that's what we returned
    1467             :     // from createDevice (likely due to an imagefilter)
    1468           0 :     SkPixmap pmap;
    1469           0 :     if (device->peekPixels(&pmap)) {
    1470           0 :         SkBitmap bitmap;
    1471           0 :         bitmap.installPixels(pmap);
    1472           0 :         this->drawSprite(bitmap, x, y, paint);
    1473           0 :         return;
    1474             :     }
    1475             : 
    1476             :     // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses.
    1477           0 :     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
    1478             : 
    1479           0 :     SkScalar scalarX = SkIntToScalar(x);
    1480           0 :     SkScalar scalarY = SkIntToScalar(y);
    1481           0 :     for (const RectWithData& l : pdfDevice->fLinkToURLs) {
    1482           0 :         SkRect r = l.rect.makeOffset(scalarX, scalarY);
    1483           0 :         fLinkToURLs.emplace_back(r, l.data.get());
    1484             :     }
    1485           0 :     for (const RectWithData& l : pdfDevice->fLinkToDestinations) {
    1486           0 :         SkRect r = l.rect.makeOffset(scalarX, scalarY);
    1487           0 :         fLinkToDestinations.emplace_back(r, l.data.get());
    1488             :     }
    1489           0 :     for (const NamedDestination& d : pdfDevice->fNamedDestinations) {
    1490           0 :         SkPoint p = d.point + SkPoint::Make(scalarX, scalarY);
    1491           0 :         fNamedDestinations.emplace_back(d.nameData.get(), p);
    1492             :     }
    1493             : 
    1494           0 :     if (pdfDevice->isContentEmpty()) {
    1495           0 :         return;
    1496             :     }
    1497             : 
    1498           0 :     SkMatrix matrix = SkMatrix::MakeTrans(SkIntToScalar(x), SkIntToScalar(y));
    1499           0 :     ScopedContentEntry content(this, this->cs(), matrix, paint);
    1500           0 :     if (!content.entry()) {
    1501           0 :         return;
    1502             :     }
    1503           0 :     if (content.needShape()) {
    1504           0 :         SkPath shape;
    1505           0 :         shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
    1506           0 :                                        SkIntToScalar(device->width()),
    1507           0 :                                        SkIntToScalar(device->height())));
    1508           0 :         content.setShape(shape);
    1509             :     }
    1510           0 :     if (!content.needSource()) {
    1511           0 :         return;
    1512             :     }
    1513             : 
    1514           0 :     sk_sp<SkPDFObject> xObject = pdfDevice->makeFormXObjectFromDevice();
    1515           0 :     SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
    1516           0 :                                 &content.entry()->fContent);
    1517             : }
    1518             : 
    1519           0 : sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
    1520           0 :     return SkSurface::MakeRaster(info, &props);
    1521             : }
    1522             : 
    1523             : 
    1524           0 : sk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() const {
    1525           0 :     SkTDArray<SkPDFObject*> fonts;
    1526           0 :     fonts.setReserve(fFontResources.count());
    1527           0 :     for (SkPDFFont* font : fFontResources) {
    1528           0 :         fonts.push(font);
    1529             :     }
    1530             :     return SkPDFResourceDict::Make(
    1531             :             &fGraphicStateResources,
    1532             :             &fShaderResources,
    1533             :             &fXObjectResources,
    1534           0 :             &fonts);
    1535             : }
    1536             : 
    1537           0 : sk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const {
    1538           0 :     auto mediaBox = sk_make_sp<SkPDFArray>();
    1539           0 :     mediaBox->reserve(4);
    1540           0 :     mediaBox->appendInt(0);
    1541           0 :     mediaBox->appendInt(0);
    1542           0 :     mediaBox->appendInt(fPageSize.width());
    1543           0 :     mediaBox->appendInt(fPageSize.height());
    1544           0 :     return mediaBox;
    1545             : }
    1546             : 
    1547           0 : std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const {
    1548           0 :     SkDynamicMemoryWStream buffer;
    1549           0 :     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
    1550           0 :         SkPDFUtils::AppendTransform(fInitialTransform, &buffer);
    1551             :     }
    1552             : 
    1553           0 :     GraphicStackState gsState(fExistingClipStack, &buffer);
    1554           0 :     for (const auto& entry : fContentEntries) {
    1555           0 :         gsState.updateClip(entry.fState.fClipStack,
    1556           0 :                 {0, 0}, SkRect::Make(size(*this)));
    1557           0 :         gsState.updateMatrix(entry.fState.fMatrix);
    1558           0 :         gsState.updateDrawingState(entry.fState);
    1559             : 
    1560           0 :         entry.fContent.writeToStream(&buffer);
    1561             :     }
    1562           0 :     gsState.drainStack();
    1563           0 :     if (buffer.bytesWritten() > 0) {
    1564           0 :         return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream());
    1565             :     } else {
    1566           0 :         return skstd::make_unique<SkMemoryStream>();
    1567             :     }
    1568             : }
    1569             : 
    1570             : /* Draws an inverse filled path by using Path Ops to compute the positive
    1571             :  * inverse using the current clip as the inverse bounds.
    1572             :  * Return true if this was an inverse path and was properly handled,
    1573             :  * otherwise returns false and the normal drawing routine should continue,
    1574             :  * either as a (incorrect) fallback or because the path was not inverse
    1575             :  * in the first place.
    1576             :  */
    1577           0 : bool SkPDFDevice::handleInversePath(const SkPath& origPath,
    1578             :                                     const SkPaint& paint, bool pathIsMutable,
    1579             :                                     const SkMatrix* prePathMatrix) {
    1580           0 :     if (!origPath.isInverseFillType()) {
    1581           0 :         return false;
    1582             :     }
    1583             : 
    1584           0 :     if (this->cs().isEmpty(size(*this))) {
    1585           0 :         return false;
    1586             :     }
    1587             : 
    1588           0 :     SkPath modifiedPath;
    1589           0 :     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
    1590           0 :     SkPaint noInversePaint(paint);
    1591             : 
    1592             :     // Merge stroking operations into final path.
    1593           0 :     if (SkPaint::kStroke_Style == paint.getStyle() ||
    1594           0 :         SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
    1595           0 :         bool doFillPath = paint.getFillPath(origPath, &modifiedPath);
    1596           0 :         if (doFillPath) {
    1597           0 :             noInversePaint.setStyle(SkPaint::kFill_Style);
    1598           0 :             noInversePaint.setStrokeWidth(0);
    1599           0 :             pathPtr = &modifiedPath;
    1600             :         } else {
    1601             :             // To be consistent with the raster output, hairline strokes
    1602             :             // are rendered as non-inverted.
    1603           0 :             modifiedPath.toggleInverseFillType();
    1604           0 :             this->drawPath(modifiedPath, paint, nullptr, true);
    1605           0 :             return true;
    1606             :         }
    1607             :     }
    1608             : 
    1609             :     // Get bounds of clip in current transform space
    1610             :     // (clip bounds are given in device space).
    1611             :     SkMatrix transformInverse;
    1612           0 :     SkMatrix totalMatrix = this->ctm();
    1613           0 :     if (prePathMatrix) {
    1614           0 :         totalMatrix.preConcat(*prePathMatrix);
    1615             :     }
    1616           0 :     if (!totalMatrix.invert(&transformInverse)) {
    1617           0 :         return false;
    1618             :     }
    1619           0 :     SkRect bounds = this->cs().bounds(size(*this));
    1620           0 :     transformInverse.mapRect(&bounds);
    1621             : 
    1622             :     // Extend the bounds by the line width (plus some padding)
    1623             :     // so the edge doesn't cause a visible stroke.
    1624           0 :     bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
    1625           0 :                   paint.getStrokeWidth() + SK_Scalar1);
    1626             : 
    1627           0 :     if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
    1628           0 :         return false;
    1629             :     }
    1630             : 
    1631           0 :     this->drawPath(modifiedPath, noInversePaint, prePathMatrix, true);
    1632           0 :     return true;
    1633             : }
    1634             : 
    1635           0 : void SkPDFDevice::appendAnnotations(SkPDFArray* array) const {
    1636           0 :     array->reserve(fLinkToURLs.count() + fLinkToDestinations.count());
    1637           0 :     for (const RectWithData& rectWithURL : fLinkToURLs) {
    1638             :         SkRect r;
    1639           0 :         fInitialTransform.mapRect(&r, rectWithURL.rect);
    1640           0 :         array->appendObject(create_link_to_url(rectWithURL.data.get(), r));
    1641             :     }
    1642           0 :     for (const RectWithData& linkToDestination : fLinkToDestinations) {
    1643             :         SkRect r;
    1644           0 :         fInitialTransform.mapRect(&r, linkToDestination.rect);
    1645           0 :         array->appendObject(
    1646           0 :                 create_link_named_dest(linkToDestination.data.get(), r));
    1647             :     }
    1648           0 : }
    1649             : 
    1650           0 : void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
    1651           0 :     for (const NamedDestination& dest : fNamedDestinations) {
    1652           0 :         auto pdfDest = sk_make_sp<SkPDFArray>();
    1653           0 :         pdfDest->reserve(5);
    1654           0 :         pdfDest->appendObjRef(sk_ref_sp(page));
    1655           0 :         pdfDest->appendName("XYZ");
    1656           0 :         SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y());
    1657           0 :         pdfDest->appendScalar(p.x());
    1658           0 :         pdfDest->appendScalar(p.y());
    1659           0 :         pdfDest->appendInt(0);  // Leave zoom unchanged
    1660           0 :         SkString name(static_cast<const char*>(dest.nameData->data()));
    1661           0 :         dict->insertObject(name, std::move(pdfDest));
    1662             :     }
    1663           0 : }
    1664             : 
    1665           0 : sk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice() {
    1666           0 :     SkMatrix inverseTransform = SkMatrix::I();
    1667           0 :     if (!fInitialTransform.isIdentity()) {
    1668           0 :         if (!fInitialTransform.invert(&inverseTransform)) {
    1669           0 :             SkDEBUGFAIL("Layer initial transform should be invertible.");
    1670           0 :             inverseTransform.reset();
    1671             :         }
    1672             :     }
    1673             :     sk_sp<SkPDFObject> xobject =
    1674           0 :         SkPDFMakeFormXObject(this->content(), this->copyMediaBox(),
    1675           0 :                              this->makeResourceDict(), inverseTransform, nullptr);
    1676             :     // We always draw the form xobjects that we create back into the device, so
    1677             :     // we simply preserve the font usage instead of pulling it out and merging
    1678             :     // it back in later.
    1679           0 :     this->cleanUp();  // Reset this device to have no content.
    1680           0 :     this->init();
    1681           0 :     return xobject;
    1682             : }
    1683             : 
    1684           0 : void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
    1685             :                                           sk_sp<SkPDFObject> mask,
    1686             :                                           const SkClipStack& clipStack,
    1687             :                                           SkBlendMode mode,
    1688             :                                           bool invertClip) {
    1689           0 :     if (!invertClip && clipStack.isEmpty(size(*this))) {
    1690           0 :         return;
    1691             :     }
    1692             : 
    1693             :     sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
    1694           0 :             std::move(mask), invertClip,
    1695           0 :             SkPDFGraphicState::kAlpha_SMaskMode, fDocument->canon());
    1696             : 
    1697           0 :     SkPaint paint;
    1698           0 :     paint.setBlendMode(mode);
    1699           0 :     ScopedContentEntry content(this, clipStack, SkMatrix::I(), paint);
    1700           0 :     if (!content.entry()) {
    1701           0 :         return;
    1702             :     }
    1703           0 :     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
    1704           0 :                                   &content.entry()->fContent);
    1705           0 :     SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
    1706             : 
    1707             :     // Call makeNoSmaskGraphicState() instead of
    1708             :     // SkPDFGraphicState::MakeNoSmaskGraphicState so that the canon
    1709             :     // can deduplicate.
    1710           0 :     sMaskGS = fDocument->canon()->makeNoSmaskGraphicState();
    1711           0 :     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
    1712           0 :                                   &content.entry()->fContent);
    1713             : }
    1714             : 
    1715           0 : SkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack& clipStack,
    1716             :                                                           const SkMatrix& matrix,
    1717             :                                                           const SkPaint& paint,
    1718             :                                                           bool hasText,
    1719             :                                                           sk_sp<SkPDFObject>* dst) {
    1720           0 :     *dst = nullptr;
    1721           0 :     SkBlendMode blendMode = paint.getBlendMode();
    1722             : 
    1723             :     // For the following modes, we want to handle source and destination
    1724             :     // separately, so make an object of what's already there.
    1725           0 :     if (blendMode == SkBlendMode::kClear       ||
    1726           0 :             blendMode == SkBlendMode::kSrc     ||
    1727           0 :             blendMode == SkBlendMode::kSrcIn   ||
    1728           0 :             blendMode == SkBlendMode::kDstIn   ||
    1729           0 :             blendMode == SkBlendMode::kSrcOut  ||
    1730           0 :             blendMode == SkBlendMode::kDstOut  ||
    1731           0 :             blendMode == SkBlendMode::kSrcATop ||
    1732           0 :             blendMode == SkBlendMode::kDstATop ||
    1733             :             blendMode == SkBlendMode::kModulate) {
    1734           0 :         if (!isContentEmpty()) {
    1735           0 :             *dst = this->makeFormXObjectFromDevice();
    1736           0 :             SkASSERT(isContentEmpty());
    1737           0 :         } else if (blendMode != SkBlendMode::kSrc &&
    1738             :                    blendMode != SkBlendMode::kSrcOut) {
    1739             :             // Except for Src and SrcOut, if there isn't anything already there,
    1740             :             // then we're done.
    1741           0 :             return nullptr;
    1742             :         }
    1743             :     }
    1744             :     // TODO(vandebo): Figure out how/if we can handle the following modes:
    1745             :     // Xor, Plus.
    1746             : 
    1747             :     // Dst xfer mode doesn't draw source at all.
    1748           0 :     if (blendMode == SkBlendMode::kDst) {
    1749           0 :         return nullptr;
    1750             :     }
    1751             : 
    1752             :     SkPDFDevice::ContentEntry* entry;
    1753           0 :     if (fContentEntries.back() && fContentEntries.back()->fContent.bytesWritten() == 0) {
    1754           0 :         entry = fContentEntries.back();
    1755           0 :     } else if (blendMode != SkBlendMode::kDstOver) {
    1756           0 :         entry = fContentEntries.emplace_back();
    1757             :     } else {
    1758           0 :         entry = fContentEntries.emplace_front();
    1759             :     }
    1760           0 :     populateGraphicStateEntryFromPaint(matrix, clipStack, paint, hasText, &entry->fState);
    1761           0 :     return entry;
    1762             : }
    1763             : 
    1764           0 : void SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
    1765             :                                      sk_sp<SkPDFObject> dst,
    1766             :                                      SkPath* shape) {
    1767           0 :     if (blendMode != SkBlendMode::kClear       &&
    1768           0 :             blendMode != SkBlendMode::kSrc     &&
    1769           0 :             blendMode != SkBlendMode::kDstOver &&
    1770           0 :             blendMode != SkBlendMode::kSrcIn   &&
    1771           0 :             blendMode != SkBlendMode::kDstIn   &&
    1772           0 :             blendMode != SkBlendMode::kSrcOut  &&
    1773           0 :             blendMode != SkBlendMode::kDstOut  &&
    1774           0 :             blendMode != SkBlendMode::kSrcATop &&
    1775           0 :             blendMode != SkBlendMode::kDstATop &&
    1776             :             blendMode != SkBlendMode::kModulate) {
    1777           0 :         SkASSERT(!dst);
    1778           0 :         return;
    1779             :     }
    1780           0 :     if (blendMode == SkBlendMode::kDstOver) {
    1781           0 :         SkASSERT(!dst);
    1782           0 :         if (fContentEntries.front()->fContent.bytesWritten() == 0) {
    1783             :             // For DstOver, an empty content entry was inserted before the rest
    1784             :             // of the content entries. If nothing was drawn, it needs to be
    1785             :             // removed.
    1786           0 :             fContentEntries.pop_front();
    1787             :         }
    1788           0 :         return;
    1789             :     }
    1790           0 :     if (!dst) {
    1791           0 :         SkASSERT(blendMode == SkBlendMode::kSrc ||
    1792             :                  blendMode == SkBlendMode::kSrcOut);
    1793           0 :         return;
    1794             :     }
    1795             : 
    1796           0 :     SkASSERT(dst);
    1797           0 :     SkASSERT(fContentEntries.count() == 1);
    1798             :     // Changing the current content into a form-xobject will destroy the clip
    1799             :     // objects which is fine since the xobject will already be clipped. However
    1800             :     // if source has shape, we need to clip it too, so a copy of the clip is
    1801             :     // saved.
    1802             : 
    1803           0 :     SkClipStack clipStack = fContentEntries.front()->fState.fClipStack;
    1804             : 
    1805           0 :     SkPaint stockPaint;
    1806             : 
    1807           0 :     sk_sp<SkPDFObject> srcFormXObject;
    1808           0 :     if (isContentEmpty()) {
    1809             :         // If nothing was drawn and there's no shape, then the draw was a
    1810             :         // no-op, but dst needs to be restored for that to be true.
    1811             :         // If there is shape, then an empty source with Src, SrcIn, SrcOut,
    1812             :         // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
    1813             :         // reduces to Dst.
    1814           0 :         if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
    1815             :                 blendMode == SkBlendMode::kSrcATop) {
    1816           0 :             ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
    1817             :             // TODO: addXObjectResource take sk_sp
    1818           0 :             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()),
    1819           0 :                                         &content.entry()->fContent);
    1820           0 :             return;
    1821             :         } else {
    1822           0 :             blendMode = SkBlendMode::kClear;
    1823             :         }
    1824             :     } else {
    1825           0 :         SkASSERT(fContentEntries.count() == 1);
    1826           0 :         srcFormXObject = this->makeFormXObjectFromDevice();
    1827             :     }
    1828             : 
    1829             :     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
    1830             :     // without alpha.
    1831           0 :     if (blendMode == SkBlendMode::kSrcATop) {
    1832             :         // TODO(vandebo): In order to properly support SrcATop we have to track
    1833             :         // the shape of what's been drawn at all times. It's the intersection of
    1834             :         // the non-transparent parts of the device and the outlines (shape) of
    1835             :         // all images and devices drawn.
    1836           0 :         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
    1837           0 :                                 fExistingClipStack, SkBlendMode::kSrcOver, true);
    1838             :     } else {
    1839           0 :         if (shape != nullptr) {
    1840             :             // Draw shape into a form-xobject.
    1841           0 :             SkPaint filledPaint;
    1842           0 :             filledPaint.setColor(SK_ColorBLACK);
    1843           0 :             filledPaint.setStyle(SkPaint::kFill_Style);
    1844           0 :             this->internalDrawPath(clipStack, SkMatrix::I(), *shape, filledPaint, nullptr, true);
    1845           0 :             this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()),
    1846           0 :                                           this->makeFormXObjectFromDevice(),
    1847             :                                           fExistingClipStack,
    1848           0 :                                           SkBlendMode::kSrcOver, true);
    1849             :         } else {
    1850           0 :             this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()),
    1851             :                                           srcFormXObject,
    1852             :                                           fExistingClipStack,
    1853           0 :                                           SkBlendMode::kSrcOver, true);
    1854             :         }
    1855             :     }
    1856             : 
    1857           0 :     if (blendMode == SkBlendMode::kClear) {
    1858           0 :         return;
    1859           0 :     } else if (blendMode == SkBlendMode::kSrc ||
    1860             :             blendMode == SkBlendMode::kDstATop) {
    1861           0 :         ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
    1862           0 :         if (content.entry()) {
    1863           0 :             SkPDFUtils::DrawFormXObject(
    1864             :                     this->addXObjectResource(srcFormXObject.get()),
    1865           0 :                     &content.entry()->fContent);
    1866             :         }
    1867           0 :         if (blendMode == SkBlendMode::kSrc) {
    1868           0 :             return;
    1869           0 :         }
    1870           0 :     } else if (blendMode == SkBlendMode::kSrcATop) {
    1871             :         ScopedContentEntry content(this, fExistingClipStack,
    1872           0 :                                    SkMatrix::I(), stockPaint);
    1873           0 :         if (content.entry()) {
    1874           0 :             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()),
    1875           0 :                                         &content.entry()->fContent);
    1876             :         }
    1877             :     }
    1878             : 
    1879           0 :     SkASSERT(blendMode == SkBlendMode::kSrcIn   ||
    1880             :              blendMode == SkBlendMode::kDstIn   ||
    1881             :              blendMode == SkBlendMode::kSrcOut  ||
    1882             :              blendMode == SkBlendMode::kDstOut  ||
    1883             :              blendMode == SkBlendMode::kSrcATop ||
    1884             :              blendMode == SkBlendMode::kDstATop ||
    1885             :              blendMode == SkBlendMode::kModulate);
    1886             : 
    1887           0 :     if (blendMode == SkBlendMode::kSrcIn ||
    1888           0 :             blendMode == SkBlendMode::kSrcOut ||
    1889             :             blendMode == SkBlendMode::kSrcATop) {
    1890           0 :         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
    1891           0 :                                 std::move(dst),
    1892             :                                 fExistingClipStack,
    1893             :                                 SkBlendMode::kSrcOver,
    1894           0 :                                 blendMode == SkBlendMode::kSrcOut);
    1895           0 :         return;
    1896             :     } else {
    1897           0 :         SkBlendMode mode = SkBlendMode::kSrcOver;
    1898           0 :         int resourceID = addXObjectResource(dst.get());
    1899           0 :         if (blendMode == SkBlendMode::kModulate) {
    1900           0 :             drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
    1901           0 :                                     std::move(dst), fExistingClipStack,
    1902           0 :                                     SkBlendMode::kSrcOver, false);
    1903           0 :             mode = SkBlendMode::kMultiply;
    1904             :         }
    1905           0 :         drawFormXObjectWithMask(resourceID, std::move(srcFormXObject),
    1906             :                                 fExistingClipStack, mode,
    1907           0 :                                 blendMode == SkBlendMode::kDstOut);
    1908           0 :         return;
    1909             :     }
    1910             : }
    1911             : 
    1912           0 : bool SkPDFDevice::isContentEmpty() {
    1913           0 :     if (!fContentEntries.front() || fContentEntries.front()->fContent.bytesWritten() == 0) {
    1914           0 :         SkASSERT(fContentEntries.count() <= 1);
    1915           0 :         return true;
    1916             :     }
    1917           0 :     return false;
    1918             : }
    1919             : 
    1920           0 : void SkPDFDevice::populateGraphicStateEntryFromPaint(
    1921             :         const SkMatrix& matrix,
    1922             :         const SkClipStack& clipStack,
    1923             :         const SkPaint& paint,
    1924             :         bool hasText,
    1925             :         SkPDFDevice::GraphicStateEntry* entry) {
    1926           0 :     NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
    1927           0 :     NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
    1928           0 :     NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
    1929             : 
    1930           0 :     entry->fMatrix = matrix;
    1931           0 :     entry->fClipStack = clipStack;
    1932           0 :     entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
    1933           0 :     entry->fShaderIndex = -1;
    1934             : 
    1935             :     // PDF treats a shader as a color, so we only set one or the other.
    1936           0 :     sk_sp<SkPDFObject> pdfShader;
    1937           0 :     SkShader* shader = paint.getShader();
    1938           0 :     SkColor color = paint.getColor();
    1939           0 :     if (shader) {
    1940           0 :         if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
    1941             :             // We don't have to set a shader just for a color.
    1942             :             SkShader::GradientInfo gradientInfo;
    1943           0 :             SkColor gradientColor = SK_ColorBLACK;
    1944           0 :             gradientInfo.fColors = &gradientColor;
    1945           0 :             gradientInfo.fColorOffsets = nullptr;
    1946           0 :             gradientInfo.fColorCount = 1;
    1947           0 :             SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
    1948           0 :             entry->fColor = SkColorSetA(gradientColor, 0xFF);
    1949           0 :             color = gradientColor;
    1950             :         } else {
    1951             :             // PDF positions patterns relative to the initial transform, so
    1952             :             // we need to apply the current transform to the shader parameters.
    1953           0 :             SkMatrix transform = matrix;
    1954           0 :             transform.postConcat(fInitialTransform);
    1955             : 
    1956             :             // PDF doesn't support kClamp_TileMode, so we simulate it by making
    1957             :             // a pattern the size of the current clip.
    1958           0 :             SkRect clipStackBounds = clipStack.bounds(size(*this));
    1959             : 
    1960             :             // We need to apply the initial transform to bounds in order to get
    1961             :             // bounds in a consistent coordinate system.
    1962           0 :             fInitialTransform.mapRect(&clipStackBounds);
    1963             :             SkIRect bounds;
    1964           0 :             clipStackBounds.roundOut(&bounds);
    1965             : 
    1966             :             SkScalar rasterScale =
    1967           0 :                     SkIntToScalar(fRasterDpi) / DPI_FOR_RASTER_SCALE_ONE;
    1968           0 :             pdfShader = SkPDFShader::GetPDFShader(
    1969           0 :                     fDocument, fRasterDpi, shader, transform, bounds, rasterScale);
    1970             : 
    1971           0 :             if (pdfShader.get()) {
    1972             :                 // pdfShader has been canonicalized so we can directly compare
    1973             :                 // pointers.
    1974           0 :                 int resourceIndex = fShaderResources.find(pdfShader.get());
    1975           0 :                 if (resourceIndex < 0) {
    1976           0 :                     resourceIndex = fShaderResources.count();
    1977           0 :                     fShaderResources.push(pdfShader.get());
    1978           0 :                     pdfShader.get()->ref();
    1979             :                 }
    1980           0 :                 entry->fShaderIndex = resourceIndex;
    1981             :             }
    1982             :         }
    1983             :     }
    1984             : 
    1985           0 :     sk_sp<SkPDFGraphicState> newGraphicState;
    1986           0 :     if (color == paint.getColor()) {
    1987           0 :         newGraphicState.reset(
    1988           0 :                 SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint));
    1989             :     } else {
    1990           0 :         SkPaint newPaint = paint;
    1991           0 :         newPaint.setColor(color);
    1992           0 :         newGraphicState.reset(
    1993           0 :                 SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint));
    1994             :     }
    1995           0 :     int resourceIndex = addGraphicStateResource(newGraphicState.get());
    1996           0 :     entry->fGraphicStateIndex = resourceIndex;
    1997             : 
    1998           0 :     if (hasText) {
    1999           0 :         entry->fTextScaleX = paint.getTextScaleX();
    2000           0 :         entry->fTextFill = paint.getStyle();
    2001             :     } else {
    2002           0 :         entry->fTextScaleX = 0;
    2003             :     }
    2004           0 : }
    2005             : 
    2006           0 : int SkPDFDevice::addGraphicStateResource(SkPDFObject* gs) {
    2007             :     // Assumes that gs has been canonicalized (so we can directly compare
    2008             :     // pointers).
    2009           0 :     int result = fGraphicStateResources.find(gs);
    2010           0 :     if (result < 0) {
    2011           0 :         result = fGraphicStateResources.count();
    2012           0 :         fGraphicStateResources.push(gs);
    2013           0 :         gs->ref();
    2014             :     }
    2015           0 :     return result;
    2016             : }
    2017             : 
    2018           0 : int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
    2019             :     // TODO(halcanary): make this take a sk_sp<SkPDFObject>
    2020             :     // Assumes that xobject has been canonicalized (so we can directly compare
    2021             :     // pointers).
    2022           0 :     int result = fXObjectResources.find(xObject);
    2023           0 :     if (result < 0) {
    2024           0 :         result = fXObjectResources.count();
    2025           0 :         fXObjectResources.push(SkRef(xObject));
    2026             :     }
    2027           0 :     return result;
    2028             : }
    2029             : 
    2030           0 : int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
    2031             :     sk_sp<SkPDFFont> newFont(
    2032           0 :             SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID));
    2033           0 :     if (!newFont) {
    2034           0 :         return -1;
    2035             :     }
    2036           0 :     int resourceIndex = fFontResources.find(newFont.get());
    2037           0 :     if (resourceIndex < 0) {
    2038           0 :         fDocument->registerFont(newFont.get());
    2039           0 :         resourceIndex = fFontResources.count();
    2040           0 :         fFontResources.push(newFont.release());
    2041             :     }
    2042           0 :     return resourceIndex;
    2043             : }
    2044             : 
    2045           0 : static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
    2046             : 
    2047           0 : static sk_sp<SkImage> color_filter(const SkImageSubset& imageSubset,
    2048             :                                    SkColorFilter* colorFilter) {
    2049             :     auto surface =
    2050           0 :         SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(imageSubset.dimensions()));
    2051           0 :     SkASSERT(surface);
    2052           0 :     SkCanvas* canvas = surface->getCanvas();
    2053           0 :     canvas->clear(SK_ColorTRANSPARENT);
    2054           0 :     SkPaint paint;
    2055           0 :     paint.setColorFilter(sk_ref_sp(colorFilter));
    2056           0 :     imageSubset.draw(canvas, &paint);
    2057           0 :     canvas->flush();
    2058           0 :     return surface->makeImageSnapshot();
    2059             : }
    2060             : 
    2061             : ////////////////////////////////////////////////////////////////////////////////
    2062           0 : void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix,
    2063             :                                     const SkClipStack& clipStack,
    2064             :                                     SkImageSubset imageSubset,
    2065             :                                     const SkPaint& paint) {
    2066           0 :     if (imageSubset.dimensions().isZero()) {
    2067           0 :         return;
    2068             :     }
    2069             :     #ifdef SK_PDF_IMAGE_STATS
    2070             :     gDrawImageCalls.fetch_add(1);
    2071             :     #endif
    2072           0 :     SkMatrix matrix = origMatrix;
    2073             : 
    2074             :     // Rasterize the bitmap using perspective in a new bitmap.
    2075           0 :     if (origMatrix.hasPerspective()) {
    2076           0 :         if (fRasterDpi == 0) {
    2077           0 :             return;
    2078             :         }
    2079             :         // Transform the bitmap in the new space, without taking into
    2080             :         // account the initial transform.
    2081           0 :         SkPath perspectiveOutline;
    2082           0 :         SkRect imageBounds = SkRect::Make(imageSubset.bounds());
    2083           0 :         perspectiveOutline.addRect(imageBounds);
    2084           0 :         perspectiveOutline.transform(origMatrix);
    2085             : 
    2086             :         // TODO(edisonn): perf - use current clip too.
    2087             :         // Retrieve the bounds of the new shape.
    2088           0 :         SkRect bounds = perspectiveOutline.getBounds();
    2089             : 
    2090             :         // Transform the bitmap in the new space, taking into
    2091             :         // account the initial transform.
    2092           0 :         SkMatrix total = origMatrix;
    2093           0 :         total.postConcat(fInitialTransform);
    2094           0 :         SkScalar dpiScale = SkIntToScalar(fRasterDpi) /
    2095           0 :                             SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE);
    2096           0 :         total.postScale(dpiScale, dpiScale);
    2097             : 
    2098           0 :         SkPath physicalPerspectiveOutline;
    2099           0 :         physicalPerspectiveOutline.addRect(imageBounds);
    2100           0 :         physicalPerspectiveOutline.transform(total);
    2101             : 
    2102             :         SkRect physicalPerspectiveBounds =
    2103           0 :                 physicalPerspectiveOutline.getBounds();
    2104           0 :         SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width();
    2105           0 :         SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height();
    2106             : 
    2107             :         // TODO(edisonn): A better approach would be to use a bitmap shader
    2108             :         // (in clamp mode) and draw a rect over the entire bounding box. Then
    2109             :         // intersect perspectiveOutline to the clip. That will avoid introducing
    2110             :         // alpha to the image while still giving good behavior at the edge of
    2111             :         // the image.  Avoiding alpha will reduce the pdf size and generation
    2112             :         // CPU time some.
    2113             : 
    2114           0 :         SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil();
    2115             : 
    2116           0 :         auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(wh));
    2117           0 :         if (!surface) {
    2118           0 :             return;
    2119             :         }
    2120           0 :         SkCanvas* canvas = surface->getCanvas();
    2121           0 :         canvas->clear(SK_ColorTRANSPARENT);
    2122             : 
    2123           0 :         SkScalar deltaX = bounds.left();
    2124           0 :         SkScalar deltaY = bounds.top();
    2125             : 
    2126           0 :         SkMatrix offsetMatrix = origMatrix;
    2127           0 :         offsetMatrix.postTranslate(-deltaX, -deltaY);
    2128           0 :         offsetMatrix.postScale(scaleX, scaleY);
    2129             : 
    2130             :         // Translate the draw in the new canvas, so we perfectly fit the
    2131             :         // shape in the bitmap.
    2132           0 :         canvas->setMatrix(offsetMatrix);
    2133           0 :         imageSubset.draw(canvas, nullptr);
    2134             :         // Make sure the final bits are in the bitmap.
    2135           0 :         canvas->flush();
    2136             : 
    2137             :         // In the new space, we use the identity matrix translated
    2138             :         // and scaled to reflect DPI.
    2139           0 :         matrix.setScale(1 / scaleX, 1 / scaleY);
    2140           0 :         matrix.postTranslate(deltaX, deltaY);
    2141             : 
    2142           0 :         imageSubset = SkImageSubset(surface->makeImageSnapshot());
    2143             :     }
    2144             : 
    2145             :     SkMatrix scaled;
    2146             :     // Adjust for origin flip.
    2147           0 :     scaled.setScale(SK_Scalar1, -SK_Scalar1);
    2148           0 :     scaled.postTranslate(0, SK_Scalar1);
    2149             :     // Scale the image up from 1x1 to WxH.
    2150           0 :     SkIRect subset = imageSubset.bounds();
    2151           0 :     scaled.postScale(SkIntToScalar(imageSubset.dimensions().width()),
    2152           0 :                      SkIntToScalar(imageSubset.dimensions().height()));
    2153           0 :     scaled.postConcat(matrix);
    2154           0 :     ScopedContentEntry content(this, clipStack, scaled, paint);
    2155           0 :     if (!content.entry()) {
    2156           0 :         return;
    2157             :     }
    2158           0 :     if (content.needShape()) {
    2159           0 :         SkPath shape;
    2160           0 :         shape.addRect(SkRect::Make(subset));
    2161           0 :         shape.transform(matrix);
    2162           0 :         content.setShape(shape);
    2163             :     }
    2164           0 :     if (!content.needSource()) {
    2165           0 :         return;
    2166             :     }
    2167             : 
    2168           0 :     if (SkColorFilter* colorFilter = paint.getColorFilter()) {
    2169             :         // TODO(https://bug.skia.org/4378): implement colorfilter on other
    2170             :         // draw calls.  This code here works for all
    2171             :         // drawBitmap*()/drawImage*() calls amd ImageFilters (which
    2172             :         // rasterize a layer on this backend).  Fortuanely, this seems
    2173             :         // to be how Chromium impements most color-filters.
    2174           0 :         sk_sp<SkImage> img = color_filter(imageSubset, colorFilter);
    2175           0 :         imageSubset = SkImageSubset(std::move(img));
    2176             :         // TODO(halcanary): de-dupe this by caching filtered images.
    2177             :         // (maybe in the resource cache?)
    2178             :     }
    2179             : 
    2180           0 :     SkBitmapKey key = imageSubset.getKey();
    2181           0 :     sk_sp<SkPDFObject> pdfimage = fDocument->canon()->findPDFBitmap(key);
    2182           0 :     if (!pdfimage) {
    2183           0 :         sk_sp<SkImage> img = imageSubset.makeImage();
    2184           0 :         if (!img) {
    2185           0 :             return;
    2186             :         }
    2187           0 :         pdfimage = SkPDFCreateBitmapObject(
    2188           0 :                 std::move(img), fDocument->canon()->getPixelSerializer());
    2189           0 :         if (!pdfimage) {
    2190           0 :             return;
    2191             :         }
    2192           0 :         fDocument->serialize(pdfimage);  // serialize images early.
    2193           0 :         fDocument->canon()->addPDFBitmap(key, pdfimage);
    2194             :     }
    2195             :     // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject>
    2196           0 :     SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()),
    2197           0 :                                 &content.entry()->fContent);
    2198             : }
    2199             : 
    2200             : ///////////////////////////////////////////////////////////////////////////////////////////////////
    2201             : 
    2202             : #include "SkSpecialImage.h"
    2203             : #include "SkImageFilter.h"
    2204             : 
    2205           0 : void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
    2206             :                               const SkPaint& paint) {
    2207           0 :     SkASSERT(!srcImg->isTextureBacked());
    2208             : 
    2209           0 :     SkBitmap resultBM;
    2210             : 
    2211           0 :     SkImageFilter* filter = paint.getImageFilter();
    2212           0 :     if (filter) {
    2213           0 :         SkIPoint offset = SkIPoint::Make(0, 0);
    2214           0 :         SkMatrix matrix = this->ctm();
    2215           0 :         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
    2216             :         const SkIRect clipBounds =
    2217           0 :             this->cs().bounds(this->imageInfo().bounds()).roundOut().makeOffset(-x, -y);
    2218           0 :         sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
    2219             :         // TODO: Should PDF be operating in a specified color space? For now, run the filter
    2220             :         // in the same color space as the source (this is different from all other backends).
    2221           0 :         SkImageFilter::OutputProperties outputProperties(srcImg->getColorSpace());
    2222           0 :         SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
    2223             : 
    2224           0 :         sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
    2225           0 :         if (resultImg) {
    2226           0 :             SkPaint tmpUnfiltered(paint);
    2227           0 :             tmpUnfiltered.setImageFilter(nullptr);
    2228           0 :             if (resultImg->getROPixels(&resultBM)) {
    2229           0 :                 this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
    2230             :             }
    2231             :         }
    2232             :     } else {
    2233           0 :         if (srcImg->getROPixels(&resultBM)) {
    2234           0 :             this->drawSprite(resultBM, x, y, paint);
    2235             :         }
    2236             :     }
    2237           0 : }
    2238             : 
    2239           0 : sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) {
    2240           0 :     return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap);
    2241             : }
    2242             : 
    2243           0 : sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) {
    2244             :     // TODO: See comment above in drawSpecial. The color mode we use for decode should be driven
    2245             :     // by the destination where we're going to draw thing thing (ie this device). But we don't have
    2246             :     // a color space, so we always decode in legacy mode for now.
    2247           0 :     SkColorSpace* legacyColorSpace = nullptr;
    2248           0 :     return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()),
    2249           0 :                                          image->makeNonTextureImage(), legacyColorSpace);
    2250             : }
    2251             : 
    2252           0 : sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() {
    2253           0 :     return nullptr;
    2254             : }
    2255             : 
    2256           0 : SkImageFilterCache* SkPDFDevice::getImageFilterCache() {
    2257             :     // We always return a transient cache, so it is freed after each
    2258             :     // filter traversal.
    2259           0 :     return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize);
    2260             : }

Generated by: LCOV version 1.13