• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &currentSegment);
148                 lastMovePt = args[0];
149                 fillState = kEmpty_SkipFillState;
150                 break;
151             case SkPath::kLine_Verb:
152                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
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, &currentSegment);
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], &currentSegment);
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, &currentSegment);
175                 fillState = kNonSingleLine_SkipFillState;
176                 break;
177             case SkPath::kClose_Verb:
178 
179                     ClosePath(&currentSegment);
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