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