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, ¤tSegment);
140 lastMovePt = args[0];
141 fillState = kEmpty_SkipFillState;
142 break;
143 case SkPath::kLine_Verb:
144 AppendLine(args[1].fX, args[1].fY, ¤tSegment);
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, ¤tSegment);
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, ¤tSegment);
164 fillState = kNonSingleLine_SkipFillState;
165 break;
166 case SkPath::kClose_Verb:
167 if (fillState != kSingleLine_SkipFillState) {
168 ClosePath(¤tSegment);
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