xref: /third_party/skia/src/pdf/SkPDFUtils.cpp (revision cb93a386)
1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/pdf/SkPDFUtils.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
11cb93a386Sopenharmony_ci#include "include/core/SkData.h"
12cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
13cb93a386Sopenharmony_ci#include "include/core/SkString.h"
14cb93a386Sopenharmony_ci#include "include/private/SkFixed.h"
15cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h"
16cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h"
17cb93a386Sopenharmony_ci#include "src/image/SkImage_Base.h"
18cb93a386Sopenharmony_ci#include "src/pdf/SkPDFResourceDict.h"
19cb93a386Sopenharmony_ci#include "src/pdf/SkPDFTypes.h"
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci#include <cmath>
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ciconst char* SkPDFUtils::BlendModeName(SkBlendMode mode) {
24cb93a386Sopenharmony_ci    // PDF32000.book section 11.3.5 "Blend Mode"
25cb93a386Sopenharmony_ci    switch (mode) {
26cb93a386Sopenharmony_ci        case SkBlendMode::kSrcOver:     return "Normal";
27cb93a386Sopenharmony_ci        case SkBlendMode::kXor:         return "Normal";  // (unsupported mode)
28cb93a386Sopenharmony_ci        case SkBlendMode::kPlus:        return "Normal";  // (unsupported mode)
29cb93a386Sopenharmony_ci        case SkBlendMode::kScreen:      return "Screen";
30cb93a386Sopenharmony_ci        case SkBlendMode::kOverlay:     return "Overlay";
31cb93a386Sopenharmony_ci        case SkBlendMode::kDarken:      return "Darken";
32cb93a386Sopenharmony_ci        case SkBlendMode::kLighten:     return "Lighten";
33cb93a386Sopenharmony_ci        case SkBlendMode::kColorDodge:  return "ColorDodge";
34cb93a386Sopenharmony_ci        case SkBlendMode::kColorBurn:   return "ColorBurn";
35cb93a386Sopenharmony_ci        case SkBlendMode::kHardLight:   return "HardLight";
36cb93a386Sopenharmony_ci        case SkBlendMode::kSoftLight:   return "SoftLight";
37cb93a386Sopenharmony_ci        case SkBlendMode::kDifference:  return "Difference";
38cb93a386Sopenharmony_ci        case SkBlendMode::kExclusion:   return "Exclusion";
39cb93a386Sopenharmony_ci        case SkBlendMode::kMultiply:    return "Multiply";
40cb93a386Sopenharmony_ci        case SkBlendMode::kHue:         return "Hue";
41cb93a386Sopenharmony_ci        case SkBlendMode::kSaturation:  return "Saturation";
42cb93a386Sopenharmony_ci        case SkBlendMode::kColor:       return "Color";
43cb93a386Sopenharmony_ci        case SkBlendMode::kLuminosity:  return "Luminosity";
44cb93a386Sopenharmony_ci        // Other blendmodes are handled in SkPDFDevice::setUpContentEntry.
45cb93a386Sopenharmony_ci        default:                        return nullptr;
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci}
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_cistd::unique_ptr<SkPDFArray> SkPDFUtils::RectToArray(const SkRect& r) {
50cb93a386Sopenharmony_ci    return SkPDFMakeArray(r.left(), r.top(), r.right(), r.bottom());
51cb93a386Sopenharmony_ci}
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_cistd::unique_ptr<SkPDFArray> SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
54cb93a386Sopenharmony_ci    SkScalar a[6];
55cb93a386Sopenharmony_ci    if (!matrix.asAffine(a)) {
56cb93a386Sopenharmony_ci        SkMatrix::SetAffineIdentity(a);
57cb93a386Sopenharmony_ci    }
58cb93a386Sopenharmony_ci    return SkPDFMakeArray(a[0], a[1], a[2], a[3], a[4], a[5]);
59cb93a386Sopenharmony_ci}
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_civoid SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
62cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(x, content);
63cb93a386Sopenharmony_ci    content->writeText(" ");
64cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(y, content);
65cb93a386Sopenharmony_ci    content->writeText(" m\n");
66cb93a386Sopenharmony_ci}
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_civoid SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
69cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(x, content);
70cb93a386Sopenharmony_ci    content->writeText(" ");
71cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(y, content);
72cb93a386Sopenharmony_ci    content->writeText(" l\n");
73cb93a386Sopenharmony_ci}
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_cistatic void append_cubic(SkScalar ctl1X, SkScalar ctl1Y,
76cb93a386Sopenharmony_ci                         SkScalar ctl2X, SkScalar ctl2Y,
77cb93a386Sopenharmony_ci                         SkScalar dstX, SkScalar dstY, SkWStream* content) {
78cb93a386Sopenharmony_ci    SkString cmd("y\n");
79cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(ctl1X, content);
80cb93a386Sopenharmony_ci    content->writeText(" ");
81cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(ctl1Y, content);
82cb93a386Sopenharmony_ci    content->writeText(" ");
83cb93a386Sopenharmony_ci    if (ctl2X != dstX || ctl2Y != dstY) {
84cb93a386Sopenharmony_ci        cmd.set("c\n");
85cb93a386Sopenharmony_ci        SkPDFUtils::AppendScalar(ctl2X, content);
86cb93a386Sopenharmony_ci        content->writeText(" ");
87cb93a386Sopenharmony_ci        SkPDFUtils::AppendScalar(ctl2Y, content);
88cb93a386Sopenharmony_ci        content->writeText(" ");
89cb93a386Sopenharmony_ci    }
90cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(dstX, content);
91cb93a386Sopenharmony_ci    content->writeText(" ");
92cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(dstY, content);
93cb93a386Sopenharmony_ci    content->writeText(" ");
94cb93a386Sopenharmony_ci    content->writeText(cmd.c_str());
95cb93a386Sopenharmony_ci}
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_cistatic void append_quad(const SkPoint quad[], SkWStream* content) {
98cb93a386Sopenharmony_ci    SkPoint cubic[4];
99cb93a386Sopenharmony_ci    SkConvertQuadToCubic(quad, cubic);
100cb93a386Sopenharmony_ci    append_cubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
101cb93a386Sopenharmony_ci                 cubic[3].fX, cubic[3].fY, content);
102cb93a386Sopenharmony_ci}
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_civoid SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
105cb93a386Sopenharmony_ci    // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
106cb93a386Sopenharmony_ci    SkScalar bottom = std::min(rect.fBottom, rect.fTop);
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(rect.fLeft, content);
109cb93a386Sopenharmony_ci    content->writeText(" ");
110cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(bottom, content);
111cb93a386Sopenharmony_ci    content->writeText(" ");
112cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(rect.width(), content);
113cb93a386Sopenharmony_ci    content->writeText(" ");
114cb93a386Sopenharmony_ci    SkPDFUtils::AppendScalar(rect.height(), content);
115cb93a386Sopenharmony_ci    content->writeText(" re\n");
116cb93a386Sopenharmony_ci}
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_civoid SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
119cb93a386Sopenharmony_ci                          bool doConsumeDegerates, SkWStream* content,
120cb93a386Sopenharmony_ci                          SkScalar tolerance) {
121cb93a386Sopenharmony_ci    if (path.isEmpty() && SkPaint::kFill_Style == paintStyle) {
122cb93a386Sopenharmony_ci        SkPDFUtils::AppendRectangle({0, 0, 0, 0}, content);
123cb93a386Sopenharmony_ci        return;
124cb93a386Sopenharmony_ci    }
125cb93a386Sopenharmony_ci    // Filling a path with no area results in a drawing in PDF renderers but
126cb93a386Sopenharmony_ci    // Chrome expects to be able to draw some such entities with no visible
127cb93a386Sopenharmony_ci    // result, so we detect those cases and discard the drawing for them.
128cb93a386Sopenharmony_ci    // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci    SkRect rect;
131cb93a386Sopenharmony_ci    bool isClosed; // Both closure and direction need to be checked.
132cb93a386Sopenharmony_ci    SkPathDirection direction;
133cb93a386Sopenharmony_ci    if (path.isRect(&rect, &isClosed, &direction) &&
134cb93a386Sopenharmony_ci        isClosed &&
135cb93a386Sopenharmony_ci        (SkPathDirection::kCW == direction ||
136cb93a386Sopenharmony_ci         SkPathFillType::kEvenOdd == path.getFillType()))
137cb93a386Sopenharmony_ci    {
138cb93a386Sopenharmony_ci        SkPDFUtils::AppendRectangle(rect, content);
139cb93a386Sopenharmony_ci        return;
140cb93a386Sopenharmony_ci    }
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    enum SkipFillState {
143cb93a386Sopenharmony_ci        kEmpty_SkipFillState,
144cb93a386Sopenharmony_ci        kSingleLine_SkipFillState,
145cb93a386Sopenharmony_ci        kNonSingleLine_SkipFillState,
146cb93a386Sopenharmony_ci    };
147cb93a386Sopenharmony_ci    SkipFillState fillState = kEmpty_SkipFillState;
148cb93a386Sopenharmony_ci    //if (paintStyle != SkPaint::kFill_Style) {
149cb93a386Sopenharmony_ci    //    fillState = kNonSingleLine_SkipFillState;
150cb93a386Sopenharmony_ci    //}
151cb93a386Sopenharmony_ci    SkPoint lastMovePt = SkPoint::Make(0,0);
152cb93a386Sopenharmony_ci    SkDynamicMemoryWStream currentSegment;
153cb93a386Sopenharmony_ci    SkPoint args[4];
154cb93a386Sopenharmony_ci    SkPath::Iter iter(path, false);
155cb93a386Sopenharmony_ci    for (SkPath::Verb verb = iter.next(args);
156cb93a386Sopenharmony_ci         verb != SkPath::kDone_Verb;
157cb93a386Sopenharmony_ci         verb = iter.next(args)) {
158cb93a386Sopenharmony_ci        // args gets all the points, even the implicit first point.
159cb93a386Sopenharmony_ci        switch (verb) {
160cb93a386Sopenharmony_ci            case SkPath::kMove_Verb:
161cb93a386Sopenharmony_ci                MoveTo(args[0].fX, args[0].fY, &currentSegment);
162cb93a386Sopenharmony_ci                lastMovePt = args[0];
163cb93a386Sopenharmony_ci                fillState = kEmpty_SkipFillState;
164cb93a386Sopenharmony_ci                break;
165cb93a386Sopenharmony_ci            case SkPath::kLine_Verb:
166cb93a386Sopenharmony_ci                if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 2)) {
167cb93a386Sopenharmony_ci                    AppendLine(args[1].fX, args[1].fY, &currentSegment);
168cb93a386Sopenharmony_ci                    if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
169cb93a386Sopenharmony_ci                        fillState = kSingleLine_SkipFillState;
170cb93a386Sopenharmony_ci                        break;
171cb93a386Sopenharmony_ci                    }
172cb93a386Sopenharmony_ci                    fillState = kNonSingleLine_SkipFillState;
173cb93a386Sopenharmony_ci                }
174cb93a386Sopenharmony_ci                break;
175cb93a386Sopenharmony_ci            case SkPath::kQuad_Verb:
176cb93a386Sopenharmony_ci                if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
177cb93a386Sopenharmony_ci                    append_quad(args, &currentSegment);
178cb93a386Sopenharmony_ci                    fillState = kNonSingleLine_SkipFillState;
179cb93a386Sopenharmony_ci                }
180cb93a386Sopenharmony_ci                break;
181cb93a386Sopenharmony_ci            case SkPath::kConic_Verb:
182cb93a386Sopenharmony_ci                if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
183cb93a386Sopenharmony_ci                    SkAutoConicToQuads converter;
184cb93a386Sopenharmony_ci                    const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tolerance);
185cb93a386Sopenharmony_ci                    for (int i = 0; i < converter.countQuads(); ++i) {
186cb93a386Sopenharmony_ci                        append_quad(&quads[i * 2], &currentSegment);
187cb93a386Sopenharmony_ci                    }
188cb93a386Sopenharmony_ci                    fillState = kNonSingleLine_SkipFillState;
189cb93a386Sopenharmony_ci                }
190cb93a386Sopenharmony_ci                break;
191cb93a386Sopenharmony_ci            case SkPath::kCubic_Verb:
192cb93a386Sopenharmony_ci                if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 4)) {
193cb93a386Sopenharmony_ci                    append_cubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
194cb93a386Sopenharmony_ci                                 args[3].fX, args[3].fY, &currentSegment);
195cb93a386Sopenharmony_ci                    fillState = kNonSingleLine_SkipFillState;
196cb93a386Sopenharmony_ci                }
197cb93a386Sopenharmony_ci                break;
198cb93a386Sopenharmony_ci            case SkPath::kClose_Verb:
199cb93a386Sopenharmony_ci                ClosePath(&currentSegment);
200cb93a386Sopenharmony_ci                currentSegment.writeToStream(content);
201cb93a386Sopenharmony_ci                currentSegment.reset();
202cb93a386Sopenharmony_ci                break;
203cb93a386Sopenharmony_ci            default:
204cb93a386Sopenharmony_ci                SkASSERT(false);
205cb93a386Sopenharmony_ci                break;
206cb93a386Sopenharmony_ci        }
207cb93a386Sopenharmony_ci    }
208cb93a386Sopenharmony_ci    if (currentSegment.bytesWritten() > 0) {
209cb93a386Sopenharmony_ci        currentSegment.writeToStream(content);
210cb93a386Sopenharmony_ci    }
211cb93a386Sopenharmony_ci}
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_civoid SkPDFUtils::ClosePath(SkWStream* content) {
214cb93a386Sopenharmony_ci    content->writeText("h\n");
215cb93a386Sopenharmony_ci}
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_civoid SkPDFUtils::PaintPath(SkPaint::Style style, SkPathFillType fill, SkWStream* content) {
218cb93a386Sopenharmony_ci    if (style == SkPaint::kFill_Style) {
219cb93a386Sopenharmony_ci        content->writeText("f");
220cb93a386Sopenharmony_ci    } else if (style == SkPaint::kStrokeAndFill_Style) {
221cb93a386Sopenharmony_ci        content->writeText("B");
222cb93a386Sopenharmony_ci    } else if (style == SkPaint::kStroke_Style) {
223cb93a386Sopenharmony_ci        content->writeText("S");
224cb93a386Sopenharmony_ci    }
225cb93a386Sopenharmony_ci
226cb93a386Sopenharmony_ci    if (style != SkPaint::kStroke_Style) {
227cb93a386Sopenharmony_ci        NOT_IMPLEMENTED(fill == SkPathFillType::kInverseEvenOdd, false);
228cb93a386Sopenharmony_ci        NOT_IMPLEMENTED(fill == SkPathFillType::kInverseWinding, false);
229cb93a386Sopenharmony_ci        if (fill == SkPathFillType::kEvenOdd) {
230cb93a386Sopenharmony_ci            content->writeText("*");
231cb93a386Sopenharmony_ci        }
232cb93a386Sopenharmony_ci    }
233cb93a386Sopenharmony_ci    content->writeText("\n");
234cb93a386Sopenharmony_ci}
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_civoid SkPDFUtils::StrokePath(SkWStream* content) {
237cb93a386Sopenharmony_ci    SkPDFUtils::PaintPath(SkPaint::kStroke_Style, SkPathFillType::kWinding, content);
238cb93a386Sopenharmony_ci}
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_civoid SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
241cb93a386Sopenharmony_ci    SkPDFWriteResourceName(content, SkPDFResourceType::kExtGState, objectIndex);
242cb93a386Sopenharmony_ci    content->writeText(" gs\n");
243cb93a386Sopenharmony_ci}
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_civoid SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
246cb93a386Sopenharmony_ci    // Select Pattern color space (CS, cs) and set pattern object as current
247cb93a386Sopenharmony_ci    // color (SCN, scn)
248cb93a386Sopenharmony_ci    content->writeText("/Pattern CS/Pattern cs");
249cb93a386Sopenharmony_ci    SkPDFWriteResourceName(content, SkPDFResourceType::kPattern, objectIndex);
250cb93a386Sopenharmony_ci    content->writeText(" SCN");
251cb93a386Sopenharmony_ci    SkPDFWriteResourceName(content, SkPDFResourceType::kPattern, objectIndex);
252cb93a386Sopenharmony_ci    content->writeText(" scn\n");
253cb93a386Sopenharmony_ci}
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_ci// return "x/pow(10, places)", given 0<x<pow(10, places)
256cb93a386Sopenharmony_ci// result points to places+2 chars.
257cb93a386Sopenharmony_cistatic size_t print_permil_as_decimal(int x, char* result, unsigned places) {
258cb93a386Sopenharmony_ci    result[0] = '.';
259cb93a386Sopenharmony_ci    for (int i = places; i > 0; --i) {
260cb93a386Sopenharmony_ci        result[i] = '0' + x % 10;
261cb93a386Sopenharmony_ci        x /= 10;
262cb93a386Sopenharmony_ci    }
263cb93a386Sopenharmony_ci    int j;
264cb93a386Sopenharmony_ci    for (j = places; j > 1; --j) {
265cb93a386Sopenharmony_ci        if (result[j] != '0') {
266cb93a386Sopenharmony_ci            break;
267cb93a386Sopenharmony_ci        }
268cb93a386Sopenharmony_ci    }
269cb93a386Sopenharmony_ci    result[j + 1] = '\0';
270cb93a386Sopenharmony_ci    return j + 1;
271cb93a386Sopenharmony_ci}
272cb93a386Sopenharmony_ci
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_cistatic constexpr int int_pow(int base, unsigned exp, int acc = 1) {
275cb93a386Sopenharmony_ci  return exp < 1 ? acc
276cb93a386Sopenharmony_ci                 : int_pow(base * base,
277cb93a386Sopenharmony_ci                           exp / 2,
278cb93a386Sopenharmony_ci                           (exp % 2) ? acc * base : acc);
279cb93a386Sopenharmony_ci}
280cb93a386Sopenharmony_ci
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_cisize_t SkPDFUtils::ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]) {
283cb93a386Sopenharmony_ci    static constexpr int kFactor = int_pow(10, kFloatColorDecimalCount);
284cb93a386Sopenharmony_ci    int x = sk_float_round2int(value * kFactor);
285cb93a386Sopenharmony_ci    if (x >= kFactor || x <= 0) {  // clamp to 0-1
286cb93a386Sopenharmony_ci        result[0] = x > 0 ? '1' : '0';
287cb93a386Sopenharmony_ci        result[1] = '\0';
288cb93a386Sopenharmony_ci        return 1;
289cb93a386Sopenharmony_ci    }
290cb93a386Sopenharmony_ci    return print_permil_as_decimal(x, result, kFloatColorDecimalCount);
291cb93a386Sopenharmony_ci}
292cb93a386Sopenharmony_ci
293cb93a386Sopenharmony_cisize_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
294cb93a386Sopenharmony_ci    if (value == 255 || value == 0) {
295cb93a386Sopenharmony_ci        result[0] = value ? '1' : '0';
296cb93a386Sopenharmony_ci        result[1] = '\0';
297cb93a386Sopenharmony_ci        return 1;
298cb93a386Sopenharmony_ci    }
299cb93a386Sopenharmony_ci    // int x = 0.5 + (1000.0 / 255.0) * value;
300cb93a386Sopenharmony_ci    int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value);
301cb93a386Sopenharmony_ci    return print_permil_as_decimal(x, result, 3);
302cb93a386Sopenharmony_ci}
303cb93a386Sopenharmony_ci
304cb93a386Sopenharmony_cibool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
305cb93a386Sopenharmony_ci    SkMatrix inverse;
306cb93a386Sopenharmony_ci    if (!matrix.invert(&inverse)) {
307cb93a386Sopenharmony_ci        return false;
308cb93a386Sopenharmony_ci    }
309cb93a386Sopenharmony_ci    inverse.mapRect(bbox);
310cb93a386Sopenharmony_ci    return true;
311cb93a386Sopenharmony_ci}
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_civoid SkPDFUtils::PopulateTilingPatternDict(SkPDFDict* pattern,
314cb93a386Sopenharmony_ci                                           SkRect& bbox,
315cb93a386Sopenharmony_ci                                           std::unique_ptr<SkPDFDict> resources,
316cb93a386Sopenharmony_ci                                           const SkMatrix& matrix) {
317cb93a386Sopenharmony_ci    const int kTiling_PatternType = 1;
318cb93a386Sopenharmony_ci    const int kColoredTilingPattern_PaintType = 1;
319cb93a386Sopenharmony_ci    const int kConstantSpacing_TilingType = 1;
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci    pattern->insertName("Type", "Pattern");
322cb93a386Sopenharmony_ci    pattern->insertInt("PatternType", kTiling_PatternType);
323cb93a386Sopenharmony_ci    pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
324cb93a386Sopenharmony_ci    pattern->insertInt("TilingType", kConstantSpacing_TilingType);
325cb93a386Sopenharmony_ci    pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
326cb93a386Sopenharmony_ci    pattern->insertScalar("XStep", bbox.width());
327cb93a386Sopenharmony_ci    pattern->insertScalar("YStep", bbox.height());
328cb93a386Sopenharmony_ci    pattern->insertObject("Resources", std::move(resources));
329cb93a386Sopenharmony_ci    if (!matrix.isIdentity()) {
330cb93a386Sopenharmony_ci        pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
331cb93a386Sopenharmony_ci    }
332cb93a386Sopenharmony_ci}
333cb93a386Sopenharmony_ci
334cb93a386Sopenharmony_cibool SkPDFUtils::ToBitmap(const SkImage* img, SkBitmap* dst) {
335cb93a386Sopenharmony_ci    SkASSERT(img);
336cb93a386Sopenharmony_ci    SkASSERT(dst);
337cb93a386Sopenharmony_ci    SkBitmap bitmap;
338cb93a386Sopenharmony_ci    // TODO: support GPU images
339cb93a386Sopenharmony_ci    if(as_IB(img)->getROPixels(nullptr, &bitmap)) {
340cb93a386Sopenharmony_ci        SkASSERT(bitmap.dimensions() == img->dimensions());
341cb93a386Sopenharmony_ci        SkASSERT(!bitmap.drawsNothing());
342cb93a386Sopenharmony_ci        *dst = std::move(bitmap);
343cb93a386Sopenharmony_ci        return true;
344cb93a386Sopenharmony_ci    }
345cb93a386Sopenharmony_ci    return false;
346cb93a386Sopenharmony_ci}
347cb93a386Sopenharmony_ci
348cb93a386Sopenharmony_ci#ifdef SK_PDF_BASE85_BINARY
349cb93a386Sopenharmony_civoid SkPDFUtils::Base85Encode(std::unique_ptr<SkStreamAsset> stream, SkDynamicMemoryWStream* dst) {
350cb93a386Sopenharmony_ci    SkASSERT(dst);
351cb93a386Sopenharmony_ci    SkASSERT(stream);
352cb93a386Sopenharmony_ci    dst->writeText("\n");
353cb93a386Sopenharmony_ci    int column = 0;
354cb93a386Sopenharmony_ci    while (true) {
355cb93a386Sopenharmony_ci        uint8_t src[4] = {0, 0, 0, 0};
356cb93a386Sopenharmony_ci        size_t count = stream->read(src, 4);
357cb93a386Sopenharmony_ci        SkASSERT(count < 5);
358cb93a386Sopenharmony_ci        if (0 == count) {
359cb93a386Sopenharmony_ci            dst->writeText("~>\n");
360cb93a386Sopenharmony_ci            return;
361cb93a386Sopenharmony_ci        }
362cb93a386Sopenharmony_ci        uint32_t v = ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) |
363cb93a386Sopenharmony_ci                     ((uint32_t)src[2] <<  8) | src[3];
364cb93a386Sopenharmony_ci        if (v == 0 && count == 4) {
365cb93a386Sopenharmony_ci            dst->writeText("z");
366cb93a386Sopenharmony_ci            column += 1;
367cb93a386Sopenharmony_ci        } else {
368cb93a386Sopenharmony_ci            char buffer[5];
369cb93a386Sopenharmony_ci            for (int n = 4; n > 0; --n) {
370cb93a386Sopenharmony_ci                buffer[n] = (v % 85) + '!';
371cb93a386Sopenharmony_ci                v /= 85;
372cb93a386Sopenharmony_ci            }
373cb93a386Sopenharmony_ci            buffer[0] = v + '!';
374cb93a386Sopenharmony_ci            dst->write(buffer, count + 1);
375cb93a386Sopenharmony_ci            column += count + 1;
376cb93a386Sopenharmony_ci        }
377cb93a386Sopenharmony_ci        if (column > 74) {
378cb93a386Sopenharmony_ci            dst->writeText("\n");
379cb93a386Sopenharmony_ci            column = 0;
380cb93a386Sopenharmony_ci        }
381cb93a386Sopenharmony_ci    }
382cb93a386Sopenharmony_ci}
383cb93a386Sopenharmony_ci#endif //  SK_PDF_BASE85_BINARY
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_civoid SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
386cb93a386Sopenharmony_ci    SkScalar values[6];
387cb93a386Sopenharmony_ci    if (!matrix.asAffine(values)) {
388cb93a386Sopenharmony_ci        SkMatrix::SetAffineIdentity(values);
389cb93a386Sopenharmony_ci    }
390cb93a386Sopenharmony_ci    for (SkScalar v : values) {
391cb93a386Sopenharmony_ci        SkPDFUtils::AppendScalar(v, content);
392cb93a386Sopenharmony_ci        content->writeText(" ");
393cb93a386Sopenharmony_ci    }
394cb93a386Sopenharmony_ci    content->writeText("cm\n");
395cb93a386Sopenharmony_ci}
396