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