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