• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/pdf/SkPDFDevice.h"
9 
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkBlendMode.h"
13 #include "include/core/SkCanvas.h"
14 #include "include/core/SkClipOp.h"
15 #include "include/core/SkColor.h"
16 #include "include/core/SkColorFilter.h"
17 #include "include/core/SkColorSpace.h"
18 #include "include/core/SkColorType.h"
19 #include "include/core/SkData.h"
20 #include "include/core/SkFont.h"
21 #include "include/core/SkImage.h"
22 #include "include/core/SkImageInfo.h"
23 #include "include/core/SkM44.h"
24 #include "include/core/SkPaint.h"
25 #include "include/core/SkPath.h"
26 #include "include/core/SkPathEffect.h"
27 #include "include/core/SkPathTypes.h"
28 #include "include/core/SkPathUtils.h"
29 #include "include/core/SkPixmap.h"
30 #include "include/core/SkPoint.h"
31 #include "include/core/SkRect.h"
32 #include "include/core/SkShader.h"
33 #include "include/core/SkSize.h"
34 #include "include/core/SkSpan.h"
35 #include "include/core/SkStrokeRec.h"
36 #include "include/core/SkSurface.h"
37 #include "include/core/SkSurfaceProps.h"
38 #include "include/core/SkTypeface.h"
39 #include "include/core/SkTypes.h"
40 #include "include/docs/SkPDFDocument.h"
41 #include "include/encode/SkJpegEncoder.h"
42 #include "include/pathops/SkPathOps.h"
43 #include "include/private/base/SkDebug.h"
44 #include "include/private/base/SkTemplates.h"
45 #include "include/private/base/SkTo.h"
46 #include "src/base/SkScopeExit.h"
47 #include "src/base/SkTLazy.h"
48 #include "src/base/SkUTF.h"
49 #include "src/core/SkAdvancedTypefaceMetrics.h"
50 #include "src/core/SkAnnotationKeys.h"
51 #include "src/core/SkBitmapDevice.h"
52 #include "src/core/SkBlendModePriv.h"
53 #include "src/core/SkColorSpacePriv.h"
54 #include "src/core/SkDevice.h"
55 #include "src/core/SkDraw.h"
56 #include "src/core/SkGlyph.h"
57 #include "src/core/SkMask.h"
58 #include "src/core/SkMaskFilterBase.h"
59 #include "src/core/SkPaintPriv.h"
60 #include "src/core/SkRasterClip.h"
61 #include "src/core/SkSpecialImage.h"
62 #include "src/core/SkStrikeSpec.h"
63 #include "src/pdf/SkBitmapKey.h"
64 #include "src/pdf/SkClusterator.h"
65 #include "src/pdf/SkPDFBitmap.h"
66 #include "src/pdf/SkPDFDocumentPriv.h"
67 #include "src/pdf/SkPDFFont.h"
68 #include "src/pdf/SkPDFFormXObject.h"
69 #include "src/pdf/SkPDFGraphicState.h"
70 #include "src/pdf/SkPDFResourceDict.h"
71 #include "src/pdf/SkPDFShader.h"
72 #include "src/pdf/SkPDFTag.h"
73 #include "src/pdf/SkPDFTypes.h"
74 #include "src/pdf/SkPDFUtils.h"
75 #include "src/shaders/SkColorShader.h"
76 #include "src/shaders/SkShaderBase.h"
77 #include "src/text/GlyphRun.h"
78 #include "src/utils/SkClipStackUtils.h"
79 
80 #include <algorithm>
81 #include <cstdint>
82 #include <cstring>
83 #include <utility>
84 #include <vector>
85 
86 class SkBlender;
87 class SkMesh;
88 class SkVertices;
89 
90 using namespace skia_private;
91 
92 #ifndef SK_PDF_MASK_QUALITY
93     // If MASK_QUALITY is in [0,100], will be used for JpegEncoder.
94     // Otherwise, just encode masks losslessly.
95     #define SK_PDF_MASK_QUALITY 50
96     // Since these masks are used for blurry shadows, we shouldn't need
97     // high quality.  Raise this value if your shadows have visible JPEG
98     // artifacts.
99     // If SkJpegEncoder::Encode fails, we will fall back to the lossless
100     // encoding.
101 #endif
102 
103 namespace {
104 
105 // If nodeId is not zero, outputs the tags to begin a marked-content sequence
106 // for the given node ID, and then closes those tags when this object goes
107 // out of scope.
108 class ScopedOutputMarkedContentTags {
109 public:
ScopedOutputMarkedContentTags(int nodeId,SkPoint point,SkPDFDocument * document,SkDynamicMemoryWStream * out)110     ScopedOutputMarkedContentTags(int nodeId, SkPoint point, SkPDFDocument* document,
111                                   SkDynamicMemoryWStream* out)
112         : fOut(out)
113         , fMark(nodeId ? document->createMarkIdForNodeId(nodeId, point) : SkPDFTagTree::Mark())
114     {
115         if (fMark) {
116             fOut->writeText("/P <</MCID ");
117             fOut->writeDecAsText(fMark.id());
118             fOut->writeText(" >>BDC\n");
119         }
120     }
121 
operator bool() const122     explicit operator bool() const { return bool(fMark); }
123 
point()124     SkPoint& point() { return SkASSERT(fMark), fMark.point(); }
125 
~ScopedOutputMarkedContentTags()126     ~ScopedOutputMarkedContentTags() {
127         if (fMark) {
128             fOut->writeText("EMC\n");
129         }
130     }
131 
132 private:
133     SkDynamicMemoryWStream* fOut;
134     SkPDFTagTree::Mark fMark;
135 };
136 
137 }  // namespace
138 
139 // Utility functions
140 
141 // This function destroys the mask and either frees or takes the pixels.
mask_to_greyscale_image(SkMaskBuilder * mask)142 sk_sp<SkImage> mask_to_greyscale_image(SkMaskBuilder* mask) {
143     sk_sp<SkImage> img;
144     SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(),
145                                   kGray_8_SkColorType, kOpaque_SkAlphaType),
146                 mask->fImage, mask->fRowBytes);
147     const int imgQuality = SK_PDF_MASK_QUALITY;
148     if (imgQuality <= 100 && imgQuality >= 0) {
149         SkDynamicMemoryWStream buffer;
150         SkJpegEncoder::Options jpegOptions;
151         jpegOptions.fQuality = imgQuality;
152         if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
153             img = SkImages::DeferredFromEncodedData(buffer.detachAsData());
154             SkASSERT(img);
155             if (img) {
156                 SkMaskBuilder::FreeImage(mask->image());
157             }
158         }
159     }
160     if (!img) {
161         img = SkImages::RasterFromPixmap(
162                 pm, [](const void* p, void*) { SkMaskBuilder::FreeImage(const_cast<void*>(p)); }, nullptr);
163     }
164     *mask = SkMaskBuilder();  // destructive;
165     return img;
166 }
167 
alpha_image_to_greyscale_image(const SkImage * mask)168 sk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) {
169     int w = mask->width(), h = mask->height();
170     SkBitmap greyBitmap;
171     greyBitmap.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
172     // TODO: support gpu images in pdf
173     if (!mask->readPixels(nullptr, SkImageInfo::MakeA8(w, h),
174                           greyBitmap.getPixels(), greyBitmap.rowBytes(), 0, 0)) {
175         return nullptr;
176     }
177     greyBitmap.setImmutable();
178     return greyBitmap.asImage();
179 }
180 
add_resource(THashSet<SkPDFIndirectReference> & resources,SkPDFIndirectReference ref)181 static int add_resource(THashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
182     resources.add(ref);
183     return ref.fValue;
184 }
185 
draw_points(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & paint,const SkIRect & bounds,SkDevice * device)186 static void draw_points(SkCanvas::PointMode mode,
187                         size_t count,
188                         const SkPoint* points,
189                         const SkPaint& paint,
190                         const SkIRect& bounds,
191                         SkDevice* device) {
192     SkRasterClip rc(bounds);
193     SkDraw draw;
194     draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0);
195     draw.fCTM = &device->localToDevice();
196     draw.fRC = &rc;
197     draw.drawPoints(mode, count, points, paint, device);
198 }
199 
transform_shader(SkPaint * paint,const SkMatrix & ctm)200 static void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
201     SkASSERT(!ctm.isIdentity());
202 #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
203     // A shader's matrix is:  CTM x LocalMatrix x WrappingLocalMatrix.  We want to
204     // switch to device space, where CTM = I, while keeping the original behavior.
205     //
206     //               I * LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
207     //                   LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
208     //  InvLocalMatrix * LocalMatrix * NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
209     //                                 NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
210     //
211     SkMatrix lm = SkPDFUtils::GetShaderLocalMatrix(paint->getShader());
212     SkMatrix lmInv;
213     if (lm.invert(&lmInv)) {
214         SkMatrix m = SkMatrix::Concat(SkMatrix::Concat(lmInv, ctm), lm);
215         paint->setShader(paint->getShader()->makeWithLocalMatrix(m));
216     }
217     return;
218 #endif
219     paint->setShader(paint->getShader()->makeWithLocalMatrix(ctm));
220 }
221 
222 
clean_paint(const SkPaint & srcPaint)223 static SkTCopyOnFirstWrite<SkPaint> clean_paint(const SkPaint& srcPaint) {
224     SkTCopyOnFirstWrite<SkPaint> paint(srcPaint);
225     // If the paint will definitely draw opaquely, replace kSrc with
226     // kSrcOver.  http://crbug.com/473572
227     if (!paint->isSrcOver() &&
228         SkBlendFastPath::kSrcOver == CheckFastPath(*paint, false))
229     {
230         paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
231     }
232     if (paint->getColorFilter()) {
233         // We assume here that PDFs all draw in sRGB.
234         SkPaintPriv::RemoveColorFilter(paint.writable(), sk_srgb_singleton());
235     }
236     SkASSERT(!paint->getColorFilter());
237     return paint;
238 }
239 
set_style(SkTCopyOnFirstWrite<SkPaint> * paint,SkPaint::Style style)240 static void set_style(SkTCopyOnFirstWrite<SkPaint>* paint, SkPaint::Style style) {
241     if (paint->get()->getStyle() != style) {
242         paint->writable()->setStyle(style);
243     }
244 }
245 
246 /* Calculate an inverted path's equivalent non-inverted path, given the
247  * canvas bounds.
248  * outPath may alias with invPath (since this is supported by PathOps).
249  */
calculate_inverse_path(const SkRect & bounds,const SkPath & invPath,SkPath * outPath)250 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
251                                    SkPath* outPath) {
252     SkASSERT(invPath.isInverseFillType());
253     return Op(SkPath::Rect(bounds), invPath, kIntersect_SkPathOp, outPath);
254 }
255 
createDevice(const CreateInfo & cinfo,const SkPaint * layerPaint)256 sk_sp<SkDevice> SkPDFDevice::createDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
257     // PDF does not support image filters, so render them on CPU.
258     // Note that this rendering is done at "screen" resolution (100dpi), not
259     // printer resolution.
260 
261     // TODO: It may be possible to express some filters natively using PDF
262     // to improve quality and file size (https://bug.skia.org/3043)
263     if ((layerPaint && (layerPaint->getImageFilter() || layerPaint->getColorFilter()))
264         || (cinfo.fInfo.colorSpace() && !cinfo.fInfo.colorSpace()->isSRGB())) {
265         // need to return a raster device, which we will detect in drawDevice()
266         return SkBitmapDevice::Create(cinfo.fInfo,
267                                       SkSurfaceProps());
268     }
269     return sk_make_sp<SkPDFDevice>(cinfo.fInfo.dimensions(), fDocument);
270 }
271 
272 // A helper class to automatically finish a ContentEntry at the end of a
273 // drawing method and maintain the state needed between set up and finish.
274 class ScopedContentEntry {
275 public:
ScopedContentEntry(SkPDFDevice * device,const SkClipStack * clipStack,const SkMatrix & matrix,const SkPaint & paint,SkScalar textScale=0)276     ScopedContentEntry(SkPDFDevice* device,
277                        const SkClipStack* clipStack,
278                        const SkMatrix& matrix,
279                        const SkPaint& paint,
280                        SkScalar textScale = 0)
281         : fDevice(device)
282         , fBlendMode(SkBlendMode::kSrcOver)
283         , fClipStack(clipStack)
284     {
285         if (matrix.hasPerspective()) {
286             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
287             return;
288         }
289         fBlendMode = paint.getBlendMode_or(SkBlendMode::kSrcOver);
290         fContentStream =
291             fDevice->setUpContentEntry(clipStack, matrix, paint, textScale, &fDstFormXObject);
292     }
ScopedContentEntry(SkPDFDevice * dev,const SkPaint & paint,SkScalar textScale=0)293     ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, SkScalar textScale = 0)
294         : ScopedContentEntry(dev, &dev->cs(), dev->localToDevice(), paint, textScale) {}
295 
~ScopedContentEntry()296     ~ScopedContentEntry() {
297         if (fContentStream) {
298             SkPath* shape = &fShape;
299             if (shape->isEmpty()) {
300                 shape = nullptr;
301             }
302             fDevice->finishContentEntry(fClipStack, fBlendMode, fDstFormXObject, shape);
303         }
304     }
305 
operator bool() const306     explicit operator bool() const { return fContentStream != nullptr; }
stream()307     SkDynamicMemoryWStream* stream() { return fContentStream; }
308 
309     /* Returns true when we explicitly need the shape of the drawing. */
needShape()310     bool needShape() {
311         switch (fBlendMode) {
312             case SkBlendMode::kClear:
313             case SkBlendMode::kSrc:
314             case SkBlendMode::kSrcIn:
315             case SkBlendMode::kSrcOut:
316             case SkBlendMode::kDstIn:
317             case SkBlendMode::kDstOut:
318             case SkBlendMode::kSrcATop:
319             case SkBlendMode::kDstATop:
320             case SkBlendMode::kModulate:
321                 return true;
322             default:
323                 return false;
324         }
325     }
326 
327     /* Returns true unless we only need the shape of the drawing. */
needSource()328     bool needSource() {
329         if (fBlendMode == SkBlendMode::kClear) {
330             return false;
331         }
332         return true;
333     }
334 
335     /* If the shape is different than the alpha component of the content, then
336      * setShape should be called with the shape.  In particular, images and
337      * devices have rectangular shape.
338      */
setShape(const SkPath & shape)339     void setShape(const SkPath& shape) {
340         fShape = shape;
341     }
342 
343 private:
344     SkPDFDevice* fDevice = nullptr;
345     SkDynamicMemoryWStream* fContentStream = nullptr;
346     SkBlendMode fBlendMode;
347     SkPDFIndirectReference fDstFormXObject;
348     SkPath fShape;
349     const SkClipStack* fClipStack;
350 };
351 
352 ////////////////////////////////////////////////////////////////////////////////
353 
SkPDFDevice(SkISize pageSize,SkPDFDocument * doc,const SkMatrix & transform)354 SkPDFDevice::SkPDFDevice(SkISize pageSize, SkPDFDocument* doc, const SkMatrix& transform)
355         : SkClipStackDevice(SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()),
356                             SkSurfaceProps())
357         , fInitialTransform(transform)
358         , fNodeId(0)
359         , fDocument(doc) {
360     SkASSERT(!pageSize.isEmpty());
361 }
362 
363 SkPDFDevice::~SkPDFDevice() = default;
364 
reset()365 void SkPDFDevice::reset() {
366     fGraphicStateResources.reset();
367     fXObjectResources.reset();
368     fShaderResources.reset();
369     fFontResources.reset();
370     fContent.reset();
371     fActiveStackState = SkPDFGraphicStackState();
372 }
373 
drawAnnotation(const SkRect & rect,const char key[],SkData * value)374 void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
375     if (!value || !fDocument->hasCurrentPage()) {
376         return;
377     }
378     // Annotations are specified in absolute coordinates, so the page xform maps from device space
379     // to the global space, and applies the document transform.
380     SkMatrix pageXform = this->deviceToGlobal().asM33();
381     pageXform.postConcat(fDocument->currentPageTransform());
382     if (rect.isEmpty()) {
383         if (!strcmp(key, SkPDFGetNodeIdKey())) {
384             int nodeID;
385             if (value->size() != sizeof(nodeID)) { return; }
386             memcpy(&nodeID, value->data(), sizeof(nodeID));
387             fNodeId = nodeID;
388             return;
389         }
390         if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
391             SkPoint p = this->localToDevice().mapXY(rect.x(), rect.y());
392             pageXform.mapPoints(&p, 1);
393             auto pg = fDocument->currentPage();
394             fDocument->fNamedDestinations.push_back(SkPDFNamedDestination{sk_ref_sp(value), p, pg});
395         }
396         return;
397     }
398     // Convert to path to handle non-90-degree rotations.
399     SkPath path = SkPath::Rect(rect).makeTransform(this->localToDevice());
400     SkPath clip;
401     SkClipStack_AsPath(this->cs(), &clip);
402     Op(clip, path, kIntersect_SkPathOp, &path);
403     // PDF wants a rectangle only.
404     SkRect transformedRect = pageXform.mapRect(path.getBounds());
405     if (transformedRect.isEmpty()) {
406         return;
407     }
408 
409     SkPDFLink::Type linkType = SkPDFLink::Type::kNone;
410     if (!strcmp(SkAnnotationKeys::URL_Key(), key)) {
411         linkType = SkPDFLink::Type::kUrl;
412     } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
413         linkType = SkPDFLink::Type::kNamedDestination;
414     }
415 
416     if (linkType != SkPDFLink::Type::kNone) {
417         std::unique_ptr<SkPDFLink> link = std::make_unique<SkPDFLink>(
418             linkType, value, transformedRect, fNodeId);
419         fDocument->fCurrentPageLinks.push_back(std::move(link));
420     }
421 }
422 
drawPaint(const SkPaint & srcPaint)423 void SkPDFDevice::drawPaint(const SkPaint& srcPaint) {
424     SkMatrix inverse;
425     if (!this->localToDevice().invert(&inverse)) {
426         return;
427     }
428     SkRect bbox = this->cs().bounds(this->bounds());
429     inverse.mapRect(&bbox);
430     bbox.roundOut(&bbox);
431     if (this->hasEmptyClip()) {
432         return;
433     }
434     SkPaint newPaint = srcPaint;
435     newPaint.setStyle(SkPaint::kFill_Style);
436     this->drawRect(bbox, newPaint);
437 }
438 
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & srcPaint)439 void SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
440                              size_t count,
441                              const SkPoint* points,
442                              const SkPaint& srcPaint) {
443     if (this->hasEmptyClip()) {
444         return;
445     }
446     if (count == 0) {
447         return;
448     }
449     SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(srcPaint));
450 
451 
452 
453     if (SkCanvas::kPoints_PointMode != mode) {
454         set_style(&paint, SkPaint::kStroke_Style);
455     }
456 
457     // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
458     // We only use this when there's a path effect or perspective because of the overhead
459     // of multiple calls to setUpContentEntry it causes.
460     if (paint->getPathEffect() || this->localToDevice().hasPerspective()) {
461         draw_points(mode, count, points, *paint, this->devClipBounds(), this);
462         return;
463     }
464 
465 
466     if (mode == SkCanvas::kPoints_PointMode && paint->getStrokeCap() != SkPaint::kRound_Cap) {
467         if (paint->getStrokeWidth()) {
468             // PDF won't draw a single point with square/butt caps because the
469             // orientation is ambiguous.  Draw a rectangle instead.
470             set_style(&paint, SkPaint::kFill_Style);
471             SkScalar strokeWidth = paint->getStrokeWidth();
472             SkScalar halfStroke = SkScalarHalf(strokeWidth);
473             for (size_t i = 0; i < count; i++) {
474                 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
475                 r.inset(-halfStroke, -halfStroke);
476                 this->drawRect(r, *paint);
477             }
478             return;
479         } else {
480             if (paint->getStrokeCap() != SkPaint::kRound_Cap) {
481                 paint.writable()->setStrokeCap(SkPaint::kRound_Cap);
482             }
483         }
484     }
485 
486     ScopedContentEntry content(this, *paint);
487     if (!content) {
488         return;
489     }
490     SkDynamicMemoryWStream* contentStream = content.stream();
491     switch (mode) {
492         case SkCanvas::kPolygon_PointMode:
493             SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream);
494             for (size_t i = 1; i < count; i++) {
495                 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, contentStream);
496             }
497             SkPDFUtils::StrokePath(contentStream);
498             break;
499         case SkCanvas::kLines_PointMode:
500             for (size_t i = 0; i < count/2; i++) {
501                 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, contentStream);
502                 SkPDFUtils::AppendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY, contentStream);
503                 SkPDFUtils::StrokePath(contentStream);
504             }
505             break;
506         case SkCanvas::kPoints_PointMode:
507             SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
508             for (size_t i = 0; i < count; i++) {
509                 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, contentStream);
510                 SkPDFUtils::ClosePath(contentStream);
511                 SkPDFUtils::StrokePath(contentStream);
512             }
513             break;
514         default:
515             SkASSERT(false);
516     }
517 }
518 
drawRect(const SkRect & rect,const SkPaint & paint)519 void SkPDFDevice::drawRect(const SkRect& rect, const SkPaint& paint) {
520     SkRect r = rect;
521     r.sort();
522     this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::Rect(r), paint, true);
523 }
524 
drawRRect(const SkRRect & rrect,const SkPaint & paint)525 void SkPDFDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
526     this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::RRect(rrect), paint, true);
527 }
528 
drawOval(const SkRect & oval,const SkPaint & paint)529 void SkPDFDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
530     this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::Oval(oval), paint, true);
531 }
532 
drawPath(const SkPath & path,const SkPaint & paint,bool pathIsMutable)533 void SkPDFDevice::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
534     this->internalDrawPath(this->cs(), this->localToDevice(), path, paint, pathIsMutable);
535 }
536 
internalDrawPathWithFilter(const SkClipStack & clipStack,const SkMatrix & ctm,const SkPath & origPath,const SkPaint & origPaint)537 void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
538                                              const SkMatrix& ctm,
539                                              const SkPath& origPath,
540                                              const SkPaint& origPaint) {
541     SkASSERT(origPaint.getMaskFilter());
542     SkPath path(origPath);
543     SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
544 
545     SkStrokeRec::InitStyle initStyle = skpathutils::FillPathWithPaint(path, *paint, &path)
546                                      ? SkStrokeRec::kFill_InitStyle
547                                      : SkStrokeRec::kHairline_InitStyle;
548     path.transform(ctm, &path);
549 
550     SkIRect bounds = clipStack.bounds(this->bounds()).roundOut();
551     SkMaskBuilder sourceMask;
552     if (!SkDraw::DrawToMask(path, bounds, paint->getMaskFilter(), &SkMatrix::I(),
553                             &sourceMask, SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode,
554                             initStyle)) {
555         return;
556     }
557     SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.image());
558     SkMaskBuilder dstMask;
559     SkIPoint margin;
560     if (!as_MFB(paint->getMaskFilter())->filterMask(&dstMask, sourceMask, ctm, &margin)) {
561         return;
562     }
563     SkIRect dstMaskBounds = dstMask.fBounds;
564     sk_sp<SkImage> mask = mask_to_greyscale_image(&dstMask);
565     // PDF doesn't seem to allow masking vector graphics with an Image XObject.
566     // Must mask with a Form XObject.
567     sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
568     {
569         SkCanvas canvas(maskDevice);
570         canvas.drawImage(mask, dstMaskBounds.x(), dstMaskBounds.y());
571     }
572     if (!ctm.isIdentity() && paint->getShader()) {
573         transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
574     }
575     ScopedContentEntry content(this, &clipStack, SkMatrix::I(), *paint);
576     if (!content) {
577         return;
578     }
579     this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
580             maskDevice->makeFormXObjectFromDevice(dstMaskBounds, true), false,
581             SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
582     SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream());
583     SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream());
584     this->clearMaskOnGraphicState(content.stream());
585 }
586 
setGraphicState(SkPDFIndirectReference gs,SkDynamicMemoryWStream * content)587 void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
588     SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
589 }
590 
clearMaskOnGraphicState(SkDynamicMemoryWStream * contentStream)591 void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
592     // The no-softmask graphic state is used to "turn off" the mask for later draw calls.
593     SkPDFIndirectReference& noSMaskGS = fDocument->fNoSmaskGraphicState;
594     if (!noSMaskGS) {
595         SkPDFDict tmp("ExtGState");
596         tmp.insertName("SMask", "None");
597         noSMaskGS = fDocument->emit(tmp);
598     }
599     this->setGraphicState(noSMaskGS, contentStream);
600 }
601 
internalDrawPath(const SkClipStack & clipStack,const SkMatrix & ctm,const SkPath & origPath,const SkPaint & srcPaint,bool pathIsMutable)602 void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
603                                    const SkMatrix& ctm,
604                                    const SkPath& origPath,
605                                    const SkPaint& srcPaint,
606                                    bool pathIsMutable) {
607     if (clipStack.isEmpty(this->bounds())) {
608         return;
609     }
610     SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(srcPaint));
611     SkPath modifiedPath;
612     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
613 
614     if (paint->getMaskFilter()) {
615         this->internalDrawPathWithFilter(clipStack, ctm, origPath, *paint);
616         return;
617     }
618 
619     SkMatrix matrix = ctm;
620 
621     if (paint->getPathEffect()) {
622         if (clipStack.isEmpty(this->bounds())) {
623             return;
624         }
625         if (!pathIsMutable) {
626             modifiedPath = origPath;
627             pathPtr = &modifiedPath;
628             pathIsMutable = true;
629         }
630         if (skpathutils::FillPathWithPaint(*pathPtr, *paint, pathPtr)) {
631             set_style(&paint, SkPaint::kFill_Style);
632         } else {
633             set_style(&paint, SkPaint::kStroke_Style);
634             if (paint->getStrokeWidth() != 0) {
635                 paint.writable()->setStrokeWidth(0);
636             }
637         }
638         paint.writable()->setPathEffect(nullptr);
639     }
640 
641     if (this->handleInversePath(*pathPtr, *paint, pathIsMutable)) {
642         return;
643     }
644     if (matrix.getType() & SkMatrix::kPerspective_Mask) {
645         if (!pathIsMutable) {
646             modifiedPath = origPath;
647             pathPtr = &modifiedPath;
648             pathIsMutable = true;
649         }
650         pathPtr->transform(matrix);
651         if (paint->getShader()) {
652             transform_shader(paint.writable(), matrix);
653         }
654         matrix = SkMatrix::I();
655     }
656 
657     ScopedContentEntry content(this, &clipStack, matrix, *paint);
658     if (!content) {
659         return;
660     }
661     constexpr SkScalar kToleranceScale = 0.0625f;  // smaller = better conics (circles).
662     SkScalar matrixScale = matrix.mapRadius(1.0f);
663     SkScalar tolerance = matrixScale > 0.0f ? kToleranceScale / matrixScale : kToleranceScale;
664     bool consumeDegeratePathSegments =
665            paint->getStyle() == SkPaint::kFill_Style ||
666            (paint->getStrokeCap() != SkPaint::kRound_Cap &&
667             paint->getStrokeCap() != SkPaint::kSquare_Cap);
668     SkPDFUtils::EmitPath(*pathPtr, paint->getStyle(), consumeDegeratePathSegments, content.stream(),
669                          tolerance);
670     SkPDFUtils::PaintPath(paint->getStyle(), pathPtr->getFillType(), content.stream());
671 }
672 
673 ////////////////////////////////////////////////////////////////////////////////
674 
drawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint)675 void SkPDFDevice::drawImageRect(const SkImage* image,
676                                 const SkRect* src,
677                                 const SkRect& dst,
678                                 const SkSamplingOptions& sampling,
679                                 const SkPaint& paint,
680                                 SkCanvas::SrcRectConstraint) {
681     SkASSERT(image);
682     this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
683                                 src, dst, sampling, paint, this->localToDevice());
684 }
685 
drawSprite(const SkBitmap & bm,int x,int y,const SkPaint & paint)686 void SkPDFDevice::drawSprite(const SkBitmap& bm, int x, int y, const SkPaint& paint) {
687     SkASSERT(!bm.drawsNothing());
688     auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height());
689     this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, SkSamplingOptions(), paint,
690                                 SkMatrix::I());
691 }
692 
693 ////////////////////////////////////////////////////////////////////////////////
694 
695 namespace {
696 class GlyphPositioner {
697 public:
GlyphPositioner(SkDynamicMemoryWStream * content,SkScalar textSkewX,SkPoint origin)698     GlyphPositioner(SkDynamicMemoryWStream* content,
699                     SkScalar textSkewX,
700                     SkPoint origin)
701         : fContent(content)
702         , fCurrentMatrixOrigin(origin)
703         , fTextSkewX(textSkewX) {
704     }
~GlyphPositioner()705     ~GlyphPositioner() { this->flush(); }
flush()706     void flush() {
707         if (fInText) {
708             fContent->writeText("> Tj\n");
709             fInText = false;
710         }
711     }
setFont(SkPDFFont * pdfFont)712     void setFont(SkPDFFont* pdfFont) {
713         this->flush();
714         fPDFFont = pdfFont;
715         // Reader 2020.013.20064 incorrectly advances some Type3 fonts https://crbug.com/1226960
716         bool convertedToType3 = fPDFFont->getType() == SkAdvancedTypefaceMetrics::kOther_Font;
717         bool thousandEM = fPDFFont->typeface()->getUnitsPerEm() == 1000;
718         fViewersAgreeOnAdvancesInFont = thousandEM || !convertedToType3;
719     }
writeGlyph(uint16_t glyph,SkScalar advanceWidth,SkPoint xy)720     void writeGlyph(uint16_t glyph, SkScalar advanceWidth, SkPoint xy) {
721         SkASSERT(fPDFFont);
722         if (!fInitialized) {
723             // Flip the text about the x-axis to account for origin swap and include
724             // the passed parameters.
725             fContent->writeText("1 0 ");
726             SkPDFUtils::AppendScalar(-fTextSkewX, fContent);
727             fContent->writeText(" -1 ");
728             SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent);
729             fContent->writeText(" ");
730             SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent);
731             fContent->writeText(" Tm\n");
732             fCurrentMatrixOrigin.set(0.0f, 0.0f);
733             fInitialized = true;
734         }
735         SkPoint position = xy - fCurrentMatrixOrigin;
736         if (!fViewersAgreeOnXAdvance || position != SkPoint{fXAdvance, 0}) {
737             this->flush();
738             SkPDFUtils::AppendScalar(position.x() - position.y() * fTextSkewX, fContent);
739             fContent->writeText(" ");
740             SkPDFUtils::AppendScalar(-position.y(), fContent);
741             fContent->writeText(" Td ");
742             fCurrentMatrixOrigin = xy;
743             fXAdvance = 0;
744             fViewersAgreeOnXAdvance = true;
745         }
746         fXAdvance += advanceWidth;
747         if (!fViewersAgreeOnAdvancesInFont) {
748             fViewersAgreeOnXAdvance = false;
749         }
750         if (!fInText) {
751             fContent->writeText("<");
752             fInText = true;
753         }
754         if (fPDFFont->multiByteGlyphs()) {
755             SkPDFUtils::WriteUInt16BE(fContent, glyph);
756         } else {
757             SkASSERT(0 == glyph >> 8);
758             SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph));
759         }
760     }
761 
762 private:
763     SkDynamicMemoryWStream* fContent;
764     SkPDFFont* fPDFFont = nullptr;
765     SkPoint fCurrentMatrixOrigin;
766     SkScalar fXAdvance = 0.0f;
767     bool fViewersAgreeOnAdvancesInFont = true;
768     bool fViewersAgreeOnXAdvance = true;
769     SkScalar fTextSkewX;
770     bool fInText = false;
771     bool fInitialized = false;
772 };
773 }  // namespace
774 
map_glyph(const std::vector<SkUnichar> & glyphToUnicode,SkGlyphID glyph)775 static SkUnichar map_glyph(const std::vector<SkUnichar>& glyphToUnicode, SkGlyphID glyph) {
776     return glyph < glyphToUnicode.size() ? glyphToUnicode[SkToInt(glyph)] : -1;
777 }
778 
779 namespace {
780 struct PositionedGlyph {
781     SkPoint fPos;
782     SkGlyphID fGlyph;
783 };
784 }  // namespace
785 
get_glyph_bounds_device_space(const SkGlyph * glyph,SkScalar xScale,SkScalar yScale,SkPoint xy,const SkMatrix & ctm)786 static SkRect get_glyph_bounds_device_space(const SkGlyph* glyph,
787                                             SkScalar xScale, SkScalar yScale,
788                                             SkPoint xy, const SkMatrix& ctm) {
789     SkRect glyphBounds = SkMatrix::Scale(xScale, yScale).mapRect(glyph->rect());
790     glyphBounds.offset(xy);
791     ctm.mapRect(&glyphBounds); // now in dev space.
792     return glyphBounds;
793 }
794 
contains(const SkRect & r,SkPoint p)795 static bool contains(const SkRect& r, SkPoint p) {
796    return r.left() <= p.x() && p.x() <= r.right() &&
797           r.top()  <= p.y() && p.y() <= r.bottom();
798 }
799 
drawGlyphRunAsPath(const sktext::GlyphRun & glyphRun,SkPoint offset,const SkPaint & runPaint)800 void SkPDFDevice::drawGlyphRunAsPath(
801         const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) {
802     const SkFont& font = glyphRun.font();
803     SkPath path;
804 
805     struct Rec {
806         SkPath* fPath;
807         SkPoint fOffset;
808         const SkPoint* fPos;
809     } rec = {&path, offset, glyphRun.positions().data()};
810 
811     font.getPaths(glyphRun.glyphsIDs().data(), glyphRun.glyphsIDs().size(),
812                   [](const SkPath* path, const SkMatrix& mx, void* ctx) {
813                       Rec* rec = reinterpret_cast<Rec*>(ctx);
814                       if (path) {
815                           SkMatrix total = mx;
816                           total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
817                                               rec->fPos->fY + rec->fOffset.fY);
818                           rec->fPath->addPath(*path, total);
819                       }
820                       rec->fPos += 1; // move to the next glyph's position
821                   }, &rec);
822     this->internalDrawPath(this->cs(), this->localToDevice(), path, runPaint, true);
823 
824     SkFont transparentFont = glyphRun.font();
825     transparentFont.setEmbolden(false); // Stop Recursion
826     sktext::GlyphRun tmpGlyphRun(glyphRun, transparentFont);
827 
828     SkPaint transparent;
829     transparent.setColor(SK_ColorTRANSPARENT);
830 
831     if (this->localToDevice().hasPerspective()) {
832         SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
833         this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
834     } else {
835         this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
836     }
837 }
838 
needs_new_font(SkPDFFont * font,const SkGlyph * glyph,SkAdvancedTypefaceMetrics::FontType fontType)839 static bool needs_new_font(SkPDFFont* font, const SkGlyph* glyph,
840                            SkAdvancedTypefaceMetrics::FontType fontType) {
841     if (!font || !font->hasGlyph(glyph->getGlyphID())) {
842         return true;
843     }
844     if (fontType == SkAdvancedTypefaceMetrics::kOther_Font) {
845         return false;
846     }
847     if (glyph->isEmpty()) {
848         return false;
849     }
850 
851     bool bitmapOnly = nullptr == glyph->path();
852     bool convertedToType3 = (font->getType() == SkAdvancedTypefaceMetrics::kOther_Font);
853     return convertedToType3 != bitmapOnly;
854 }
855 
internalDrawGlyphRun(const sktext::GlyphRun & glyphRun,SkPoint offset,const SkPaint & runPaint)856 void SkPDFDevice::internalDrawGlyphRun(
857         const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) {
858 
859     const SkGlyphID* glyphIDs = glyphRun.glyphsIDs().data();
860     uint32_t glyphCount = SkToU32(glyphRun.glyphsIDs().size());
861     const SkFont& glyphRunFont = glyphRun.font();
862 
863     if (!glyphCount || !glyphIDs || glyphRunFont.getSize() <= 0 || this->hasEmptyClip()) {
864         return;
865     }
866     if (runPaint.getPathEffect()
867         || runPaint.getMaskFilter()
868         || glyphRunFont.isEmbolden()
869         || this->localToDevice().hasPerspective()
870         || SkPaint::kFill_Style != runPaint.getStyle()) {
871         // Stroked Text doesn't work well with Type3 fonts.
872         this->drawGlyphRunAsPath(glyphRun, offset, runPaint);
873         return;
874     }
875     SkTypeface* typeface = glyphRunFont.getTypeface();
876     if (!typeface) {
877         SkDebugf("SkPDF: glyphRunFont has no typeface.\n");
878         return;
879     }
880 
881     const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, fDocument);
882     if (!metrics) {
883         return;
884     }
885     SkAdvancedTypefaceMetrics::FontType fontType = SkPDFFont::FontType(*typeface, *metrics);
886 
887     const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(typeface, fDocument);
888 
889     SkClusterator clusterator(glyphRun);
890 
891     int emSize;
892     SkStrikeSpec strikeSpec = SkStrikeSpec::MakePDFVector(*typeface, &emSize);
893 
894     SkScalar textSize = glyphRunFont.getSize();
895     SkScalar advanceScale = textSize * glyphRunFont.getScaleX() / emSize;
896 
897     // textScaleX and textScaleY are used to get a conservative bounding box for glyphs.
898     SkScalar textScaleY = textSize / emSize;
899     SkScalar textScaleX = advanceScale + glyphRunFont.getSkewX() * textScaleY;
900 
901     SkRect clipStackBounds = this->cs().bounds(this->bounds());
902 
903     SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(runPaint));
904     ScopedContentEntry content(this, *paint, glyphRunFont.getScaleX());
905     if (!content) {
906         return;
907     }
908     SkDynamicMemoryWStream* out = content.stream();
909 
910     out->writeText("BT\n");
911     SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
912 
913     // Destinations are in absolute coordinates.
914     // The glyphs bounds go through the localToDevice separately for clipping.
915     SkMatrix pageXform = this->deviceToGlobal().asM33();
916     pageXform.postConcat(fDocument->currentPageTransform());
917 
918     ScopedOutputMarkedContentTags mark(fNodeId, {SK_ScalarNaN, SK_ScalarNaN}, fDocument, out);
919     if (!glyphRun.text().empty()) {
920         fDocument->addNodeTitle(fNodeId, glyphRun.text());
921     }
922 
923     const int numGlyphs = typeface->countGlyphs();
924 
925     if (clusterator.reversedChars()) {
926         out->writeText("/ReversedChars BMC\n");
927     }
928     SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
929     GlyphPositioner glyphPositioner(out, glyphRunFont.getSkewX(), offset);
930     SkPDFFont* font = nullptr;
931 
932     SkBulkGlyphMetricsAndPaths paths{strikeSpec};
933     auto glyphs = paths.glyphs(glyphRun.glyphsIDs());
934 
935     while (SkClusterator::Cluster c = clusterator.next()) {
936         int index = c.fGlyphIndex;
937         int glyphLimit = index + c.fGlyphCount;
938 
939         bool actualText = false;
940         SK_AT_SCOPE_EXIT(if (actualText) {
941                              glyphPositioner.flush();
942                              out->writeText("EMC\n");
943                          });
944         if (c.fUtf8Text) {  // real cluster
945             // Check if `/ActualText` needed.
946             const char* textPtr = c.fUtf8Text;
947             const char* textEnd = c.fUtf8Text + c.fTextByteLength;
948             SkUnichar unichar = SkUTF::NextUTF8(&textPtr, textEnd);
949             if (unichar < 0) {
950                 return;
951             }
952             if (textPtr < textEnd ||                                    // >1 code points in cluster
953                 c.fGlyphCount > 1 ||                                    // >1 glyphs in cluster
954                 unichar != map_glyph(glyphToUnicode, glyphIDs[index]))  // 1:1 but wrong mapping
955             {
956                 glyphPositioner.flush();
957                 out->writeText("/Span<</ActualText ");
958                 SkPDFWriteTextString(out, c.fUtf8Text, c.fTextByteLength);
959                 out->writeText(" >> BDC\n");  // begin marked-content sequence
960                                                // with an associated property list.
961                 actualText = true;
962             }
963         }
964         for (; index < glyphLimit; ++index) {
965             SkGlyphID gid = glyphIDs[index];
966             if (numGlyphs <= gid) {
967                 continue;
968             }
969             SkPoint xy = glyphRun.positions()[index];
970             // Do a glyph-by-glyph bounds-reject if positions are absolute.
971             SkRect glyphBounds = get_glyph_bounds_device_space(
972                     glyphs[index], textScaleX, textScaleY,
973                     xy + offset, this->localToDevice());
974             if (glyphBounds.isEmpty()) {
975                 if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
976                     continue;
977                 }
978             } else {
979                 if (!clipStackBounds.intersects(glyphBounds)) {
980                     continue;  // reject glyphs as out of bounds
981                 }
982             }
983             if (needs_new_font(font, glyphs[index], fontType)) {
984                 // Not yet specified font or need to switch font.
985                 font = SkPDFFont::GetFontResource(fDocument, glyphs[index], typeface);
986                 SkASSERT(font);  // All preconditions for SkPDFFont::GetFontResource are met.
987                 glyphPositioner.setFont(font);
988                 SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
989                                        add_resource(fFontResources, font->indirectReference()));
990                 out->writeText(" ");
991                 SkPDFUtils::AppendScalar(textSize, out);
992                 out->writeText(" Tf\n");
993 
994             }
995             font->noteGlyphUsage(gid);
996             SkGlyphID encodedGlyph = font->glyphToPDFFontEncoding(gid);
997             SkScalar advance = advanceScale * glyphs[index]->advanceX();
998             if (mark) {
999                 SkRect absoluteGlyphBounds = pageXform.mapRect(glyphBounds);
1000                 SkPoint& markPoint = mark.point();
1001                 if (markPoint.isFinite()) {
1002                     markPoint.fX = std::min(absoluteGlyphBounds.fLeft  , markPoint.fX);
1003                     markPoint.fY = std::max(absoluteGlyphBounds.fBottom, markPoint.fY); // PDF top
1004                 } else {
1005                     markPoint = SkPoint{absoluteGlyphBounds.fLeft, absoluteGlyphBounds.fBottom};
1006                 }
1007             }
1008             glyphPositioner.writeGlyph(encodedGlyph, advance, xy);
1009         }
1010     }
1011 }
1012 
onDrawGlyphRunList(SkCanvas *,const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1013 void SkPDFDevice::onDrawGlyphRunList(SkCanvas*,
1014                                      const sktext::GlyphRunList& glyphRunList,
1015                                      const SkPaint& paint) {
1016     SkASSERT(!glyphRunList.hasRSXForm());
1017     for (const sktext::GlyphRun& glyphRun : glyphRunList) {
1018         this->internalDrawGlyphRun(glyphRun, glyphRunList.origin(), paint);
1019     }
1020 }
1021 
drawVertices(const SkVertices *,sk_sp<SkBlender>,const SkPaint &,bool)1022 void SkPDFDevice::drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool) {
1023     if (this->hasEmptyClip()) {
1024         return;
1025     }
1026     // TODO: implement drawVertices
1027 }
1028 
drawMesh(const SkMesh &,sk_sp<SkBlender>,const SkPaint &)1029 void SkPDFDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) {
1030     if (this->hasEmptyClip()) {
1031         return;
1032     }
1033     // TODO: implement drawMesh
1034 }
1035 
drawFormXObject(SkPDFIndirectReference xObject,SkDynamicMemoryWStream * content,SkPath * shape)1036 void SkPDFDevice::drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream* content,
1037                                   SkPath* shape) {
1038     SkPoint point{SK_ScalarNaN, SK_ScalarNaN};
1039     if (shape) {
1040         // Destinations are in absolute coordinates.
1041         SkMatrix pageXform = this->deviceToGlobal().asM33();
1042         pageXform.postConcat(fDocument->currentPageTransform());
1043         // The shape already has localToDevice applied.
1044 
1045         SkRect shapeBounds = shape->getBounds();
1046         pageXform.mapRect(&shapeBounds);
1047         point = SkPoint{shapeBounds.fLeft, shapeBounds.fBottom};
1048     }
1049     ScopedOutputMarkedContentTags mark(fNodeId, point, fDocument, content);
1050 
1051     SkASSERT(xObject);
1052     SkPDFWriteResourceName(content, SkPDFResourceType::kXObject,
1053                            add_resource(fXObjectResources, xObject));
1054     content->writeText(" Do\n");
1055 }
1056 
makeSurface(const SkImageInfo & info,const SkSurfaceProps & props)1057 sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1058     return SkSurfaces::Raster(info, &props);
1059 }
1060 
sort(const THashSet<SkPDFIndirectReference> & src)1061 static std::vector<SkPDFIndirectReference> sort(const THashSet<SkPDFIndirectReference>& src) {
1062     std::vector<SkPDFIndirectReference> dst;
1063     dst.reserve(src.count());
1064     for (SkPDFIndirectReference ref : src) {
1065         dst.push_back(ref);
1066     }
1067     std::sort(dst.begin(), dst.end(),
1068             [](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
1069     return dst;
1070 }
1071 
makeResourceDict()1072 std::unique_ptr<SkPDFDict> SkPDFDevice::makeResourceDict() {
1073     return SkPDFMakeResourceDict(sort(fGraphicStateResources),
1074                                  sort(fShaderResources),
1075                                  sort(fXObjectResources),
1076                                  sort(fFontResources));
1077 }
1078 
content()1079 std::unique_ptr<SkStreamAsset> SkPDFDevice::content() {
1080     if (fActiveStackState.fContentStream) {
1081         fActiveStackState.drainStack();
1082         fActiveStackState = SkPDFGraphicStackState();
1083     }
1084     if (fContent.bytesWritten() == 0) {
1085         return std::make_unique<SkMemoryStream>();
1086     }
1087     SkDynamicMemoryWStream buffer;
1088     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
1089         SkPDFUtils::AppendTransform(fInitialTransform, &buffer);
1090     }
1091     if (fNeedsExtraSave) {
1092         buffer.writeText("q\n");
1093     }
1094     fContent.writeToAndReset(&buffer);
1095     if (fNeedsExtraSave) {
1096         buffer.writeText("Q\n");
1097     }
1098     fNeedsExtraSave = false;
1099     return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream());
1100 }
1101 
1102 /* Draws an inverse filled path by using Path Ops to compute the positive
1103  * inverse using the current clip as the inverse bounds.
1104  * Return true if this was an inverse path and was properly handled,
1105  * otherwise returns false and the normal drawing routine should continue,
1106  * either as a (incorrect) fallback or because the path was not inverse
1107  * in the first place.
1108  */
handleInversePath(const SkPath & origPath,const SkPaint & paint,bool pathIsMutable)1109 bool SkPDFDevice::handleInversePath(const SkPath& origPath,
1110                                     const SkPaint& paint,
1111                                     bool pathIsMutable) {
1112     if (!origPath.isInverseFillType()) {
1113         return false;
1114     }
1115 
1116     if (this->hasEmptyClip()) {
1117         return false;
1118     }
1119 
1120     SkPath modifiedPath;
1121     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
1122     SkPaint noInversePaint(paint);
1123 
1124     // Merge stroking operations into final path.
1125     if (SkPaint::kStroke_Style == paint.getStyle() ||
1126         SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
1127         bool doFillPath = skpathutils::FillPathWithPaint(origPath, paint, &modifiedPath);
1128         if (doFillPath) {
1129             noInversePaint.setStyle(SkPaint::kFill_Style);
1130             noInversePaint.setStrokeWidth(0);
1131             pathPtr = &modifiedPath;
1132         } else {
1133             // To be consistent with the raster output, hairline strokes
1134             // are rendered as non-inverted.
1135             modifiedPath.toggleInverseFillType();
1136             this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, paint, true);
1137             return true;
1138         }
1139     }
1140 
1141     // Get bounds of clip in current transform space
1142     // (clip bounds are given in device space).
1143     SkMatrix transformInverse;
1144     SkMatrix totalMatrix = this->localToDevice();
1145 
1146     if (!totalMatrix.invert(&transformInverse)) {
1147         return false;
1148     }
1149     SkRect bounds = this->cs().bounds(this->bounds());
1150     transformInverse.mapRect(&bounds);
1151 
1152     // Extend the bounds by the line width (plus some padding)
1153     // so the edge doesn't cause a visible stroke.
1154     bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
1155                   paint.getStrokeWidth() + SK_Scalar1);
1156 
1157     if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
1158         return false;
1159     }
1160 
1161     this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, noInversePaint, true);
1162     return true;
1163 }
1164 
makeFormXObjectFromDevice(SkIRect bounds,bool alpha)1165 SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(SkIRect bounds, bool alpha) {
1166     SkMatrix inverseTransform = SkMatrix::I();
1167     if (!fInitialTransform.isIdentity()) {
1168         if (!fInitialTransform.invert(&inverseTransform)) {
1169             SkDEBUGFAIL("Layer initial transform should be invertible.");
1170             inverseTransform.reset();
1171         }
1172     }
1173     const char* colorSpace = alpha ? "DeviceGray" : nullptr;
1174 
1175     SkPDFIndirectReference xobject =
1176         SkPDFMakeFormXObject(fDocument, this->content(),
1177                              SkPDFMakeArray(bounds.left(), bounds.top(),
1178                                             bounds.right(), bounds.bottom()),
1179                              this->makeResourceDict(), inverseTransform, colorSpace);
1180     // We always draw the form xobjects that we create back into the device, so
1181     // we simply preserve the font usage instead of pulling it out and merging
1182     // it back in later.
1183     this->reset();
1184     return xobject;
1185 }
1186 
makeFormXObjectFromDevice(bool alpha)1187 SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
1188     return this->makeFormXObjectFromDevice(SkIRect{0, 0, this->width(), this->height()}, alpha);
1189 }
1190 
drawFormXObjectWithMask(SkPDFIndirectReference xObject,SkPDFIndirectReference sMask,SkBlendMode mode,bool invertClip)1191 void SkPDFDevice::drawFormXObjectWithMask(SkPDFIndirectReference xObject,
1192                                           SkPDFIndirectReference sMask,
1193                                           SkBlendMode mode,
1194                                           bool invertClip) {
1195     SkASSERT(sMask);
1196     SkPaint paint;
1197     paint.setBlendMode(mode);
1198     ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint);
1199     if (!content) {
1200         return;
1201     }
1202     this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
1203             sMask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
1204             fDocument), content.stream());
1205     this->drawFormXObject(xObject, content.stream(), nullptr);
1206     this->clearMaskOnGraphicState(content.stream());
1207 }
1208 
1209 
treat_as_regular_pdf_blend_mode(SkBlendMode blendMode)1210 static bool treat_as_regular_pdf_blend_mode(SkBlendMode blendMode) {
1211     return nullptr != SkPDFUtils::BlendModeName(blendMode);
1212 }
1213 
populate_graphic_state_entry_from_paint(SkPDFDocument * doc,const SkMatrix & matrix,const SkClipStack * clipStack,SkIRect deviceBounds,const SkPaint & paint,const SkMatrix & initialTransform,SkScalar textScale,SkPDFGraphicStackState::Entry * entry,THashSet<SkPDFIndirectReference> * shaderResources,THashSet<SkPDFIndirectReference> * graphicStateResources)1214 static void populate_graphic_state_entry_from_paint(
1215         SkPDFDocument* doc,
1216         const SkMatrix& matrix,
1217         const SkClipStack* clipStack,
1218         SkIRect deviceBounds,
1219         const SkPaint& paint,
1220         const SkMatrix& initialTransform,
1221         SkScalar textScale,
1222         SkPDFGraphicStackState::Entry* entry,
1223         THashSet<SkPDFIndirectReference>* shaderResources,
1224         THashSet<SkPDFIndirectReference>* graphicStateResources) {
1225     NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
1226     NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
1227     NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
1228 
1229     entry->fMatrix = matrix;
1230     entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
1231                                        : SkClipStack::kWideOpenGenID;
1232     SkColor4f color = paint.getColor4f();
1233     entry->fColor = {color.fR, color.fG, color.fB, 1};
1234     entry->fShaderIndex = -1;
1235 
1236     // PDF treats a shader as a color, so we only set one or the other.
1237     SkShader* shader = paint.getShader();
1238     if (shader) {
1239         // note: we always present the alpha as 1 for the shader, knowing that it will be
1240         //       accounted for when we create our newGraphicsState (below)
1241         if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) {
1242             auto colorShader = static_cast<SkColorShader*>(shader);
1243             // We don't have to set a shader just for a color.
1244             color = SkColor4f::FromColor(colorShader->color());
1245             entry->fColor = {color.fR, color.fG, color.fB, 1};
1246         } else {
1247             // PDF positions patterns relative to the initial transform, so
1248             // we need to apply the current transform to the shader parameters.
1249             SkMatrix transform = matrix;
1250             transform.postConcat(initialTransform);
1251 
1252             // PDF doesn't support kClamp_TileMode, so we simulate it by making
1253             // a pattern the size of the current clip.
1254             SkRect clipStackBounds = clipStack ? clipStack->bounds(deviceBounds)
1255                                                : SkRect::Make(deviceBounds);
1256 
1257             // We need to apply the initial transform to bounds in order to get
1258             // bounds in a consistent coordinate system.
1259             initialTransform.mapRect(&clipStackBounds);
1260             SkIRect bounds;
1261             clipStackBounds.roundOut(&bounds);
1262 
1263             auto c = paint.getColor4f();
1264             SkPDFIndirectReference pdfShader = SkPDFMakeShader(doc, shader, transform, bounds,
1265                                                                {c.fR, c.fG, c.fB, 1.0f});
1266 
1267             if (pdfShader) {
1268                 // pdfShader has been canonicalized so we can directly compare pointers.
1269                 entry->fShaderIndex = add_resource(*shaderResources, pdfShader);
1270             }
1271         }
1272     }
1273 
1274     SkPDFIndirectReference newGraphicState;
1275     if (color == paint.getColor4f()) {
1276         newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, paint);
1277     } else {
1278         SkPaint newPaint = paint;
1279         newPaint.setColor4f(color, nullptr);
1280         newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, newPaint);
1281     }
1282     entry->fGraphicStateIndex = add_resource(*graphicStateResources, newGraphicState);
1283     entry->fTextScaleX = textScale;
1284 }
1285 
setUpContentEntry(const SkClipStack * clipStack,const SkMatrix & matrix,const SkPaint & paint,SkScalar textScale,SkPDFIndirectReference * dst)1286 SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
1287                                                        const SkMatrix& matrix,
1288                                                        const SkPaint& paint,
1289                                                        SkScalar textScale,
1290                                                        SkPDFIndirectReference* dst) {
1291     SkASSERT(!*dst);
1292     SkBlendMode blendMode = paint.getBlendMode_or(SkBlendMode::kSrcOver);
1293 
1294     // Dst xfer mode doesn't draw source at all.
1295     if (blendMode == SkBlendMode::kDst) {
1296         return nullptr;
1297     }
1298 
1299     // For the following modes, we want to handle source and destination
1300     // separately, so make an object of what's already there.
1301     if (!treat_as_regular_pdf_blend_mode(blendMode) && blendMode != SkBlendMode::kDstOver) {
1302         if (!isContentEmpty()) {
1303             *dst = this->makeFormXObjectFromDevice();
1304             SkASSERT(isContentEmpty());
1305         } else if (blendMode != SkBlendMode::kSrc &&
1306                    blendMode != SkBlendMode::kSrcOut) {
1307             // Except for Src and SrcOut, if there isn't anything already there,
1308             // then we're done.
1309             return nullptr;
1310         }
1311     }
1312     // TODO(vandebo): Figure out how/if we can handle the following modes:
1313     // Xor, Plus.  For now, we treat them as SrcOver/Normal.
1314 
1315     if (treat_as_regular_pdf_blend_mode(blendMode)) {
1316         if (!fActiveStackState.fContentStream) {
1317             if (fContent.bytesWritten() != 0) {
1318                 fContent.writeText("Q\nq\n");
1319                 fNeedsExtraSave = true;
1320             }
1321             fActiveStackState = SkPDFGraphicStackState(&fContent);
1322         } else {
1323             SkASSERT(fActiveStackState.fContentStream = &fContent);
1324         }
1325     } else {
1326         fActiveStackState.drainStack();
1327         fActiveStackState = SkPDFGraphicStackState(&fContentBuffer);
1328     }
1329     SkASSERT(fActiveStackState.fContentStream);
1330     SkPDFGraphicStackState::Entry entry;
1331     populate_graphic_state_entry_from_paint(
1332             fDocument,
1333             matrix,
1334             clipStack,
1335             this->bounds(),
1336             paint,
1337             fInitialTransform,
1338             textScale,
1339             &entry,
1340             &fShaderResources,
1341             &fGraphicStateResources);
1342     fActiveStackState.updateClip(clipStack, this->bounds());
1343     fActiveStackState.updateMatrix(entry.fMatrix);
1344     fActiveStackState.updateDrawingState(entry);
1345 
1346     return fActiveStackState.fContentStream;
1347 }
1348 
finishContentEntry(const SkClipStack * clipStack,SkBlendMode blendMode,SkPDFIndirectReference dst,SkPath * shape)1349 void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
1350                                      SkBlendMode blendMode,
1351                                      SkPDFIndirectReference dst,
1352                                      SkPath* shape) {
1353     SkASSERT(blendMode != SkBlendMode::kDst);
1354     if (treat_as_regular_pdf_blend_mode(blendMode)) {
1355         SkASSERT(!dst);
1356         return;
1357     }
1358 
1359     SkASSERT(fActiveStackState.fContentStream);
1360 
1361     fActiveStackState.drainStack();
1362     fActiveStackState = SkPDFGraphicStackState();
1363 
1364     if (blendMode == SkBlendMode::kDstOver) {
1365         SkASSERT(!dst);
1366         if (fContentBuffer.bytesWritten() != 0) {
1367             if (fContent.bytesWritten() != 0) {
1368                 fContentBuffer.writeText("Q\nq\n");
1369                 fNeedsExtraSave = true;
1370             }
1371             fContentBuffer.prependToAndReset(&fContent);
1372             SkASSERT(fContentBuffer.bytesWritten() == 0);
1373         }
1374         return;
1375     }
1376     if (fContentBuffer.bytesWritten() != 0) {
1377         if (fContent.bytesWritten() != 0) {
1378             fContent.writeText("Q\nq\n");
1379             fNeedsExtraSave = true;
1380         }
1381         fContentBuffer.writeToAndReset(&fContent);
1382         SkASSERT(fContentBuffer.bytesWritten() == 0);
1383     }
1384 
1385     if (!dst) {
1386         SkASSERT(blendMode == SkBlendMode::kSrc ||
1387                  blendMode == SkBlendMode::kSrcOut);
1388         return;
1389     }
1390 
1391     SkASSERT(dst);
1392     // Changing the current content into a form-xobject will destroy the clip
1393     // objects which is fine since the xobject will already be clipped. However
1394     // if source has shape, we need to clip it too, so a copy of the clip is
1395     // saved.
1396 
1397     SkPaint stockPaint;
1398 
1399     SkPDFIndirectReference srcFormXObject;
1400     if (this->isContentEmpty()) {
1401         // If nothing was drawn and there's no shape, then the draw was a
1402         // no-op, but dst needs to be restored for that to be true.
1403         // If there is shape, then an empty source with Src, SrcIn, SrcOut,
1404         // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
1405         // reduces to Dst.
1406         if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
1407                 blendMode == SkBlendMode::kSrcATop) {
1408             ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1409             this->drawFormXObject(dst, content.stream(), nullptr);
1410             return;
1411         } else {
1412             blendMode = SkBlendMode::kClear;
1413         }
1414     } else {
1415         srcFormXObject = this->makeFormXObjectFromDevice();
1416     }
1417 
1418     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
1419     // without alpha.
1420     if (blendMode == SkBlendMode::kSrcATop) {
1421         // TODO(vandebo): In order to properly support SrcATop we have to track
1422         // the shape of what's been drawn at all times. It's the intersection of
1423         // the non-transparent parts of the device and the outlines (shape) of
1424         // all images and devices drawn.
1425         this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, true);
1426     } else {
1427         if (shape != nullptr) {
1428             // Draw shape into a form-xobject.
1429             SkPaint filledPaint;
1430             filledPaint.setColor(SK_ColorBLACK);
1431             filledPaint.setStyle(SkPaint::kFill_Style);
1432             SkClipStack empty;
1433             SkPDFDevice shapeDev(this->size(), fDocument, fInitialTransform);
1434             shapeDev.internalDrawPath(clipStack ? *clipStack : empty,
1435                                       SkMatrix::I(), *shape, filledPaint, true);
1436             this->drawFormXObjectWithMask(dst, shapeDev.makeFormXObjectFromDevice(),
1437                                           SkBlendMode::kSrcOver, true);
1438         } else {
1439             this->drawFormXObjectWithMask(dst, srcFormXObject, SkBlendMode::kSrcOver, true);
1440         }
1441     }
1442 
1443     if (blendMode == SkBlendMode::kClear) {
1444         return;
1445     } else if (blendMode == SkBlendMode::kSrc ||
1446             blendMode == SkBlendMode::kDstATop) {
1447         ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1448         if (content) {
1449             this->drawFormXObject(srcFormXObject, content.stream(), nullptr);
1450         }
1451         if (blendMode == SkBlendMode::kSrc) {
1452             return;
1453         }
1454     } else if (blendMode == SkBlendMode::kSrcATop) {
1455         ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1456         if (content) {
1457             this->drawFormXObject(dst, content.stream(), nullptr);
1458         }
1459     }
1460 
1461     SkASSERT(blendMode == SkBlendMode::kSrcIn   ||
1462              blendMode == SkBlendMode::kDstIn   ||
1463              blendMode == SkBlendMode::kSrcOut  ||
1464              blendMode == SkBlendMode::kDstOut  ||
1465              blendMode == SkBlendMode::kSrcATop ||
1466              blendMode == SkBlendMode::kDstATop ||
1467              blendMode == SkBlendMode::kModulate);
1468 
1469     if (blendMode == SkBlendMode::kSrcIn ||
1470             blendMode == SkBlendMode::kSrcOut ||
1471             blendMode == SkBlendMode::kSrcATop) {
1472         this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver,
1473                                       blendMode == SkBlendMode::kSrcOut);
1474         return;
1475     } else {
1476         SkBlendMode mode = SkBlendMode::kSrcOver;
1477         if (blendMode == SkBlendMode::kModulate) {
1478             this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, false);
1479             mode = SkBlendMode::kMultiply;
1480         }
1481         this->drawFormXObjectWithMask(dst, srcFormXObject, mode, blendMode == SkBlendMode::kDstOut);
1482         return;
1483     }
1484 }
1485 
isContentEmpty()1486 bool SkPDFDevice::isContentEmpty() {
1487     return fContent.bytesWritten() == 0 && fContentBuffer.bytesWritten() == 0;
1488 }
1489 
rect_to_size(const SkRect & r)1490 static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
1491 
color_filter(const SkImage * image,SkColorFilter * colorFilter)1492 static sk_sp<SkImage> color_filter(const SkImage* image,
1493                                    SkColorFilter* colorFilter) {
1494     auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(image->dimensions()));
1495     SkASSERT(surface);
1496     SkCanvas* canvas = surface->getCanvas();
1497     canvas->clear(SK_ColorTRANSPARENT);
1498     SkPaint paint;
1499     paint.setColorFilter(sk_ref_sp(colorFilter));
1500     canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
1501     return surface->makeImageSnapshot();
1502 }
1503 
1504 ////////////////////////////////////////////////////////////////////////////////
1505 
is_integer(SkScalar x)1506 static bool is_integer(SkScalar x) {
1507     return x == SkScalarTruncToScalar(x);
1508 }
1509 
is_integral(const SkRect & r)1510 static bool is_integral(const SkRect& r) {
1511     return is_integer(r.left()) &&
1512            is_integer(r.top()) &&
1513            is_integer(r.right()) &&
1514            is_integer(r.bottom());
1515 }
1516 
internalDrawImageRect(SkKeyedImage imageSubset,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & srcPaint,const SkMatrix & ctm)1517 void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
1518                                         const SkRect* src,
1519                                         const SkRect& dst,
1520                                         const SkSamplingOptions& sampling,
1521                                         const SkPaint& srcPaint,
1522                                         const SkMatrix& ctm) {
1523     if (this->hasEmptyClip()) {
1524         return;
1525     }
1526     if (!imageSubset) {
1527         return;
1528     }
1529 
1530     // First, figure out the src->dst transform and subset the image if needed.
1531     SkIRect bounds = imageSubset.image()->bounds();
1532     SkRect srcRect = src ? *src : SkRect::Make(bounds);
1533     SkMatrix transform = SkMatrix::RectToRect(srcRect, dst);
1534     if (src && *src != SkRect::Make(bounds)) {
1535         if (!srcRect.intersect(SkRect::Make(bounds))) {
1536             return;
1537         }
1538         srcRect.roundOut(&bounds);
1539         transform.preTranslate(SkIntToScalar(bounds.x()),
1540                                SkIntToScalar(bounds.y()));
1541         if (bounds != imageSubset.image()->bounds()) {
1542             imageSubset = imageSubset.subset(bounds);
1543         }
1544         if (!imageSubset) {
1545             return;
1546         }
1547     }
1548 
1549     // If the image is opaque and the paint's alpha is too, replace
1550     // kSrc blendmode with kSrcOver.  http://crbug.com/473572
1551     SkTCopyOnFirstWrite<SkPaint> paint(srcPaint);
1552     if (!paint->isSrcOver() &&
1553         imageSubset.image()->isOpaque() &&
1554         SkBlendFastPath::kSrcOver == CheckFastPath(*paint, false))
1555     {
1556         paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
1557     }
1558 
1559     // Alpha-only images need to get their color from the shader, before
1560     // applying the colorfilter.
1561     if (imageSubset.image()->isAlphaOnly() && paint->getColorFilter()) {
1562         // must blend alpha image and shader before applying colorfilter.
1563         auto surface =
1564                 SkSurfaces::Raster(SkImageInfo::MakeN32Premul(imageSubset.image()->dimensions()));
1565         SkCanvas* canvas = surface->getCanvas();
1566         SkPaint tmpPaint;
1567         // In the case of alpha images with shaders, the shader's coordinate
1568         // system is the image's coordiantes.
1569         tmpPaint.setShader(sk_ref_sp(paint->getShader()));
1570         tmpPaint.setColor4f(paint->getColor4f(), nullptr);
1571         canvas->clear(0x00000000);
1572         canvas->drawImage(imageSubset.image().get(), 0, 0, sampling, &tmpPaint);
1573         if (paint->getShader() != nullptr) {
1574             paint.writable()->setShader(nullptr);
1575         }
1576         imageSubset = SkKeyedImage(surface->makeImageSnapshot());
1577         SkASSERT(!imageSubset.image()->isAlphaOnly());
1578     }
1579 
1580     if (imageSubset.image()->isAlphaOnly()) {
1581         // The ColorFilter applies to the paint color/shader, not the alpha layer.
1582         SkASSERT(nullptr == paint->getColorFilter());
1583 
1584         sk_sp<SkImage> mask = alpha_image_to_greyscale_image(imageSubset.image().get());
1585         if (!mask) {
1586             return;
1587         }
1588         // PDF doesn't seem to allow masking vector graphics with an Image XObject.
1589         // Must mask with a Form XObject.
1590         sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
1591         {
1592             SkCanvas canvas(maskDevice);
1593             // This clip prevents the mask image shader from covering
1594             // entire device if unnecessary.
1595             canvas.clipRect(this->cs().bounds(this->bounds()));
1596             canvas.concat(ctm);
1597             if (paint->getMaskFilter()) {
1598                 SkPaint tmpPaint;
1599                 tmpPaint.setShader(mask->makeShader(SkSamplingOptions(), transform));
1600                 tmpPaint.setMaskFilter(sk_ref_sp(paint->getMaskFilter()));
1601                 canvas.drawRect(dst, tmpPaint);
1602             } else {
1603                 if (src && !is_integral(*src)) {
1604                     canvas.clipRect(dst);
1605                 }
1606                 canvas.concat(transform);
1607                 canvas.drawImage(mask, 0, 0);
1608             }
1609         }
1610         SkIRect maskDeviceBounds = maskDevice->cs().bounds(maskDevice->bounds()).roundOut();
1611         if (!ctm.isIdentity() && paint->getShader()) {
1612             transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
1613         }
1614         ScopedContentEntry content(this, &this->cs(), SkMatrix::I(), *paint);
1615         if (!content) {
1616             return;
1617         }
1618         this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
1619                 maskDevice->makeFormXObjectFromDevice(maskDeviceBounds, true), false,
1620                 SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
1621         SkPDFUtils::AppendRectangle(SkRect::Make(this->size()), content.stream());
1622         SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kWinding, content.stream());
1623         this->clearMaskOnGraphicState(content.stream());
1624         return;
1625     }
1626     if (paint->getMaskFilter()) {
1627         paint.writable()->setShader(imageSubset.image()->makeShader(SkSamplingOptions(),
1628                                                                     transform));
1629         SkPath path = SkPath::Rect(dst); // handles non-integral clipping.
1630         this->internalDrawPath(this->cs(), this->localToDevice(), path, *paint, true);
1631         return;
1632     }
1633     transform.postConcat(ctm);
1634 
1635     bool needToRestore = false;
1636     if (src && !is_integral(*src)) {
1637         // Need sub-pixel clipping to fix https://bug.skia.org/4374
1638         this->cs().save();
1639         this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true);
1640         needToRestore = true;
1641     }
1642     SK_AT_SCOPE_EXIT(if (needToRestore) { this->cs().restore(); });
1643 
1644     SkMatrix matrix = transform;
1645 
1646     // Rasterize the bitmap using perspective in a new bitmap.
1647     if (transform.hasPerspective()) {
1648         // Transform the bitmap in the new space, without taking into
1649         // account the initial transform.
1650         SkRect imageBounds = SkRect::Make(imageSubset.image()->bounds());
1651         SkPath perspectiveOutline = SkPath::Rect(imageBounds).makeTransform(transform);
1652 
1653         // Retrieve the bounds of the new shape.
1654         SkRect outlineBounds = perspectiveOutline.getBounds();
1655         if (!outlineBounds.intersect(SkRect::Make(this->devClipBounds()))) {
1656             return;
1657         }
1658 
1659         // Transform the bitmap in the new space to the final space, to account for DPI
1660         SkRect physicalBounds = fInitialTransform.mapRect(outlineBounds);
1661         SkScalar scaleX = physicalBounds.width() / outlineBounds.width();
1662         SkScalar scaleY = physicalBounds.height() / outlineBounds.height();
1663 
1664         // TODO(edisonn): A better approach would be to use a bitmap shader
1665         // (in clamp mode) and draw a rect over the entire bounding box. Then
1666         // intersect perspectiveOutline to the clip. That will avoid introducing
1667         // alpha to the image while still giving good behavior at the edge of
1668         // the image.  Avoiding alpha will reduce the pdf size and generation
1669         // CPU time some.
1670 
1671         SkISize wh = rect_to_size(physicalBounds).toCeil();
1672 
1673         auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(wh));
1674         if (!surface) {
1675             return;
1676         }
1677         SkCanvas* canvas = surface->getCanvas();
1678         canvas->clear(SK_ColorTRANSPARENT);
1679 
1680         SkScalar deltaX = outlineBounds.left();
1681         SkScalar deltaY = outlineBounds.top();
1682 
1683         SkMatrix offsetMatrix = transform;
1684         offsetMatrix.postTranslate(-deltaX, -deltaY);
1685         offsetMatrix.postScale(scaleX, scaleY);
1686 
1687         // Translate the draw in the new canvas, so we perfectly fit the
1688         // shape in the bitmap.
1689         canvas->setMatrix(offsetMatrix);
1690         canvas->drawImage(imageSubset.image(), 0, 0);
1691 
1692         // In the new space, we use the identity matrix translated
1693         // and scaled to reflect DPI.
1694         matrix.setScale(1 / scaleX, 1 / scaleY);
1695         matrix.postTranslate(deltaX, deltaY);
1696 
1697         imageSubset = SkKeyedImage(surface->makeImageSnapshot());
1698         if (!imageSubset) {
1699             return;
1700         }
1701     }
1702 
1703     SkMatrix scaled;
1704     // Adjust for origin flip.
1705     scaled.setScale(SK_Scalar1, -SK_Scalar1);
1706     scaled.postTranslate(0, SK_Scalar1);
1707     // Scale the image up from 1x1 to WxH.
1708     SkIRect subset = imageSubset.image()->bounds();
1709     scaled.postScale(SkIntToScalar(subset.width()),
1710                      SkIntToScalar(subset.height()));
1711     scaled.postConcat(matrix);
1712     ScopedContentEntry content(this, &this->cs(), scaled, *paint);
1713     if (!content) {
1714         return;
1715     }
1716     SkPath shape = SkPath::Rect(SkRect::Make(subset)).makeTransform(matrix);
1717     if (content.needShape()) {
1718         content.setShape(shape);
1719     }
1720     if (!content.needSource()) {
1721         return;
1722     }
1723 
1724     if (SkColorFilter* colorFilter = paint->getColorFilter()) {
1725         sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter);
1726         imageSubset = SkKeyedImage(std::move(img));
1727         if (!imageSubset) {
1728             return;
1729         }
1730         // TODO(halcanary): de-dupe this by caching filtered images.
1731         // (maybe in the resource cache?)
1732     }
1733 
1734     SkBitmapKey key = imageSubset.key();
1735     SkPDFIndirectReference* pdfimagePtr = fDocument->fPDFBitmapMap.find(key);
1736     SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference();
1737     if (!pdfimagePtr) {
1738         SkASSERT(imageSubset);
1739         pdfimage = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
1740                                        fDocument->metadata().fEncodingQuality);
1741         SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
1742         fDocument->fPDFBitmapMap.set(key, pdfimage);
1743     }
1744     SkASSERT(pdfimage != SkPDFIndirectReference());
1745     this->drawFormXObject(pdfimage, content.stream(), &shape);
1746 }
1747 
1748 ///////////////////////////////////////////////////////////////////////////////////////////////////
1749 
1750 
drawDevice(SkDevice * device,const SkSamplingOptions & sampling,const SkPaint & paint)1751 void SkPDFDevice::drawDevice(SkDevice* device, const SkSamplingOptions& sampling,
1752                              const SkPaint& paint) {
1753     SkASSERT(!paint.getImageFilter());
1754     SkASSERT(!paint.getMaskFilter());
1755 
1756     // Check if the source device is really a bitmapdevice (because that's what we returned
1757     // from createDevice (an image filter would go through drawSpecial, but createDevice uses
1758     // a raster device to apply color filters, too).
1759     SkPixmap pmap;
1760     if (device->peekPixels(&pmap)) {
1761         this->SkClipStackDevice::drawDevice(device, sampling, paint);
1762         return;
1763     }
1764 
1765     // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses.
1766     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
1767 
1768     if (pdfDevice->isContentEmpty()) {
1769         return;
1770     }
1771 
1772     SkMatrix matrix = device->getRelativeTransform(*this);
1773     ScopedContentEntry content(this, &this->cs(), matrix, paint);
1774     if (!content) {
1775         return;
1776     }
1777     SkPath shape = SkPath::Rect(SkRect::Make(device->imageInfo().dimensions()));
1778     shape.transform(matrix);
1779     if (content.needShape()) {
1780         content.setShape(shape);
1781     }
1782     if (!content.needSource()) {
1783         return;
1784     }
1785     this->drawFormXObject(pdfDevice->makeFormXObjectFromDevice(), content.stream(), &shape);
1786 }
1787 
drawSpecial(SkSpecialImage * srcImg,const SkMatrix & localToDevice,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint)1788 void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, const SkMatrix& localToDevice,
1789                               const SkSamplingOptions& sampling, const SkPaint& paint,
1790                               SkCanvas::SrcRectConstraint) {
1791     if (this->hasEmptyClip()) {
1792         return;
1793     }
1794     SkASSERT(!srcImg->isGaneshBacked() && !srcImg->isGraphiteBacked());
1795     SkASSERT(!paint.getMaskFilter() && !paint.getImageFilter());
1796 
1797     SkBitmap resultBM;
1798     if (SkSpecialImages::AsBitmap(srcImg, &resultBM)) {
1799         auto r = SkRect::MakeWH(resultBM.width(), resultBM.height());
1800         this->internalDrawImageRect(SkKeyedImage(resultBM), nullptr, r, sampling, paint,
1801                                     localToDevice);
1802     }
1803 }
1804 
makeSpecial(const SkBitmap & bitmap)1805 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) {
1806     return SkSpecialImages::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps());
1807 }
1808 
makeSpecial(const SkImage * image)1809 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) {
1810     return SkSpecialImages::MakeFromRaster(
1811             image->bounds(), image->makeNonTextureImage(), this->surfaceProps());
1812 }
1813