blob: 9d1c13adfd8a5b47c5b4a0a23c4f1d9d654fbce9 [file] [log] [blame]
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2011 Google Inc.
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/pdf/SkPDFUtils.h"
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkData.h"
11#include "include/core/SkStream.h"
12#include "include/core/SkString.h"
13#include "include/private/SkFixed.h"
14#include "src/core/SkGeometry.h"
Mike Reedba7e9a62019-08-16 13:30:34 -040015#include "src/core/SkPathPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/image/SkImage_Base.h"
17#include "src/pdf/SkPDFResourceDict.h"
18#include "src/pdf/SkPDFTypes.h"
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000019
halcanary8e9f5e32016-02-24 15:46:46 -080020#include <cmath>
21
Hal Canaryb400d4d2018-09-26 16:33:52 -040022const char* SkPDFUtils::BlendModeName(SkBlendMode mode) {
23 // PDF32000.book section 11.3.5 "Blend Mode"
24 switch (mode) {
25 case SkBlendMode::kSrcOver: return "Normal";
26 case SkBlendMode::kXor: return "Normal"; // (unsupported mode)
27 case SkBlendMode::kPlus: return "Normal"; // (unsupported mode)
28 case SkBlendMode::kScreen: return "Screen";
29 case SkBlendMode::kOverlay: return "Overlay";
30 case SkBlendMode::kDarken: return "Darken";
31 case SkBlendMode::kLighten: return "Lighten";
32 case SkBlendMode::kColorDodge: return "ColorDodge";
33 case SkBlendMode::kColorBurn: return "ColorBurn";
34 case SkBlendMode::kHardLight: return "HardLight";
35 case SkBlendMode::kSoftLight: return "SoftLight";
36 case SkBlendMode::kDifference: return "Difference";
37 case SkBlendMode::kExclusion: return "Exclusion";
38 case SkBlendMode::kMultiply: return "Multiply";
39 case SkBlendMode::kHue: return "Hue";
40 case SkBlendMode::kSaturation: return "Saturation";
41 case SkBlendMode::kColor: return "Color";
42 case SkBlendMode::kLuminosity: return "Luminosity";
43 // Other blendmodes are handled in SkPDFDevice::setUpContentEntry.
44 default: return nullptr;
45 }
46}
47
Hal Canary74801582018-12-18 16:30:41 -050048std::unique_ptr<SkPDFArray> SkPDFUtils::RectToArray(const SkRect& r) {
Hal Canarye650b852018-09-12 09:12:36 -040049 return SkPDFMakeArray(r.left(), r.top(), r.right(), r.bottom());
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +000050}
51
Hal Canary74801582018-12-18 16:30:41 -050052std::unique_ptr<SkPDFArray> SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
Hal Canarye650b852018-09-12 09:12:36 -040053 SkScalar a[6];
54 if (!matrix.asAffine(a)) {
55 SkMatrix::SetAffineIdentity(a);
bungeman@google.com1ddd7c32011-07-13 19:41:55 +000056 }
Hal Canarye650b852018-09-12 09:12:36 -040057 return SkPDFMakeArray(a[0], a[1], a[2], a[3], a[4], a[5]);
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000058}
59
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000060void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
halcanarybc4696b2015-05-06 10:56:04 -070061 SkPDFUtils::AppendScalar(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000062 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -070063 SkPDFUtils::AppendScalar(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000064 content->writeText(" m\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000065}
66
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000067void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
halcanarybc4696b2015-05-06 10:56:04 -070068 SkPDFUtils::AppendScalar(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000069 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -070070 SkPDFUtils::AppendScalar(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000071 content->writeText(" l\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000072}
73
Hal Canarye650b852018-09-12 09:12:36 -040074static void append_cubic(SkScalar ctl1X, SkScalar ctl1Y,
75 SkScalar ctl2X, SkScalar ctl2Y,
76 SkScalar dstX, SkScalar dstY, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000077 SkString cmd("y\n");
halcanarybc4696b2015-05-06 10:56:04 -070078 SkPDFUtils::AppendScalar(ctl1X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000079 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -070080 SkPDFUtils::AppendScalar(ctl1Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000081 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000082 if (ctl2X != dstX || ctl2Y != dstY) {
83 cmd.set("c\n");
halcanarybc4696b2015-05-06 10:56:04 -070084 SkPDFUtils::AppendScalar(ctl2X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000085 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -070086 SkPDFUtils::AppendScalar(ctl2Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000087 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000088 }
halcanarybc4696b2015-05-06 10:56:04 -070089 SkPDFUtils::AppendScalar(dstX, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000090 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -070091 SkPDFUtils::AppendScalar(dstY, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000092 content->writeText(" ");
93 content->writeText(cmd.c_str());
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000094}
95
reed40c85e42015-01-05 10:01:25 -080096static void append_quad(const SkPoint quad[], SkWStream* content) {
97 SkPoint cubic[4];
98 SkConvertQuadToCubic(quad, cubic);
Hal Canarye650b852018-09-12 09:12:36 -040099 append_cubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
100 cubic[3].fX, cubic[3].fY, content);
reed40c85e42015-01-05 10:01:25 -0800101}
102
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000103void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
104 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
Brian Osman116b33e2020-02-05 13:34:09 -0500105 SkScalar bottom = std::min(rect.fBottom, rect.fTop);
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000106
halcanarybc4696b2015-05-06 10:56:04 -0700107 SkPDFUtils::AppendScalar(rect.fLeft, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000108 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -0700109 SkPDFUtils::AppendScalar(bottom, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000110 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -0700111 SkPDFUtils::AppendScalar(rect.width(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000112 content->writeText(" ");
halcanarybc4696b2015-05-06 10:56:04 -0700113 SkPDFUtils::AppendScalar(rect.height(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000114 content->writeText(" re\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000115}
116
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000117void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
Hal Canary385468f2017-02-13 11:03:23 -0500118 bool doConsumeDegerates, SkWStream* content,
119 SkScalar tolerance) {
Hal Canary4e83ff12018-03-09 12:16:42 -0500120 if (path.isEmpty() && SkPaint::kFill_Style == paintStyle) {
121 SkPDFUtils::AppendRectangle({0, 0, 0, 0}, content);
122 return;
123 }
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000124 // Filling a path with no area results in a drawing in PDF renderers but
125 // Chrome expects to be able to draw some such entities with no visible
126 // result, so we detect those cases and discard the drawing for them.
127 // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
Hal Canary970225b2016-11-10 11:46:21 -0500128
129 SkRect rect;
130 bool isClosed; // Both closure and direction need to be checked.
Mike Reed30bc5272019-11-22 18:34:02 +0000131 SkPathDirection direction;
Hal Canary970225b2016-11-10 11:46:21 -0500132 if (path.isRect(&rect, &isClosed, &direction) &&
Hal Canaryfc3afa92017-07-19 14:19:14 -0400133 isClosed &&
Mike Reed30bc5272019-11-22 18:34:02 +0000134 (SkPathDirection::kCW == direction ||
Mike Reedcf0e3c62019-12-03 16:26:15 -0500135 SkPathFillType::kEvenOdd == path.getFillType()))
Hal Canary970225b2016-11-10 11:46:21 -0500136 {
137 SkPDFUtils::AppendRectangle(rect, content);
138 return;
Hal Canary385468f2017-02-13 11:03:23 -0500139 }
Hal Canary970225b2016-11-10 11:46:21 -0500140
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000141 enum SkipFillState {
halcanary8b2bc252015-10-06 09:41:47 -0700142 kEmpty_SkipFillState,
143 kSingleLine_SkipFillState,
144 kNonSingleLine_SkipFillState,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000145 };
146 SkipFillState fillState = kEmpty_SkipFillState;
halcanary8b2bc252015-10-06 09:41:47 -0700147 //if (paintStyle != SkPaint::kFill_Style) {
148 // fillState = kNonSingleLine_SkipFillState;
149 //}
sugoi@google.come2e81132013-03-05 18:35:55 +0000150 SkPoint lastMovePt = SkPoint::Make(0,0);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000151 SkDynamicMemoryWStream currentSegment;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000152 SkPoint args[4];
153 SkPath::Iter iter(path, false);
Mike Reedba7e9a62019-08-16 13:30:34 -0400154 for (SkPath::Verb verb = iter.next(args);
halcanary8b2bc252015-10-06 09:41:47 -0700155 verb != SkPath::kDone_Verb;
Mike Reedba7e9a62019-08-16 13:30:34 -0400156 verb = iter.next(args)) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000157 // args gets all the points, even the implicit first point.
158 switch (verb) {
159 case SkPath::kMove_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000160 MoveTo(args[0].fX, args[0].fY, &currentSegment);
161 lastMovePt = args[0];
162 fillState = kEmpty_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000163 break;
164 case SkPath::kLine_Verb:
Mike Reedba7e9a62019-08-16 13:30:34 -0400165 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 2)) {
166 AppendLine(args[1].fX, args[1].fY, &currentSegment);
167 if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
168 fillState = kSingleLine_SkipFillState;
169 break;
170 }
171 fillState = kNonSingleLine_SkipFillState;
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000172 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000173 break;
reed40c85e42015-01-05 10:01:25 -0800174 case SkPath::kQuad_Verb:
Mike Reedba7e9a62019-08-16 13:30:34 -0400175 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
176 append_quad(args, &currentSegment);
177 fillState = kNonSingleLine_SkipFillState;
reed40c85e42015-01-05 10:01:25 -0800178 }
Mike Reedba7e9a62019-08-16 13:30:34 -0400179 break;
180 case SkPath::kConic_Verb:
181 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
182 SkAutoConicToQuads converter;
183 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tolerance);
184 for (int i = 0; i < converter.countQuads(); ++i) {
185 append_quad(&quads[i * 2], &currentSegment);
186 }
187 fillState = kNonSingleLine_SkipFillState;
188 }
189 break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000190 case SkPath::kCubic_Verb:
Mike Reedba7e9a62019-08-16 13:30:34 -0400191 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 4)) {
192 append_cubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
193 args[3].fX, args[3].fY, &currentSegment);
194 fillState = kNonSingleLine_SkipFillState;
195 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000196 break;
197 case SkPath::kClose_Verb:
Hal Canary385468f2017-02-13 11:03:23 -0500198 ClosePath(&currentSegment);
halcanary8b2bc252015-10-06 09:41:47 -0700199 currentSegment.writeToStream(content);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000200 currentSegment.reset();
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000201 break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000202 default:
203 SkASSERT(false);
204 break;
205 }
206 }
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000207 if (currentSegment.bytesWritten() > 0) {
halcanary7af21502015-02-23 12:17:59 -0800208 currentSegment.writeToStream(content);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000209 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000210}
211
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000212void SkPDFUtils::ClosePath(SkWStream* content) {
213 content->writeText("h\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000214}
215
Mike Reed7d34dc72019-11-26 12:17:17 -0500216void SkPDFUtils::PaintPath(SkPaint::Style style, SkPathFillType fill, SkWStream* content) {
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000217 if (style == SkPaint::kFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000218 content->writeText("f");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000219 } else if (style == SkPaint::kStrokeAndFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000220 content->writeText("B");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000221 } else if (style == SkPaint::kStroke_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000222 content->writeText("S");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000223 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000224
225 if (style != SkPaint::kStroke_Style) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500226 NOT_IMPLEMENTED(fill == SkPathFillType::kInverseEvenOdd, false);
227 NOT_IMPLEMENTED(fill == SkPathFillType::kInverseWinding, false);
228 if (fill == SkPathFillType::kEvenOdd) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000229 content->writeText("*");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000230 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000231 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000232 content->writeText("\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000233}
234
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000235void SkPDFUtils::StrokePath(SkWStream* content) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500236 SkPDFUtils::PaintPath(SkPaint::kStroke_Style, SkPathFillType::kWinding, content);
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000237}
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000238
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000239void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
Hal Canarye650b852018-09-12 09:12:36 -0400240 SkPDFWriteResourceName(content, SkPDFResourceType::kExtGState, objectIndex);
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000241 content->writeText(" gs\n");
242}
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000243
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000244void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
245 // Select Pattern color space (CS, cs) and set pattern object as current
246 // color (SCN, scn)
Hal Canarye650b852018-09-12 09:12:36 -0400247 content->writeText("/Pattern CS/Pattern cs");
248 SkPDFWriteResourceName(content, SkPDFResourceType::kPattern, objectIndex);
249 content->writeText(" SCN");
250 SkPDFWriteResourceName(content, SkPDFResourceType::kPattern, objectIndex);
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000251 content->writeText(" scn\n");
252}
halcanarybc4696b2015-05-06 10:56:04 -0700253
Hal Canary04ac4612018-10-10 13:09:43 -0400254// return "x/pow(10, places)", given 0<x<pow(10, places)
255// result points to places+2 chars.
256static size_t print_permil_as_decimal(int x, char* result, unsigned places) {
halcanaryeb92cb32016-07-15 13:41:27 -0700257 result[0] = '.';
Hal Canary04ac4612018-10-10 13:09:43 -0400258 for (int i = places; i > 0; --i) {
halcanaryeb92cb32016-07-15 13:41:27 -0700259 result[i] = '0' + x % 10;
260 x /= 10;
261 }
262 int j;
Hal Canary04ac4612018-10-10 13:09:43 -0400263 for (j = places; j > 1; --j) {
halcanaryeb92cb32016-07-15 13:41:27 -0700264 if (result[j] != '0') {
265 break;
266 }
267 }
268 result[j + 1] = '\0';
269 return j + 1;
270}
271
Hal Canary94fd66c2017-07-05 11:25:42 -0400272
Hal Canary04ac4612018-10-10 13:09:43 -0400273static constexpr int int_pow(int base, unsigned exp, int acc = 1) {
274 return exp < 1 ? acc
275 : int_pow(base * base,
276 exp / 2,
277 (exp % 2) ? acc * base : acc);
278}
279
280
281size_t SkPDFUtils::ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]) {
282 static constexpr int kFactor = int_pow(10, kFloatColorDecimalCount);
283 int x = sk_float_round2int(value * kFactor);
284 if (x >= kFactor || x <= 0) { // clamp to 0-1
285 result[0] = x > 0 ? '1' : '0';
286 result[1] = '\0';
287 return 1;
288 }
289 return print_permil_as_decimal(x, result, kFloatColorDecimalCount);
290}
291
292size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
293 if (value == 255 || value == 0) {
294 result[0] = value ? '1' : '0';
295 result[1] = '\0';
296 return 1;
297 }
298 // int x = 0.5 + (1000.0 / 255.0) * value;
299 int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value);
300 return print_permil_as_decimal(x, result, 3);
301}
302
Hal Canary94fd66c2017-07-05 11:25:42 -0400303bool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
304 SkMatrix inverse;
305 if (!matrix.invert(&inverse)) {
306 return false;
307 }
308 inverse.mapRect(bbox);
309 return true;
310}
311
312void SkPDFUtils::PopulateTilingPatternDict(SkPDFDict* pattern,
313 SkRect& bbox,
Hal Canary74801582018-12-18 16:30:41 -0500314 std::unique_ptr<SkPDFDict> resources,
Hal Canary94fd66c2017-07-05 11:25:42 -0400315 const SkMatrix& matrix) {
316 const int kTiling_PatternType = 1;
317 const int kColoredTilingPattern_PaintType = 1;
318 const int kConstantSpacing_TilingType = 1;
319
320 pattern->insertName("Type", "Pattern");
321 pattern->insertInt("PatternType", kTiling_PatternType);
322 pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
323 pattern->insertInt("TilingType", kConstantSpacing_TilingType);
324 pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
325 pattern->insertScalar("XStep", bbox.width());
326 pattern->insertScalar("YStep", bbox.height());
327 pattern->insertObject("Resources", std::move(resources));
328 if (!matrix.isIdentity()) {
329 pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
330 }
331}
Hal Canary4f29c202017-07-18 10:28:31 -0400332
333bool SkPDFUtils::ToBitmap(const SkImage* img, SkBitmap* dst) {
334 SkASSERT(img);
335 SkASSERT(dst);
336 SkBitmap bitmap;
Adlai Hollerbcfc5542020-08-27 12:44:07 -0400337 // TODO: support GPU images
338 if(as_IB(img)->getROPixels(nullptr, &bitmap)) {
Hal Canary4f29c202017-07-18 10:28:31 -0400339 SkASSERT(bitmap.dimensions() == img->dimensions());
340 SkASSERT(!bitmap.drawsNothing());
341 *dst = std::move(bitmap);
342 return true;
343 }
344 return false;
345}
Hal Canaryfde18752018-12-19 13:49:23 -0500346
347#ifdef SK_PDF_BASE85_BINARY
Hal Canaryd104cc42018-12-20 16:15:17 -0500348void SkPDFUtils::Base85Encode(std::unique_ptr<SkStreamAsset> stream, SkDynamicMemoryWStream* dst) {
Hal Canaryfde18752018-12-19 13:49:23 -0500349 SkASSERT(dst);
Hal Canaryd104cc42018-12-20 16:15:17 -0500350 SkASSERT(stream);
Hal Canaryfde18752018-12-19 13:49:23 -0500351 dst->writeText("\n");
Hal Canaryd104cc42018-12-20 16:15:17 -0500352 int column = 0;
353 while (true) {
Hal Canaryfde18752018-12-19 13:49:23 -0500354 uint8_t src[4] = {0, 0, 0, 0};
Hal Canaryd104cc42018-12-20 16:15:17 -0500355 size_t count = stream->read(src, 4);
356 SkASSERT(count < 5);
357 if (0 == count) {
358 dst->writeText("~>\n");
359 return;
360 }
361 uint32_t v = ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) |
362 ((uint32_t)src[2] << 8) | src[3];
363 if (v == 0 && count == 4) {
364 dst->writeText("z");
365 column += 1;
366 } else {
367 char buffer[5];
368 for (int n = 4; n > 0; --n) {
369 buffer[n] = (v % 85) + '!';
370 v /= 85;
371 }
372 buffer[0] = v + '!';
373 dst->write(buffer, count + 1);
374 column += count + 1;
375 }
376 if (column > 74) {
377 dst->writeText("\n");
378 column = 0;
379 }
Hal Canaryfde18752018-12-19 13:49:23 -0500380 }
Hal Canaryfde18752018-12-19 13:49:23 -0500381}
382#endif // SK_PDF_BASE85_BINARY
Hal Canaryf5edf362019-04-10 11:00:01 -0400383
384void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
385 SkScalar values[6];
386 if (!matrix.asAffine(values)) {
387 SkMatrix::SetAffineIdentity(values);
388 }
389 for (SkScalar v : values) {
390 SkPDFUtils::AppendScalar(v, content);
391 content->writeText(" ");
392 }
393 content->writeText("cm\n");
394}