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