• 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 #include "SkPDFUtils.h"
9 
10 #include "SkData.h"
11 #include "SkFixed.h"
12 #include "SkGeometry.h"
13 #include "SkImage_Base.h"
14 #include "SkPDFResourceDict.h"
15 #include "SkPDFTypes.h"
16 #include "SkStream.h"
17 #include "SkString.h"
18 
19 #include <cmath>
20 
RectToArray(const SkRect & rect)21 sk_sp<SkPDFArray> SkPDFUtils::RectToArray(const SkRect& rect) {
22     auto result = sk_make_sp<SkPDFArray>();
23     result->reserve(4);
24     result->appendScalar(rect.fLeft);
25     result->appendScalar(rect.fTop);
26     result->appendScalar(rect.fRight);
27     result->appendScalar(rect.fBottom);
28     return result;
29 }
30 
MatrixToArray(const SkMatrix & matrix)31 sk_sp<SkPDFArray> SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
32     SkScalar values[6];
33     if (!matrix.asAffine(values)) {
34         SkMatrix::SetAffineIdentity(values);
35     }
36 
37     auto result = sk_make_sp<SkPDFArray>();
38     result->reserve(6);
39     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
40         result->appendScalar(values[i]);
41     }
42     return result;
43 }
44 
45 // static
AppendTransform(const SkMatrix & matrix,SkWStream * content)46 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
47     SkScalar values[6];
48     if (!matrix.asAffine(values)) {
49         SkMatrix::SetAffineIdentity(values);
50     }
51     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
52         SkPDFUtils::AppendScalar(values[i], content);
53         content->writeText(" ");
54     }
55     content->writeText("cm\n");
56 }
57 
58 // static
MoveTo(SkScalar x,SkScalar y,SkWStream * content)59 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
60     SkPDFUtils::AppendScalar(x, content);
61     content->writeText(" ");
62     SkPDFUtils::AppendScalar(y, content);
63     content->writeText(" m\n");
64 }
65 
66 // static
AppendLine(SkScalar x,SkScalar y,SkWStream * content)67 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
68     SkPDFUtils::AppendScalar(x, content);
69     content->writeText(" ");
70     SkPDFUtils::AppendScalar(y, content);
71     content->writeText(" l\n");
72 }
73 
74 // static
AppendCubic(SkScalar ctl1X,SkScalar ctl1Y,SkScalar ctl2X,SkScalar ctl2Y,SkScalar dstX,SkScalar dstY,SkWStream * content)75 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
76                              SkScalar ctl2X, SkScalar ctl2Y,
77                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
78     SkString cmd("y\n");
79     SkPDFUtils::AppendScalar(ctl1X, content);
80     content->writeText(" ");
81     SkPDFUtils::AppendScalar(ctl1Y, content);
82     content->writeText(" ");
83     if (ctl2X != dstX || ctl2Y != dstY) {
84         cmd.set("c\n");
85         SkPDFUtils::AppendScalar(ctl2X, content);
86         content->writeText(" ");
87         SkPDFUtils::AppendScalar(ctl2Y, content);
88         content->writeText(" ");
89     }
90     SkPDFUtils::AppendScalar(dstX, content);
91     content->writeText(" ");
92     SkPDFUtils::AppendScalar(dstY, content);
93     content->writeText(" ");
94     content->writeText(cmd.c_str());
95 }
96 
append_quad(const SkPoint quad[],SkWStream * content)97 static void append_quad(const SkPoint quad[], SkWStream* content) {
98     SkPoint cubic[4];
99     SkConvertQuadToCubic(quad, cubic);
100     SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
101                             cubic[3].fX, cubic[3].fY, content);
102 }
103 
104 // static
AppendRectangle(const SkRect & rect,SkWStream * content)105 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
106     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
107     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
108 
109     SkPDFUtils::AppendScalar(rect.fLeft, content);
110     content->writeText(" ");
111     SkPDFUtils::AppendScalar(bottom, content);
112     content->writeText(" ");
113     SkPDFUtils::AppendScalar(rect.width(), content);
114     content->writeText(" ");
115     SkPDFUtils::AppendScalar(rect.height(), content);
116     content->writeText(" re\n");
117 }
118 
119 // static
EmitPath(const SkPath & path,SkPaint::Style paintStyle,bool doConsumeDegerates,SkWStream * content,SkScalar tolerance)120 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
121                           bool doConsumeDegerates, SkWStream* content,
122                           SkScalar tolerance) {
123     // Filling a path with no area results in a drawing in PDF renderers but
124     // Chrome expects to be able to draw some such entities with no visible
125     // result, so we detect those cases and discard the drawing for them.
126     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
127 
128     SkRect rect;
129     bool isClosed; // Both closure and direction need to be checked.
130     SkPath::Direction direction;
131     if (path.isRect(&rect, &isClosed, &direction) &&
132         isClosed &&
133         (SkPath::kCW_Direction == direction ||
134          SkPath::kEvenOdd_FillType == path.getFillType()))
135     {
136         SkPDFUtils::AppendRectangle(rect, content);
137         return;
138     }
139 
140     enum SkipFillState {
141         kEmpty_SkipFillState,
142         kSingleLine_SkipFillState,
143         kNonSingleLine_SkipFillState,
144     };
145     SkipFillState fillState = kEmpty_SkipFillState;
146     //if (paintStyle != SkPaint::kFill_Style) {
147     //    fillState = kNonSingleLine_SkipFillState;
148     //}
149     SkPoint lastMovePt = SkPoint::Make(0,0);
150     SkDynamicMemoryWStream currentSegment;
151     SkPoint args[4];
152     SkPath::Iter iter(path, false);
153     for (SkPath::Verb verb = iter.next(args, doConsumeDegerates);
154          verb != SkPath::kDone_Verb;
155          verb = iter.next(args, doConsumeDegerates)) {
156         // args gets all the points, even the implicit first point.
157         switch (verb) {
158             case SkPath::kMove_Verb:
159                 MoveTo(args[0].fX, args[0].fY, &currentSegment);
160                 lastMovePt = args[0];
161                 fillState = kEmpty_SkipFillState;
162                 break;
163             case SkPath::kLine_Verb:
164                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
165                 if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
166                     fillState = kSingleLine_SkipFillState;
167                     break;
168                 }
169                 fillState = kNonSingleLine_SkipFillState;
170                 break;
171             case SkPath::kQuad_Verb:
172                 append_quad(args, &currentSegment);
173                 fillState = kNonSingleLine_SkipFillState;
174                 break;
175             case SkPath::kConic_Verb: {
176                 SkAutoConicToQuads converter;
177                 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tolerance);
178                 for (int i = 0; i < converter.countQuads(); ++i) {
179                     append_quad(&quads[i * 2], &currentSegment);
180                 }
181                 fillState = kNonSingleLine_SkipFillState;
182             } break;
183             case SkPath::kCubic_Verb:
184                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
185                             args[3].fX, args[3].fY, &currentSegment);
186                 fillState = kNonSingleLine_SkipFillState;
187                 break;
188             case SkPath::kClose_Verb:
189                 ClosePath(&currentSegment);
190                 currentSegment.writeToStream(content);
191                 currentSegment.reset();
192                 break;
193             default:
194                 SkASSERT(false);
195                 break;
196         }
197     }
198     if (currentSegment.bytesWritten() > 0) {
199         currentSegment.writeToStream(content);
200     }
201 }
202 
203 // static
ClosePath(SkWStream * content)204 void SkPDFUtils::ClosePath(SkWStream* content) {
205     content->writeText("h\n");
206 }
207 
208 // static
PaintPath(SkPaint::Style style,SkPath::FillType fill,SkWStream * content)209 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
210                            SkWStream* content) {
211     if (style == SkPaint::kFill_Style) {
212         content->writeText("f");
213     } else if (style == SkPaint::kStrokeAndFill_Style) {
214         content->writeText("B");
215     } else if (style == SkPaint::kStroke_Style) {
216         content->writeText("S");
217     }
218 
219     if (style != SkPaint::kStroke_Style) {
220         NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
221         NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
222         if (fill == SkPath::kEvenOdd_FillType) {
223             content->writeText("*");
224         }
225     }
226     content->writeText("\n");
227 }
228 
229 // static
StrokePath(SkWStream * content)230 void SkPDFUtils::StrokePath(SkWStream* content) {
231     SkPDFUtils::PaintPath(
232         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
233 }
234 
235 // static
DrawFormXObject(int objectIndex,SkWStream * content)236 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
237     content->writeText("/");
238     content->writeText(SkPDFResourceDict::getResourceName(
239             SkPDFResourceDict::kXObject_ResourceType,
240             objectIndex).c_str());
241     content->writeText(" Do\n");
242 }
243 
244 // static
ApplyGraphicState(int objectIndex,SkWStream * content)245 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
246     content->writeText("/");
247     content->writeText(SkPDFResourceDict::getResourceName(
248             SkPDFResourceDict::kExtGState_ResourceType,
249             objectIndex).c_str());
250     content->writeText(" gs\n");
251 }
252 
253 // static
ApplyPattern(int objectIndex,SkWStream * content)254 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
255     // Select Pattern color space (CS, cs) and set pattern object as current
256     // color (SCN, scn)
257     SkString resourceName = SkPDFResourceDict::getResourceName(
258             SkPDFResourceDict::kPattern_ResourceType,
259             objectIndex);
260     content->writeText("/Pattern CS/Pattern cs/");
261     content->writeText(resourceName.c_str());
262     content->writeText(" SCN/");
263     content->writeText(resourceName.c_str());
264     content->writeText(" scn\n");
265 }
266 
ColorToDecimal(uint8_t value,char result[5])267 size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
268     if (value == 255 || value == 0) {
269         result[0] = value ? '1' : '0';
270         result[1] = '\0';
271         return 1;
272     }
273     // int x = 0.5 + (1000.0 / 255.0) * value;
274     int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value);
275     result[0] = '.';
276     for (int i = 3; i > 0; --i) {
277         result[i] = '0' + x % 10;
278         x /= 10;
279     }
280     int j;
281     for (j = 3; j > 1; --j) {
282         if (result[j] != '0') {
283             break;
284         }
285     }
286     result[j + 1] = '\0';
287     return j + 1;
288 }
289 
AppendScalar(SkScalar value,SkWStream * stream)290 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
291     char result[kMaximumFloatDecimalLength];
292     size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
293     SkASSERT(len < kMaximumFloatDecimalLength);
294     stream->write(result, len);
295 }
296 
297 // Return pow(10.0, e), optimized for common cases.
pow10(int e)298 inline double pow10(int e) {
299     switch (e) {
300         case 0:  return 1.0;  // common cases
301         case 1:  return 10.0;
302         case 2:  return 100.0;
303         case 3:  return 1e+03;
304         case 4:  return 1e+04;
305         case 5:  return 1e+05;
306         case 6:  return 1e+06;
307         case 7:  return 1e+07;
308         case 8:  return 1e+08;
309         case 9:  return 1e+09;
310         case 10: return 1e+10;
311         case 11: return 1e+11;
312         case 12: return 1e+12;
313         case 13: return 1e+13;
314         case 14: return 1e+14;
315         case 15: return 1e+15;
316         default:
317             if (e > 15) {
318                 double value = 1e+15;
319                 while (e-- > 15) { value *= 10.0; }
320                 return value;
321             } else {
322                 SkASSERT(e < 0);
323                 double value = 1.0;
324                 while (e++ < 0) { value /= 10.0; }
325                 return value;
326             }
327     }
328 }
329 
330 /** Write a string into result, includeing a terminating '\0' (for
331     unit testing).  Return strlen(result) (for SkWStream::write) The
332     resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
333     sscanf(result, "%f", &x) will return the original value iff the
334     value is finite. This function accepts all possible input values.
335 
336     Motivation: "PDF does not support [numbers] in exponential format
337     (such as 6.02e23)."  Otherwise, this function would rely on a
338     sprintf-type function from the standard library. */
FloatToDecimal(float value,char result[kMaximumFloatDecimalLength])339 size_t SkPDFUtils::FloatToDecimal(float value,
340                                   char result[kMaximumFloatDecimalLength]) {
341     /* The longest result is -FLT_MIN.
342        We serialize it as "-.0000000000000000000000000000000000000117549435"
343        which has 48 characters plus a terminating '\0'. */
344 
345     /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
346        most PDF rasterizers will use fixed-point scalars that lack the
347        dynamic range of floats.  Even if this is the case, I want to
348        serialize these (uncommon) very small and very large scalar
349        values with enough precision to allow a floating-point
350        rasterizer to read them in with perfect accuracy.
351        Experimentally, rasterizers such as pdfium do seem to benefit
352        from this.  Rasterizers that rely on fixed-point scalars should
353        gracefully ignore these values that they can not parse. */
354     char* output = &result[0];
355     const char* const end = &result[kMaximumFloatDecimalLength - 1];
356     // subtract one to leave space for '\0'.
357 
358     /* This function is written to accept any possible input value,
359        including non-finite values such as INF and NAN.  In that case,
360        we ignore value-correctness and and output a syntacticly-valid
361        number. */
362     if (value == SK_FloatInfinity) {
363         value = FLT_MAX;  // nearest finite float.
364     }
365     if (value == SK_FloatNegativeInfinity) {
366         value = -FLT_MAX;  // nearest finite float.
367     }
368     if (!std::isfinite(value) || value == 0.0f) {
369         // NAN is unsupported in PDF.  Always output a valid number.
370         // Also catch zero here, as a special case.
371         *output++ = '0';
372         *output = '\0';
373         return output - result;
374     }
375     if (value < 0.0) {
376         *output++ = '-';
377         value = -value;
378     }
379     SkASSERT(value >= 0.0f);
380 
381     int binaryExponent;
382     (void)std::frexp(value, &binaryExponent);
383     static const double kLog2 = 0.3010299956639812;  // log10(2.0);
384     int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
385     int decimalShift = decimalExponent - 8;
386     double power = pow10(-decimalShift);
387     int32_t d = static_cast<int32_t>(value * power + 0.5);
388     // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
389     SkASSERT(d <= 999999999);
390     if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
391        // need one fewer decimal digits for 24-bit precision.
392        decimalShift = decimalExponent - 7;
393        // SkASSERT(power * 0.1 = pow10(-decimalShift));
394        // recalculate to get rounding right.
395        d = static_cast<int32_t>(value * (power * 0.1) + 0.5);
396        SkASSERT(d <= 99999999);
397     }
398     while (d % 10 == 0) {
399         d /= 10;
400         ++decimalShift;
401     }
402     SkASSERT(d > 0);
403     // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
404     uint8_t buffer[9]; // decimal value buffer.
405     int bufferIndex = 0;
406     do {
407         buffer[bufferIndex++] = d % 10;
408         d /= 10;
409     } while (d != 0);
410     SkASSERT(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
411     if (decimalShift >= 0) {
412         do {
413             --bufferIndex;
414             *output++ = '0' + buffer[bufferIndex];
415         } while (bufferIndex);
416         for (int i = 0; i < decimalShift; ++i) {
417             *output++ = '0';
418         }
419     } else {
420         int placesBeforeDecimal = bufferIndex + decimalShift;
421         if (placesBeforeDecimal > 0) {
422             while (placesBeforeDecimal-- > 0) {
423                 --bufferIndex;
424                 *output++ = '0' + buffer[bufferIndex];
425             }
426             *output++ = '.';
427         } else {
428             *output++ = '.';
429             int placesAfterDecimal = -placesBeforeDecimal;
430             while (placesAfterDecimal-- > 0) {
431                 *output++ = '0';
432             }
433         }
434         while (bufferIndex > 0) {
435             --bufferIndex;
436             *output++ = '0' + buffer[bufferIndex];
437             if (output == end) {
438                 break;  // denormalized: don't need extra precision.
439                 // Note: denormalized numbers will not have the same number of
440                 // significantDigits, but do not need them to round-trip.
441             }
442         }
443     }
444     SkASSERT(output <= end);
445     *output = '\0';
446     return output - result;
447 }
448 
WriteString(SkWStream * wStream,const char * cin,size_t len)449 void SkPDFUtils::WriteString(SkWStream* wStream, const char* cin, size_t len) {
450     SkDEBUGCODE(static const size_t kMaxLen = 65535;)
451     SkASSERT(len <= kMaxLen);
452 
453     size_t extraCharacterCount = 0;
454     for (size_t i = 0; i < len; i++) {
455         if (cin[i] > '~' || cin[i] < ' ') {
456             extraCharacterCount += 3;
457         }
458         if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
459             ++extraCharacterCount;
460         }
461     }
462     if (extraCharacterCount <= len) {
463         wStream->writeText("(");
464         for (size_t i = 0; i < len; i++) {
465             if (cin[i] > '~' || cin[i] < ' ') {
466                 uint8_t c = static_cast<uint8_t>(cin[i]);
467                 uint8_t octal[4];
468                 octal[0] = '\\';
469                 octal[1] = '0' + ( c >> 6        );
470                 octal[2] = '0' + ((c >> 3) & 0x07);
471                 octal[3] = '0' + ( c       & 0x07);
472                 wStream->write(octal, 4);
473             } else {
474                 if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
475                     wStream->writeText("\\");
476                 }
477                 wStream->write(&cin[i], 1);
478             }
479         }
480         wStream->writeText(")");
481     } else {
482         wStream->writeText("<");
483         for (size_t i = 0; i < len; i++) {
484             uint8_t c = static_cast<uint8_t>(cin[i]);
485             char hexValue[2] = { SkHexadecimalDigits::gUpper[c >> 4],
486                                  SkHexadecimalDigits::gUpper[c & 0xF] };
487             wStream->write(hexValue, 2);
488         }
489         wStream->writeText(">");
490     }
491 }
492 
InverseTransformBBox(const SkMatrix & matrix,SkRect * bbox)493 bool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
494     SkMatrix inverse;
495     if (!matrix.invert(&inverse)) {
496         return false;
497     }
498     inverse.mapRect(bbox);
499     return true;
500 }
501 
PopulateTilingPatternDict(SkPDFDict * pattern,SkRect & bbox,sk_sp<SkPDFDict> resources,const SkMatrix & matrix)502 void SkPDFUtils::PopulateTilingPatternDict(SkPDFDict* pattern,
503                                            SkRect& bbox,
504                                            sk_sp<SkPDFDict> resources,
505                                            const SkMatrix& matrix) {
506     const int kTiling_PatternType = 1;
507     const int kColoredTilingPattern_PaintType = 1;
508     const int kConstantSpacing_TilingType = 1;
509 
510     pattern->insertName("Type", "Pattern");
511     pattern->insertInt("PatternType", kTiling_PatternType);
512     pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
513     pattern->insertInt("TilingType", kConstantSpacing_TilingType);
514     pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
515     pattern->insertScalar("XStep", bbox.width());
516     pattern->insertScalar("YStep", bbox.height());
517     pattern->insertObject("Resources", std::move(resources));
518     if (!matrix.isIdentity()) {
519         pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
520     }
521 }
522 
ToBitmap(const SkImage * img,SkBitmap * dst)523 bool SkPDFUtils::ToBitmap(const SkImage* img, SkBitmap* dst) {
524     SkASSERT(img);
525     SkASSERT(dst);
526     SkBitmap bitmap;
527     if(as_IB(img)->getROPixels(&bitmap, nullptr)) {
528         SkASSERT(bitmap.dimensions() == img->dimensions());
529         SkASSERT(!bitmap.drawsNothing());
530         *dst = std::move(bitmap);
531         return true;
532     }
533     return false;
534 }
535