1 /*
2 * Copyright (C) 2011 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "SkPaint.h"
18 #include "SkPath.h"
19 #include "SkPDFUtils.h"
20 #include "SkStream.h"
21 #include "SkString.h"
22 #include "SkPDFTypes.h"
23
24 // static
MatrixToArray(const SkMatrix & matrix)25 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
26 SkScalar values[6];
27 SkAssertResult(matrix.pdfTransform(values));
28
29 SkPDFArray* result = new SkPDFArray;
30 result->reserve(6);
31 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
32 result->append(new SkPDFScalar(values[i]))->unref();
33 }
34 return result;
35 }
36
37 // static
AppendTransform(const SkMatrix & matrix,SkWStream * content)38 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
39 SkScalar values[6];
40 SkAssertResult(matrix.pdfTransform(values));
41 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
42 SkPDFScalar::Append(values[i], content);
43 content->writeText(" ");
44 }
45 content->writeText("cm\n");
46 }
47
48 // static
MoveTo(SkScalar x,SkScalar y,SkWStream * content)49 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
50 SkPDFScalar::Append(x, content);
51 content->writeText(" ");
52 SkPDFScalar::Append(y, content);
53 content->writeText(" m\n");
54 }
55
56 // static
AppendLine(SkScalar x,SkScalar y,SkWStream * content)57 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
58 SkPDFScalar::Append(x, content);
59 content->writeText(" ");
60 SkPDFScalar::Append(y, content);
61 content->writeText(" l\n");
62 }
63
64 // static
AppendCubic(SkScalar ctl1X,SkScalar ctl1Y,SkScalar ctl2X,SkScalar ctl2Y,SkScalar dstX,SkScalar dstY,SkWStream * content)65 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
66 SkScalar ctl2X, SkScalar ctl2Y,
67 SkScalar dstX, SkScalar dstY, SkWStream* content) {
68 SkString cmd("y\n");
69 SkPDFScalar::Append(ctl1X, content);
70 content->writeText(" ");
71 SkPDFScalar::Append(ctl1Y, content);
72 content->writeText(" ");
73 if (ctl2X != dstX || ctl2Y != dstY) {
74 cmd.set("c\n");
75 SkPDFScalar::Append(ctl2X, content);
76 content->writeText(" ");
77 SkPDFScalar::Append(ctl2Y, content);
78 content->writeText(" ");
79 }
80 SkPDFScalar::Append(dstX, content);
81 content->writeText(" ");
82 SkPDFScalar::Append(dstY, content);
83 content->writeText(" ");
84 content->writeText(cmd.c_str());
85 }
86
87 // static
AppendRectangle(const SkRect & rect,SkWStream * content)88 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
89 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
90 SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
91
92 SkPDFScalar::Append(rect.fLeft, content);
93 content->writeText(" ");
94 SkPDFScalar::Append(bottom, content);
95 content->writeText(" ");
96 SkPDFScalar::Append(rect.width(), content);
97 content->writeText(" ");
98 SkPDFScalar::Append(rect.height(), content);
99 content->writeText(" re\n");
100 }
101
102 // static
EmitPath(const SkPath & path,SkWStream * content)103 void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) {
104 SkPoint args[4];
105 SkPath::Iter iter(path, false);
106 for (SkPath::Verb verb = iter.next(args);
107 verb != SkPath::kDone_Verb;
108 verb = iter.next(args)) {
109 // args gets all the points, even the implicit first point.
110 switch (verb) {
111 case SkPath::kMove_Verb:
112 MoveTo(args[0].fX, args[0].fY, content);
113 break;
114 case SkPath::kLine_Verb:
115 AppendLine(args[1].fX, args[1].fY, content);
116 break;
117 case SkPath::kQuad_Verb: {
118 // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
119 const SkScalar three = SkIntToScalar(3);
120 args[1].scale(SkIntToScalar(2));
121 SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
122 SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
123 SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
124 SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
125 AppendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY,
126 content);
127 break;
128 }
129 case SkPath::kCubic_Verb:
130 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
131 args[3].fX, args[3].fY, content);
132 break;
133 case SkPath::kClose_Verb:
134 ClosePath(content);
135 break;
136 case SkPath::kDone_Verb:
137 break;
138 default:
139 SkASSERT(false);
140 break;
141 }
142 }
143 }
144
145 // static
ClosePath(SkWStream * content)146 void SkPDFUtils::ClosePath(SkWStream* content) {
147 content->writeText("h\n");
148 }
149
150 // static
PaintPath(SkPaint::Style style,SkPath::FillType fill,SkWStream * content)151 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
152 SkWStream* content) {
153 if (style == SkPaint::kFill_Style)
154 content->writeText("f");
155 else if (style == SkPaint::kStrokeAndFill_Style)
156 content->writeText("B");
157 else if (style == SkPaint::kStroke_Style)
158 content->writeText("S");
159
160 if (style != SkPaint::kStroke_Style) {
161 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
162 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
163 if (fill == SkPath::kEvenOdd_FillType)
164 content->writeText("*");
165 }
166 content->writeText("\n");
167 }
168
169 // static
StrokePath(SkWStream * content)170 void SkPDFUtils::StrokePath(SkWStream* content) {
171 SkPDFUtils::PaintPath(
172 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
173 }
174
175 // static
DrawFormXObject(int objectIndex,SkWStream * content)176 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
177 content->writeText("/X");
178 content->writeDecAsText(objectIndex);
179 content->writeText(" Do\n");
180 }
181
182 // static
ApplyGraphicState(int objectIndex,SkWStream * content)183 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
184 content->writeText("/G");
185 content->writeDecAsText(objectIndex);
186 content->writeText(" gs\n");
187 }
188