Flutter Engine
The Flutter Engine
SkPDFUtils.cpp
Go to the documentation of this file.
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
11#include "include/core/SkPath.h"
13#include "include/core/SkRect.h"
14#include "include/core/SkSize.h"
22#include "src/core/SkGeometry.h"
23#include "src/core/SkPathPriv.h"
26#include "src/pdf/SkPDFTypes.h"
27#include "src/pdf/SkPDFUtils.h"
28
29#include <algorithm>
30#include <ctime>
31#include <utility>
32
33#if defined(SK_BUILD_FOR_WIN)
35#endif
36
38 // PDF32000.book section 11.3.5 "Blend Mode"
39 switch (mode) {
40 case SkBlendMode::kSrcOver: return "Normal";
41 case SkBlendMode::kXor: return "Normal"; // (unsupported mode)
42 case SkBlendMode::kPlus: return "Normal"; // (unsupported mode)
43 case SkBlendMode::kScreen: return "Screen";
44 case SkBlendMode::kOverlay: return "Overlay";
45 case SkBlendMode::kDarken: return "Darken";
46 case SkBlendMode::kLighten: return "Lighten";
47 case SkBlendMode::kColorDodge: return "ColorDodge";
48 case SkBlendMode::kColorBurn: return "ColorBurn";
49 case SkBlendMode::kHardLight: return "HardLight";
50 case SkBlendMode::kSoftLight: return "SoftLight";
51 case SkBlendMode::kDifference: return "Difference";
52 case SkBlendMode::kExclusion: return "Exclusion";
53 case SkBlendMode::kMultiply: return "Multiply";
54 case SkBlendMode::kHue: return "Hue";
55 case SkBlendMode::kSaturation: return "Saturation";
56 case SkBlendMode::kColor: return "Color";
57 case SkBlendMode::kLuminosity: return "Luminosity";
58 // Other blendmodes are handled in SkPDFDevice::setUpContentEntry.
59 default: return nullptr;
60 }
61}
62
63std::unique_ptr<SkPDFArray> SkPDFUtils::RectToArray(const SkRect& r) {
64 return SkPDFMakeArray(r.left(), r.top(), r.right(), r.bottom());
65}
66
67std::unique_ptr<SkPDFArray> SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
68 SkScalar a[6];
69 if (!matrix.asAffine(a)) {
71 }
72 return SkPDFMakeArray(a[0], a[1], a[2], a[3], a[4], a[5]);
73}
74
77 content->writeText(" ");
79 content->writeText(" m\n");
80}
81
84 content->writeText(" ");
86 content->writeText(" l\n");
87}
88
89static void append_cubic(SkScalar ctl1X, SkScalar ctl1Y,
90 SkScalar ctl2X, SkScalar ctl2Y,
91 SkScalar dstX, SkScalar dstY, SkWStream* content) {
92 SkString cmd("y\n");
94 content->writeText(" ");
96 content->writeText(" ");
97 if (ctl2X != dstX || ctl2Y != dstY) {
98 cmd.set("c\n");
100 content->writeText(" ");
102 content->writeText(" ");
103 }
105 content->writeText(" ");
107 content->writeText(" ");
108 content->writeText(cmd.c_str());
109}
110
111static void append_quad(const SkPoint quad[], SkWStream* content) {
112 SkPoint cubic[4];
114 append_cubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
115 cubic[3].fX, cubic[3].fY, content);
116}
117
119 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
120 SkScalar bottom = std::min(rect.fBottom, rect.fTop);
121
123 content->writeText(" ");
125 content->writeText(" ");
127 content->writeText(" ");
129 content->writeText(" re\n");
130}
131
133 bool doConsumeDegerates, SkWStream* content,
134 SkScalar tolerance) {
135 if (path.isEmpty() && SkPaint::kFill_Style == paintStyle) {
137 return;
138 }
139 // Filling a path with no area results in a drawing in PDF renderers but
140 // Chrome expects to be able to draw some such entities with no visible
141 // result, so we detect those cases and discard the drawing for them.
142 // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
143
144 SkRect rect;
145 bool isClosed; // Both closure and direction need to be checked.
146 SkPathDirection direction;
147 if (path.isRect(&rect, &isClosed, &direction) &&
148 isClosed &&
149 (SkPathDirection::kCW == direction ||
150 SkPathFillType::kEvenOdd == path.getFillType()))
151 {
153 return;
154 }
155
156 enum SkipFillState {
157 kEmpty_SkipFillState,
158 kSingleLine_SkipFillState,
159 kNonSingleLine_SkipFillState,
160 };
161 SkipFillState fillState = kEmpty_SkipFillState;
162 //if (paintStyle != SkPaint::kFill_Style) {
163 // fillState = kNonSingleLine_SkipFillState;
164 //}
165 SkPoint lastMovePt = SkPoint::Make(0,0);
166 SkDynamicMemoryWStream currentSegment;
167 SkPoint args[4];
168 SkPath::Iter iter(path, false);
169 for (SkPath::Verb verb = iter.next(args);
170 verb != SkPath::kDone_Verb;
171 verb = iter.next(args)) {
172 // args gets all the points, even the implicit first point.
173 switch (verb) {
175 MoveTo(args[0].fX, args[0].fY, &currentSegment);
176 lastMovePt = args[0];
177 fillState = kEmpty_SkipFillState;
178 break;
180 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 2)) {
181 AppendLine(args[1].fX, args[1].fY, &currentSegment);
182 if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
183 fillState = kSingleLine_SkipFillState;
184 break;
185 }
186 fillState = kNonSingleLine_SkipFillState;
187 }
188 break;
190 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
191 append_quad(args, &currentSegment);
192 fillState = kNonSingleLine_SkipFillState;
193 }
194 break;
196 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) {
198 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tolerance);
199 for (int i = 0; i < converter.countQuads(); ++i) {
200 append_quad(&quads[i * 2], &currentSegment);
201 }
202 fillState = kNonSingleLine_SkipFillState;
203 }
204 break;
206 if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 4)) {
207 append_cubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
208 args[3].fX, args[3].fY, &currentSegment);
209 fillState = kNonSingleLine_SkipFillState;
210 }
211 break;
213 ClosePath(&currentSegment);
214 currentSegment.writeToStream(content);
215 currentSegment.reset();
216 break;
217 default:
218 SkASSERT(false);
219 break;
220 }
221 }
222 if (currentSegment.bytesWritten() > 0) {
223 currentSegment.writeToStream(content);
224 }
225}
226
228 content->writeText("h\n");
229}
230
232 if (style == SkPaint::kFill_Style) {
233 content->writeText("f");
234 } else if (style == SkPaint::kStrokeAndFill_Style) {
235 content->writeText("B");
236 } else if (style == SkPaint::kStroke_Style) {
237 content->writeText("S");
238 }
239
240 if (style != SkPaint::kStroke_Style) {
243 if (fill == SkPathFillType::kEvenOdd) {
244 content->writeText("*");
245 }
246 }
247 content->writeText("\n");
248}
249
252}
253
256 content->writeText(" gs\n");
257}
258
260 // Select Pattern color space (CS, cs) and set pattern object as current
261 // color (SCN, scn)
262 content->writeText("/Pattern CS/Pattern cs");
264 content->writeText(" SCN");
266 content->writeText(" scn\n");
267}
268
269// return "x/pow(10, places)", given 0<x<pow(10, places)
270// result points to places+2 chars.
271static size_t print_permil_as_decimal(int x, char* result, unsigned places) {
272 result[0] = '.';
273 for (int i = places; i > 0; --i) {
274 result[i] = '0' + x % 10;
275 x /= 10;
276 }
277 int j;
278 for (j = places; j > 1; --j) {
279 if (result[j] != '0') {
280 break;
281 }
282 }
283 result[j + 1] = '\0';
284 return j + 1;
285}
286
287
288static constexpr int int_pow(int base, unsigned exp, int acc = 1) {
289 return exp < 1 ? acc
290 : int_pow(base * base,
291 exp / 2,
292 (exp % 2) ? acc * base : acc);
293}
294
295
297 static constexpr int kFactor = int_pow(10, kFloatColorDecimalCount);
298 int x = sk_float_round2int(value * kFactor);
299 if (x >= kFactor || x <= 0) { // clamp to 0-1
300 result[0] = x > 0 ? '1' : '0';
301 result[1] = '\0';
302 return 1;
303 }
305}
306
307size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
308 if (value == 255 || value == 0) {
309 result[0] = value ? '1' : '0';
310 result[1] = '\0';
311 return 1;
312 }
313 // int x = 0.5 + (1000.0 / 255.0) * value;
314 int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value);
315 return print_permil_as_decimal(x, result, 3);
316}
317
319 SkMatrix inverse;
320 if (!matrix.invert(&inverse)) {
321 return false;
322 }
323 inverse.mapRect(bbox);
324 return true;
325}
326
328 SkRect& bbox,
329 std::unique_ptr<SkPDFDict> resources,
330 const SkMatrix& matrix) {
331 const int kTiling_PatternType = 1;
332 const int kColoredTilingPattern_PaintType = 1;
333 const int kConstantSpacing_TilingType = 1;
334
335 pattern->insertName("Type", "Pattern");
336 pattern->insertInt("PatternType", kTiling_PatternType);
337 pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
338 pattern->insertInt("TilingType", kConstantSpacing_TilingType);
339 pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
340 pattern->insertScalar("XStep", bbox.width());
341 pattern->insertScalar("YStep", bbox.height());
342 pattern->insertObject("Resources", std::move(resources));
343 if (!matrix.isIdentity()) {
344 pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
345 }
346}
347
349 SkASSERT(img);
350 SkASSERT(dst);
352 // TODO: support GPU images
353 if(as_IB(img)->getROPixels(nullptr, &bitmap)) {
354 SkASSERT(bitmap.dimensions() == img->dimensions());
355 SkASSERT(!bitmap.drawsNothing());
356 *dst = std::move(bitmap);
357 return true;
358 }
359 return false;
360}
361
362#ifdef SK_PDF_BASE85_BINARY
363void SkPDFUtils::Base85Encode(std::unique_ptr<SkStreamAsset> stream, SkDynamicMemoryWStream* dst) {
364 SkASSERT(dst);
366 dst->writeText("\n");
367 int column = 0;
368 while (true) {
369 uint8_t src[4] = {0, 0, 0, 0};
370 size_t count = stream->read(src, 4);
371 SkASSERT(count < 5);
372 if (0 == count) {
373 dst->writeText("~>\n");
374 return;
375 }
376 uint32_t v = ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) |
377 ((uint32_t)src[2] << 8) | src[3];
378 if (v == 0 && count == 4) {
379 dst->writeText("z");
380 column += 1;
381 } else {
382 char buffer[5];
383 for (int n = 4; n > 0; --n) {
384 buffer[n] = (v % 85) + '!';
385 v /= 85;
386 }
387 buffer[0] = v + '!';
388 dst->write(buffer, count + 1);
389 column += count + 1;
390 }
391 if (column > 74) {
392 dst->writeText("\n");
393 column = 0;
394 }
395 }
396}
397#endif // SK_PDF_BASE85_BINARY
398
400 SkScalar values[6];
401 if (!matrix.asAffine(values)) {
403 }
404 for (SkScalar v : values) {
406 content->writeText(" ");
407 }
408 content->writeText("cm\n");
409}
410
411
412#if defined(SK_BUILD_FOR_WIN)
413
415 if (dt) {
416 SYSTEMTIME st;
417 GetSystemTime(&st);
418 dt->fTimeZoneMinutes = 0;
419 dt->fYear = st.wYear;
420 dt->fMonth = SkToU8(st.wMonth);
421 dt->fDayOfWeek = SkToU8(st.wDayOfWeek);
422 dt->fDay = SkToU8(st.wDay);
423 dt->fHour = SkToU8(st.wHour);
424 dt->fMinute = SkToU8(st.wMinute);
425 dt->fSecond = SkToU8(st.wSecond);
426 }
427}
428
429#else // SK_BUILD_FOR_WIN
430
432 if (dt) {
433 time_t m_time;
434 time(&m_time);
435 struct tm tstruct;
436 gmtime_r(&m_time, &tstruct);
437 dt->fTimeZoneMinutes = 0;
438 dt->fYear = tstruct.tm_year + 1900;
439 dt->fMonth = SkToU8(tstruct.tm_mon + 1);
440 dt->fDayOfWeek = SkToU8(tstruct.tm_wday);
441 dt->fDay = SkToU8(tstruct.tm_mday);
442 dt->fHour = SkToU8(tstruct.tm_hour);
443 dt->fMinute = SkToU8(tstruct.tm_min);
444 dt->fSecond = SkToU8(tstruct.tm_sec);
445 }
446}
447#endif // SK_BUILD_FOR_WIN
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
#define SK_Fixed1
Definition: SkFixed.h:26
#define SkFixedRoundToInt(x)
Definition: SkFixed.h:76
#define sk_float_round2int(x)
void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4])
Definition: SkGeometry.cpp:378
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
void SkPDFWriteResourceName(SkWStream *dst, SkPDFResourceType type, int key)
static std::unique_ptr< SkPDFArray > SkPDFMakeArray(Args... args)
Definition: SkPDFTypes.h:125
static size_t print_permil_as_decimal(int x, char *result, unsigned places)
Definition: SkPDFUtils.cpp:271
static void append_quad(const SkPoint quad[], SkWStream *content)
Definition: SkPDFUtils.cpp:111
static void append_cubic(SkScalar ctl1X, SkScalar ctl1Y, SkScalar ctl2X, SkScalar ctl2Y, SkScalar dstX, SkScalar dstY, SkWStream *content)
Definition: SkPDFUtils.cpp:89
static constexpr int int_pow(int base, unsigned exp, int acc=1)
Definition: SkPDFUtils.cpp:288
#define NOT_IMPLEMENTED(condition, assertion)
Definition: SkPDFUtils.h:51
SkPathDirection
Definition: SkPathTypes.h:34
SkPathFillType
Definition: SkPathTypes.h:11
constexpr uint8_t SkToU8(S x)
Definition: SkTo.h:22
size_t bytesWritten() const override
Definition: SkStream.cpp:526
bool writeToStream(SkWStream *dst) const
Definition: SkStream.cpp:642
SkISize dimensions() const
Definition: SkImage.h:297
static void SetAffineIdentity(SkScalar affine[6])
Definition: SkMatrix.cpp:746
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
void insertName(const char key[], const char nameValue[])
Definition: SkPDFTypes.cpp:512
void insertObject(const char key[], std::unique_ptr< SkPDFObject > &&)
Definition: SkPDFTypes.cpp:484
void insertInt(const char key[], int32_t value)
Definition: SkPDFTypes.cpp:496
void insertScalar(const char key[], SkScalar value)
Definition: SkPDFTypes.cpp:508
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
@ kStrokeAndFill_Style
sets to stroke and fill geometry
Definition: SkPaint.h:195
static bool AllPointsEq(const SkPoint pts[], int count)
Definition: SkPathPriv.h:339
Verb next(SkPoint pts[4])
Definition: SkPath.cpp:1901
SkScalar conicWeight() const
Definition: SkPath.h:1535
Definition: SkPath.h:59
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
float SkScalar
Definition: extension.cpp:12
struct MyStruct a[10]
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
GAsyncResult * result
static float min(float r, float g, float b)
Definition: hsl.cpp:48
union flutter::testing::@2836::KeyboardChange::@76 content
double y
double x
void GetDateTime(SkPDF::DateTime *)
Definition: SkPDFUtils.cpp:431
void ApplyGraphicState(int objectIndex, SkWStream *content)
Definition: SkPDFUtils.cpp:254
void PopulateTilingPatternDict(SkPDFDict *pattern, SkRect &bbox, std::unique_ptr< SkPDFDict > resources, const SkMatrix &matrix)
Definition: SkPDFUtils.cpp:327
size_t ColorToDecimalF(float value, char result[kFloatColorDecimalCount+2])
Definition: SkPDFUtils.cpp:296
size_t ColorToDecimal(uint8_t value, char result[5])
Definition: SkPDFUtils.cpp:307
const char * BlendModeName(SkBlendMode)
Definition: SkPDFUtils.cpp:37
std::unique_ptr< SkPDFArray > MatrixToArray(const SkMatrix &matrix)
Definition: SkPDFUtils.cpp:67
void EmitPath(const SkPath &path, SkPaint::Style paintStyle, bool doConsumeDegerates, SkWStream *content, SkScalar tolerance=0.25f)
Definition: SkPDFUtils.cpp:132
void AppendRectangle(const SkRect &rect, SkWStream *content)
Definition: SkPDFUtils.cpp:118
void PaintPath(SkPaint::Style style, SkPathFillType fill, SkWStream *content)
Definition: SkPDFUtils.cpp:231
void AppendScalar(SkScalar value, SkWStream *stream)
Definition: SkPDFUtils.h:98
void MoveTo(SkScalar x, SkScalar y, SkWStream *content)
Definition: SkPDFUtils.cpp:75
void AppendLine(SkScalar x, SkScalar y, SkWStream *content)
Definition: SkPDFUtils.cpp:82
void AppendTransform(const SkMatrix &, SkWStream *)
Definition: SkPDFUtils.cpp:399
void ApplyPattern(int objectIndex, SkWStream *content)
Definition: SkPDFUtils.cpp:259
bool ToBitmap(const SkImage *img, SkBitmap *dst)
Definition: SkPDFUtils.cpp:348
bool InverseTransformBBox(const SkMatrix &matrix, SkRect *bbox)
Definition: SkPDFUtils.cpp:318
void ClosePath(SkWStream *content)
Definition: SkPDFUtils.cpp:227
static constexpr unsigned kFloatColorDecimalCount
Definition: SkPDFUtils.h:85
void StrokePath(SkWStream *content)
Definition: SkPDFUtils.cpp:250
std::unique_ptr< SkPDFArray > RectToArray(const SkRect &rect)
Definition: SkPDFUtils.cpp:63
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
Definition: bitmap.py:1
string converter
Definition: cacheimages.py:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
dst
Definition: cp.py:12
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
uint8_t fMinute
0..59
Definition: SkPDFDocument.h:77
uint8_t fMonth
1..12
Definition: SkPDFDocument.h:73
uint8_t fDay
1..31
Definition: SkPDFDocument.h:75
uint16_t fYear
e.g. 2005
Definition: SkPDFDocument.h:72
uint8_t fSecond
0..59
Definition: SkPDFDocument.h:78
int16_t fTimeZoneMinutes
Definition: SkPDFDocument.h:70
uint8_t fHour
0..23
Definition: SkPDFDocument.h:76
uint8_t fDayOfWeek
0..6, 0==Sunday
Definition: SkPDFDocument.h:74
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
constexpr float height() const
Definition: SkRect.h:769
constexpr float right() const
Definition: SkRect.h:748
constexpr float width() const
Definition: SkRect.h:762
constexpr float bottom() const
Definition: SkRect.h:755