• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "SkData.h"
11 #include "SkGeometry.h"
12 #include "SkPaint.h"
13 #include "SkPath.h"
14 #include "SkPDFResourceDict.h"
15 #include "SkPDFUtils.h"
16 #include "SkStream.h"
17 #include "SkString.h"
18 #include "SkPDFTypes.h"
19 
20 //static
RectToArray(const SkRect & rect)21 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) {
22     SkPDFArray* result = new 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 
31 // static
MatrixToArray(const SkMatrix & matrix)32 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
33     SkScalar values[6];
34     if (!matrix.asAffine(values)) {
35         SkMatrix::SetAffineIdentity(values);
36     }
37 
38     SkPDFArray* result = new SkPDFArray;
39     result->reserve(6);
40     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
41         result->appendScalar(values[i]);
42     }
43     return result;
44 }
45 
46 // static
AppendTransform(const SkMatrix & matrix,SkWStream * content)47 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
48     SkScalar values[6];
49     if (!matrix.asAffine(values)) {
50         SkMatrix::SetAffineIdentity(values);
51     }
52     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
53         SkPDFScalar::Append(values[i], content);
54         content->writeText(" ");
55     }
56     content->writeText("cm\n");
57 }
58 
59 // static
MoveTo(SkScalar x,SkScalar y,SkWStream * content)60 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
61     SkPDFScalar::Append(x, content);
62     content->writeText(" ");
63     SkPDFScalar::Append(y, content);
64     content->writeText(" m\n");
65 }
66 
67 // static
AppendLine(SkScalar x,SkScalar y,SkWStream * content)68 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
69     SkPDFScalar::Append(x, content);
70     content->writeText(" ");
71     SkPDFScalar::Append(y, content);
72     content->writeText(" l\n");
73 }
74 
75 // static
AppendCubic(SkScalar ctl1X,SkScalar ctl1Y,SkScalar ctl2X,SkScalar ctl2Y,SkScalar dstX,SkScalar dstY,SkWStream * content)76 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
77                              SkScalar ctl2X, SkScalar ctl2Y,
78                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
79     SkString cmd("y\n");
80     SkPDFScalar::Append(ctl1X, content);
81     content->writeText(" ");
82     SkPDFScalar::Append(ctl1Y, content);
83     content->writeText(" ");
84     if (ctl2X != dstX || ctl2Y != dstY) {
85         cmd.set("c\n");
86         SkPDFScalar::Append(ctl2X, content);
87         content->writeText(" ");
88         SkPDFScalar::Append(ctl2Y, content);
89         content->writeText(" ");
90     }
91     SkPDFScalar::Append(dstX, content);
92     content->writeText(" ");
93     SkPDFScalar::Append(dstY, content);
94     content->writeText(" ");
95     content->writeText(cmd.c_str());
96 }
97 
98 // static
AppendRectangle(const SkRect & rect,SkWStream * content)99 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
100     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
101     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
102 
103     SkPDFScalar::Append(rect.fLeft, content);
104     content->writeText(" ");
105     SkPDFScalar::Append(bottom, content);
106     content->writeText(" ");
107     SkPDFScalar::Append(rect.width(), content);
108     content->writeText(" ");
109     SkPDFScalar::Append(rect.height(), content);
110     content->writeText(" re\n");
111 }
112 
113 // static
EmitPath(const SkPath & path,SkPaint::Style paintStyle,SkWStream * content)114 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
115                           SkWStream* content) {
116     // Filling a path with no area results in a drawing in PDF renderers but
117     // Chrome expects to be able to draw some such entities with no visible
118     // result, so we detect those cases and discard the drawing for them.
119     // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
120     enum SkipFillState {
121         kEmpty_SkipFillState         = 0,
122         kSingleLine_SkipFillState    = 1,
123         kNonSingleLine_SkipFillState = 2,
124     };
125     SkipFillState fillState = kEmpty_SkipFillState;
126     if (paintStyle != SkPaint::kFill_Style) {
127         fillState = kNonSingleLine_SkipFillState;
128     }
129     SkPoint lastMovePt = SkPoint::Make(0,0);
130     SkDynamicMemoryWStream currentSegment;
131     SkPoint args[4];
132     SkPath::Iter iter(path, false);
133     for (SkPath::Verb verb = iter.next(args);
134          verb != SkPath::kDone_Verb;
135          verb = iter.next(args)) {
136         // args gets all the points, even the implicit first point.
137         switch (verb) {
138             case SkPath::kMove_Verb:
139                 MoveTo(args[0].fX, args[0].fY, &currentSegment);
140                 lastMovePt = args[0];
141                 fillState = kEmpty_SkipFillState;
142                 break;
143             case SkPath::kLine_Verb:
144                 AppendLine(args[1].fX, args[1].fY, &currentSegment);
145                 if (fillState == kEmpty_SkipFillState) {
146                    if (args[0] != lastMovePt) {
147                        fillState = kSingleLine_SkipFillState;
148                    }
149                 } else if (fillState == kSingleLine_SkipFillState) {
150                     fillState = kNonSingleLine_SkipFillState;
151                 }
152                 break;
153             case SkPath::kQuad_Verb: {
154                 SkPoint cubic[4];
155                 SkConvertQuadToCubic(args, cubic);
156                 AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
157                             cubic[3].fX, cubic[3].fY, &currentSegment);
158                 fillState = kNonSingleLine_SkipFillState;
159                 break;
160             }
161             case SkPath::kCubic_Verb:
162                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
163                             args[3].fX, args[3].fY, &currentSegment);
164                 fillState = kNonSingleLine_SkipFillState;
165                 break;
166             case SkPath::kClose_Verb:
167                 if (fillState != kSingleLine_SkipFillState) {
168                     ClosePath(&currentSegment);
169                     SkData* data = currentSegment.copyToData();
170                     content->write(data->data(), data->size());
171                     data->unref();
172                 }
173                 currentSegment.reset();
174                 break;
175             default:
176                 SkASSERT(false);
177                 break;
178         }
179     }
180     if (currentSegment.bytesWritten() > 0) {
181         SkData* data = currentSegment.copyToData();
182         content->write(data->data(), data->size());
183         data->unref();
184     }
185 }
186 
187 // static
ClosePath(SkWStream * content)188 void SkPDFUtils::ClosePath(SkWStream* content) {
189     content->writeText("h\n");
190 }
191 
192 // static
PaintPath(SkPaint::Style style,SkPath::FillType fill,SkWStream * content)193 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
194                            SkWStream* content) {
195     if (style == SkPaint::kFill_Style) {
196         content->writeText("f");
197     } else if (style == SkPaint::kStrokeAndFill_Style) {
198         content->writeText("B");
199     } else if (style == SkPaint::kStroke_Style) {
200         content->writeText("S");
201     }
202 
203     if (style != SkPaint::kStroke_Style) {
204         NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
205         NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
206         if (fill == SkPath::kEvenOdd_FillType) {
207             content->writeText("*");
208         }
209     }
210     content->writeText("\n");
211 }
212 
213 // static
StrokePath(SkWStream * content)214 void SkPDFUtils::StrokePath(SkWStream* content) {
215     SkPDFUtils::PaintPath(
216         SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
217 }
218 
219 // static
DrawFormXObject(int objectIndex,SkWStream * content)220 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
221     content->writeText("/");
222     content->writeText(SkPDFResourceDict::getResourceName(
223             SkPDFResourceDict::kXObject_ResourceType,
224             objectIndex).c_str());
225     content->writeText(" Do\n");
226 }
227 
228 // static
ApplyGraphicState(int objectIndex,SkWStream * content)229 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
230     content->writeText("/");
231     content->writeText(SkPDFResourceDict::getResourceName(
232             SkPDFResourceDict::kExtGState_ResourceType,
233             objectIndex).c_str());
234     content->writeText(" gs\n");
235 }
236 
237 // static
ApplyPattern(int objectIndex,SkWStream * content)238 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
239     // Select Pattern color space (CS, cs) and set pattern object as current
240     // color (SCN, scn)
241     SkString resourceName = SkPDFResourceDict::getResourceName(
242             SkPDFResourceDict::kPattern_ResourceType,
243             objectIndex);
244     content->writeText("/Pattern CS/Pattern cs/");
245     content->writeText(resourceName.c_str());
246     content->writeText(" SCN/");
247     content->writeText(resourceName.c_str());
248     content->writeText(" scn\n");
249 }
250