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