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