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
22SkPDFArray* 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
33SkPDFArray* 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
48void 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
61void 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
69void 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
77void 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
99static 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
107void 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
122void 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
195void SkPDFUtils::ClosePath(SkWStream* content) {
196    content->writeText("h\n");
197}
198
199// static
200void 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
221void SkPDFUtils::StrokePath(SkWStream* content) {
222    SkPDFUtils::PaintPath(
223        SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
224}
225
226// static
227void 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
236void 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
245void 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
258void 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. */
274size_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
375SkString 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