blob: d034270e07447f07503e603109868d72e9d252f9 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
vandebo@chromium.org683001c2012-05-09 17:17:51 +000010#include "SkData.h"
reed@google.coma44ea512011-07-27 18:24:25 +000011#include "SkGeometry.h"
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000012#include "SkPaint.h"
13#include "SkPath.h"
commit-bot@chromium.org47401352013-07-23 21:49:29 +000014#include "SkPDFResourceDict.h"
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000015#include "SkPDFUtils.h"
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000016#include "SkStream.h"
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000017#include "SkString.h"
18#include "SkPDFTypes.h"
19
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +000020//static
21SkPDFArray* 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
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000031// static
32SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
33 SkScalar values[6];
bungeman@google.com1ddd7c32011-07-13 19:41:55 +000034 if (!matrix.asAffine(values)) {
35 SkMatrix::SetAffineIdentity(values);
36 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000037
38 SkPDFArray* result = new SkPDFArray;
39 result->reserve(6);
40 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
reed@google.comc789cf12011-07-20 12:14:33 +000041 result->appendScalar(values[i]);
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000042 }
43 return result;
44}
45
46// static
vandebo@chromium.org75f97e42011-04-11 23:24:18 +000047void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
48 SkScalar values[6];
bungeman@google.com1ddd7c32011-07-13 19:41:55 +000049 if (!matrix.asAffine(values)) {
50 SkMatrix::SetAffineIdentity(values);
51 }
vandebo@chromium.org75f97e42011-04-11 23:24:18 +000052 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
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000060void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000061 SkPDFScalar::Append(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000062 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000063 SkPDFScalar::Append(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000064 content->writeText(" m\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000065}
66
67// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000068void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000069 SkPDFScalar::Append(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000070 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000071 SkPDFScalar::Append(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000072 content->writeText(" l\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000073}
74
75// static
76void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
77 SkScalar ctl2X, SkScalar ctl2Y,
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000078 SkScalar dstX, SkScalar dstY, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000079 SkString cmd("y\n");
80 SkPDFScalar::Append(ctl1X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000081 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000082 SkPDFScalar::Append(ctl1Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000083 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000084 if (ctl2X != dstX || ctl2Y != dstY) {
85 cmd.set("c\n");
86 SkPDFScalar::Append(ctl2X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000087 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000088 SkPDFScalar::Append(ctl2Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000089 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000090 }
91 SkPDFScalar::Append(dstX, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000092 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000093 SkPDFScalar::Append(dstY, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000094 content->writeText(" ");
95 content->writeText(cmd.c_str());
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000096}
97
98// static
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000099void 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);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000104 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000105 SkPDFScalar::Append(bottom, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000106 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000107 SkPDFScalar::Append(rect.width(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000108 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000109 SkPDFScalar::Append(rect.height(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000110 content->writeText(" re\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000111}
112
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000113// static
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000114void 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 }
sugoi@google.come2e81132013-03-05 18:35:55 +0000129 SkPoint lastMovePt = SkPoint::Make(0,0);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000130 SkDynamicMemoryWStream currentSegment;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000131 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:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000139 MoveTo(args[0].fX, args[0].fY, &currentSegment);
140 lastMovePt = args[0];
141 fillState = kEmpty_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000142 break;
143 case SkPath::kLine_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000144 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 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000152 break;
153 case SkPath::kQuad_Verb: {
reed@google.coma44ea512011-07-27 18:24:25 +0000154 SkPoint cubic[4];
155 SkConvertQuadToCubic(args, cubic);
156 AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000157 cubic[3].fX, cubic[3].fY, &currentSegment);
158 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000159 break;
160 }
161 case SkPath::kCubic_Verb:
162 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000163 args[3].fX, args[3].fY, &currentSegment);
164 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000165 break;
166 case SkPath::kClose_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000167 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();
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000174 break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000175 default:
176 SkASSERT(false);
177 break;
178 }
179 }
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000180 if (currentSegment.bytesWritten() > 0) {
181 SkData* data = currentSegment.copyToData();
182 content->write(data->data(), data->size());
183 data->unref();
184 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000185}
186
187// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000188void SkPDFUtils::ClosePath(SkWStream* content) {
189 content->writeText("h\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000190}
191
192// static
193void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000194 SkWStream* content) {
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000195 if (style == SkPaint::kFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000196 content->writeText("f");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000197 } else if (style == SkPaint::kStrokeAndFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000198 content->writeText("B");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000199 } else if (style == SkPaint::kStroke_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000200 content->writeText("S");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000201 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000202
203 if (style != SkPaint::kStroke_Style) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000204 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
205 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000206 if (fill == SkPath::kEvenOdd_FillType) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000207 content->writeText("*");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000208 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000209 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000210 content->writeText("\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000211}
212
213// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000214void SkPDFUtils::StrokePath(SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000215 SkPDFUtils::PaintPath(
216 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
217}
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000218
219// static
220void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000221 content->writeText("/");
222 content->writeText(SkPDFResourceDict::getResourceName(
223 SkPDFResourceDict::kXObject_ResourceType,
224 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000225 content->writeText(" Do\n");
226}
227
228// static
229void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000230 content->writeText("/");
231 content->writeText(SkPDFResourceDict::getResourceName(
232 SkPDFResourceDict::kExtGState_ResourceType,
233 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000234 content->writeText(" gs\n");
235}
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000236
237// static
238void 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}