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
9 #include "SkData.h"
10 #include "SkGeometry.h"
11 #include "SkPaint.h"
12 #include "SkPath.h"
13 #include "SkPDFResourceDict.h"
14 #include "SkPDFUtils.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkPDFTypes.h"
18
19 #include <cmath>
20
21 //static
RectToArray(const SkRect & rect)22 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
23 SkPDFArray* result = new SkPDFArray();
24 result->reserve(4);
25 result->appendScalar(rect.fLeft);
26 result->appendScalar(rect.fTop);
27 result->appendScalar(rect.fRight);
28 result->appendScalar(rect.fBottom);
29 return result;
30 }
31
32 // static
MatrixToArray(const SkMatrix & matrix)33 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
34 SkScalar values[6];
35 if (!matrix.asAffine(values)) {
36 SkMatrix::SetAffineIdentity(values);
37 }
38
39 SkPDFArray* result = new SkPDFArray;
40 result->reserve(6);
41 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
42 result->appendScalar(values[i]);
43 }
44 return result;
45 }
46
47 // static
AppendTransform(const SkMatrix & matrix,SkWStream * content)48 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
49 SkScalar values[6];
50 if (!matrix.asAffine(values)) {
51 SkMatrix::SetAffineIdentity(values);
52 }
53 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
54 SkPDFUtils::AppendScalar(values[i], content);
55 content->writeText(" ");
56 }
57 content->writeText("cm\n");
58 }
59
60 // static
MoveTo(SkScalar x,SkScalar y,SkWStream * content)61 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
62 SkPDFUtils::AppendScalar(x, content);
63 content->writeText(" ");
64 SkPDFUtils::AppendScalar(y, content);
65 content->writeText(" m\n");
66 }
67
68 // static
AppendLine(SkScalar x,SkScalar y,SkWStream * content)69 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
70 SkPDFUtils::AppendScalar(x, content);
71 content->writeText(" ");
72 SkPDFUtils::AppendScalar(y, content);
73 content->writeText(" l\n");
74 }
75
76 // static
AppendCubic(SkScalar ctl1X,SkScalar ctl1Y,SkScalar ctl2X,SkScalar ctl2Y,SkScalar dstX,SkScalar dstY,SkWStream * content)77 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
78 SkScalar ctl2X, SkScalar ctl2Y,
79 SkScalar dstX, SkScalar dstY, SkWStream* content) {
80 SkString cmd("y\n");
81 SkPDFUtils::AppendScalar(ctl1X, content);
82 content->writeText(" ");
83 SkPDFUtils::AppendScalar(ctl1Y, content);
84 content->writeText(" ");
85 if (ctl2X != dstX || ctl2Y != dstY) {
86 cmd.set("c\n");
87 SkPDFUtils::AppendScalar(ctl2X, content);
88 content->writeText(" ");
89 SkPDFUtils::AppendScalar(ctl2Y, content);
90 content->writeText(" ");
91 }
92 SkPDFUtils::AppendScalar(dstX, content);
93 content->writeText(" ");
94 SkPDFUtils::AppendScalar(dstY, content);
95 content->writeText(" ");
96 content->writeText(cmd.c_str());
97 }
98
append_quad(const SkPoint quad[],SkWStream * content)99 static void append_quad(const SkPoint quad[], SkWStream* content) {
100 SkPoint cubic[4];
101 SkConvertQuadToCubic(quad, cubic);
102 SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
103 cubic[3].fX, cubic[3].fY, content);
104 }
105
106 // static
AppendRectangle(const SkRect & rect,SkWStream * content)107 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
108 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
109 SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
110
111 SkPDFUtils::AppendScalar(rect.fLeft, content);
112 content->writeText(" ");
113 SkPDFUtils::AppendScalar(bottom, content);
114 content->writeText(" ");
115 SkPDFUtils::AppendScalar(rect.width(), content);
116 content->writeText(" ");
117 SkPDFUtils::AppendScalar(rect.height(), content);
118 content->writeText(" re\n");
119 }
120
121 // static
EmitPath(const SkPath & path,SkPaint::Style paintStyle,bool doConsumeDegerates,SkWStream * content)122 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
123 bool doConsumeDegerates, SkWStream* content) {
124 // 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).
128 enum SkipFillState {
129 kEmpty_SkipFillState,
130 kSingleLine_SkipFillState,
131 kNonSingleLine_SkipFillState,
132 };
133 SkipFillState fillState = kEmpty_SkipFillState;
134 //if (paintStyle != SkPaint::kFill_Style) {
135 // fillState = kNonSingleLine_SkipFillState;
136 //}
137 SkPoint lastMovePt = SkPoint::Make(0,0);
138 SkDynamicMemoryWStream currentSegment;
139 SkPoint args[4];
140 SkPath::Iter iter(path, false);
141 for (SkPath::Verb verb = iter.next(args, doConsumeDegerates);
142 verb != SkPath::kDone_Verb;
143 verb = iter.next(args, doConsumeDegerates)) {
144 // args gets all the points, even the implicit first point.
145 switch (verb) {
146 case SkPath::kMove_Verb:
147 MoveTo(args[0].fX, args[0].fY, ¤tSegment);
148 lastMovePt = args[0];
149 fillState = kEmpty_SkipFillState;
150 break;
151 case SkPath::kLine_Verb:
152 AppendLine(args[1].fX, args[1].fY, ¤tSegment);
153 if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
154 fillState = kSingleLine_SkipFillState;
155 break;
156 }
157 fillState = kNonSingleLine_SkipFillState;
158 break;
159 case SkPath::kQuad_Verb:
160 append_quad(args, ¤tSegment);
161 fillState = kNonSingleLine_SkipFillState;
162 break;
163 case SkPath::kConic_Verb: {
164 const SkScalar tol = SK_Scalar1 / 4;
165 SkAutoConicToQuads converter;
166 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tol);
167 for (int i = 0; i < converter.countQuads(); ++i) {
168 append_quad(&quads[i * 2], ¤tSegment);
169 }
170 fillState = kNonSingleLine_SkipFillState;
171 } break;
172 case SkPath::kCubic_Verb:
173 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
174 args[3].fX, args[3].fY, ¤tSegment);
175 fillState = kNonSingleLine_SkipFillState;
176 break;
177 case SkPath::kClose_Verb:
178
179 ClosePath(¤tSegment);
180
181 currentSegment.writeToStream(content);
182 currentSegment.reset();
183 break;
184 default:
185 SkASSERT(false);
186 break;
187 }
188 }
189 if (currentSegment.bytesWritten() > 0) {
190 currentSegment.writeToStream(content);
191 }
192 }
193
194 // static
ClosePath(SkWStream * content)195 void SkPDFUtils::ClosePath(SkWStream* content) {
196 content->writeText("h\n");
197 }
198
199 // static
PaintPath(SkPaint::Style style,SkPath::FillType fill,SkWStream * content)200 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
201 SkWStream* content) {
202 if (style == SkPaint::kFill_Style) {
203 content->writeText("f");
204 } else if (style == SkPaint::kStrokeAndFill_Style) {
205 content->writeText("B");
206 } else if (style == SkPaint::kStroke_Style) {
207 content->writeText("S");
208 }
209
210 if (style != SkPaint::kStroke_Style) {
211 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
212 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
213 if (fill == SkPath::kEvenOdd_FillType) {
214 content->writeText("*");
215 }
216 }
217 content->writeText("\n");
218 }
219
220 // static
StrokePath(SkWStream * content)221 void SkPDFUtils::StrokePath(SkWStream* content) {
222 SkPDFUtils::PaintPath(
223 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
224 }
225
226 // static
DrawFormXObject(int objectIndex,SkWStream * content)227 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
228 content->writeText("/");
229 content->writeText(SkPDFResourceDict::getResourceName(
230 SkPDFResourceDict::kXObject_ResourceType,
231 objectIndex).c_str());
232 content->writeText(" Do\n");
233 }
234
235 // static
ApplyGraphicState(int objectIndex,SkWStream * content)236 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
237 content->writeText("/");
238 content->writeText(SkPDFResourceDict::getResourceName(
239 SkPDFResourceDict::kExtGState_ResourceType,
240 objectIndex).c_str());
241 content->writeText(" gs\n");
242 }
243
244 // static
ApplyPattern(int objectIndex,SkWStream * content)245 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
246 // Select Pattern color space (CS, cs) and set pattern object as current
247 // color (SCN, scn)
248 SkString resourceName = SkPDFResourceDict::getResourceName(
249 SkPDFResourceDict::kPattern_ResourceType,
250 objectIndex);
251 content->writeText("/Pattern CS/Pattern cs/");
252 content->writeText(resourceName.c_str());
253 content->writeText(" SCN/");
254 content->writeText(resourceName.c_str());
255 content->writeText(" scn\n");
256 }
257
AppendScalar(SkScalar value,SkWStream * stream)258 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
259 char result[kMaximumFloatDecimalLength];
260 size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
261 SkASSERT(len < kMaximumFloatDecimalLength);
262 stream->write(result, len);
263 }
264
265 /** Write a string into result, includeing a terminating '\0' (for
266 unit testing). Return strlen(result) (for SkWStream::write) The
267 resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
268 sscanf(result, "%f", &x) will return the original value iff the
269 value is finite. This function accepts all possible input values.
270
271 Motivation: "PDF does not support [numbers] in exponential format
272 (such as 6.02e23)." Otherwise, this function would rely on a
273 sprintf-type function from the standard library. */
FloatToDecimal(float value,char result[kMaximumFloatDecimalLength])274 size_t SkPDFUtils::FloatToDecimal(float value,
275 char result[kMaximumFloatDecimalLength]) {
276 /* The longest result is -FLT_MIN.
277 We serialize it as "-.0000000000000000000000000000000000000117549435"
278 which has 48 characters plus a terminating '\0'. */
279
280 /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
281 most PDF rasterizers will use fixed-point scalars that lack the
282 dynamic range of floats. Even if this is the case, I want to
283 serialize these (uncommon) very small and very large scalar
284 values with enough precision to allow a floating-point
285 rasterizer to read them in with perfect accuracy.
286 Experimentally, rasterizers such as pdfium do seem to benefit
287 from this. Rasterizers that rely on fixed-point scalars should
288 gracefully ignore these values that they can not parse. */
289 char* output = &result[0];
290 const char* const end = &result[kMaximumFloatDecimalLength - 1];
291 // subtract one to leave space for '\0'.
292
293 /* This function is written to accept any possible input value,
294 including non-finite values such as INF and NAN. In that case,
295 we ignore value-correctness and and output a syntacticly-valid
296 number. */
297 if (value == SK_FloatInfinity) {
298 value = FLT_MAX; // nearest finite float.
299 }
300 if (value == SK_FloatNegativeInfinity) {
301 value = -FLT_MAX; // nearest finite float.
302 }
303 if (!std::isfinite(value) || value == 0.0f) {
304 // NAN is unsupported in PDF. Always output a valid number.
305 // Also catch zero here, as a special case.
306 *output++ = '0';
307 *output = '\0';
308 return output - result;
309 }
310 // Inspired by:
311 // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/
312
313 if (value < 0.0) {
314 *output++ = '-';
315 value = -value;
316 }
317 SkASSERT(value >= 0.0f);
318
319 // Must use double math to keep precision right.
320 double intPart;
321 double fracPart = std::modf(static_cast<double>(value), &intPart);
322 SkASSERT(intPart + fracPart == static_cast<double>(value));
323 size_t significantDigits = 0;
324 const size_t maxSignificantDigits = 9;
325 // Any fewer significant digits loses precision. The unit test
326 // checks round-trip correctness.
327 SkASSERT(intPart >= 0.0 && fracPart >= 0.0); // negative handled already.
328 SkASSERT(intPart > 0.0 || fracPart > 0.0); // zero already caught.
329 if (intPart > 0.0) {
330 // put the intPart digits onto a stack for later reversal.
331 char reversed[1 + FLT_MAX_10_EXP]; // 39 == 1 + FLT_MAX_10_EXP
332 // the largest integer part is FLT_MAX; it has 39 decimal digits.
333 size_t reversedIndex = 0;
334 do {
335 SkASSERT(reversedIndex < sizeof(reversed));
336 int digit = static_cast<int>(std::fmod(intPart, 10.0));
337 SkASSERT(digit >= 0 && digit <= 9);
338 reversed[reversedIndex++] = '0' + digit;
339 intPart = std::floor(intPart / 10.0);
340 } while (intPart > 0.0);
341 significantDigits = reversedIndex;
342 SkASSERT(reversedIndex <= sizeof(reversed));
343 SkASSERT(output + reversedIndex <= end);
344 while (reversedIndex-- > 0) { // pop from stack, append to result
345 *output++ = reversed[reversedIndex];
346 }
347 }
348 if (fracPart > 0 && significantDigits < maxSignificantDigits) {
349 *output++ = '.';
350 SkASSERT(output <= end);
351 do {
352 fracPart = std::modf(fracPart * 10.0, &intPart);
353 int digit = static_cast<int>(intPart);
354 SkASSERT(digit >= 0 && digit <= 9);
355 *output++ = '0' + digit;
356 SkASSERT(output <= end);
357 if (digit > 0 || significantDigits > 0) {
358 // start counting significantDigits after first non-zero digit.
359 ++significantDigits;
360 }
361 } while (fracPart > 0.0
362 && significantDigits < maxSignificantDigits
363 && output < end);
364 // When fracPart == 0, additional digits will be zero.
365 // When significantDigits == maxSignificantDigits, we can stop.
366 // when output == end, we have filed the string.
367 // Note: denormalized numbers will not have the same number of
368 // significantDigits, but do not need them to round-trip.
369 }
370 SkASSERT(output <= end);
371 *output = '\0';
372 return output - result;
373 }
374
FormatString(const char * cin,size_t len)375 SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
376 SkDEBUGCODE(static const size_t kMaxLen = 65535;)
377 SkASSERT(len <= kMaxLen);
378
379 // 7-bit clean is a heuristic to decide what string format to use;
380 // a 7-bit clean string should require little escaping.
381 bool sevenBitClean = true;
382 size_t characterCount = 2 + len;
383 for (size_t i = 0; i < len; i++) {
384 if (cin[i] > '~' || cin[i] < ' ') {
385 sevenBitClean = false;
386 break;
387 }
388 if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
389 ++characterCount;
390 }
391 }
392 SkString result;
393 if (sevenBitClean) {
394 result.resize(characterCount);
395 char* str = result.writable_str();
396 *str++ = '(';
397 for (size_t i = 0; i < len; i++) {
398 if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
399 *str++ = '\\';
400 }
401 *str++ = cin[i];
402 }
403 *str++ = ')';
404 } else {
405 result.resize(2 * len + 2);
406 char* str = result.writable_str();
407 *str++ = '<';
408 for (size_t i = 0; i < len; i++) {
409 uint8_t c = static_cast<uint8_t>(cin[i]);
410 static const char gHex[] = "0123456789ABCDEF";
411 *str++ = gHex[(c >> 4) & 0xF];
412 *str++ = gHex[(c ) & 0xF];
413 }
414 *str++ = '>';
415 }
416 return result;
417 }
418