blob: e488f7da06956ede2d49139a788f3925ccd57b16 [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
reed40c85e42015-01-05 10:01:25 -080098static void append_quad(const SkPoint quad[], SkWStream* content) {
99 SkPoint cubic[4];
100 SkConvertQuadToCubic(quad, cubic);
101 SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
102 cubic[3].fX, cubic[3].fY, content);
103}
104
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000105// static
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000106void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
107 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
108 SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
109
110 SkPDFScalar::Append(rect.fLeft, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000111 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000112 SkPDFScalar::Append(bottom, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000113 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000114 SkPDFScalar::Append(rect.width(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000115 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000116 SkPDFScalar::Append(rect.height(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000117 content->writeText(" re\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000118}
119
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000120// static
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000121void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
122 SkWStream* content) {
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 enum SkipFillState {
128 kEmpty_SkipFillState = 0,
129 kSingleLine_SkipFillState = 1,
130 kNonSingleLine_SkipFillState = 2,
131 };
132 SkipFillState fillState = kEmpty_SkipFillState;
133 if (paintStyle != SkPaint::kFill_Style) {
134 fillState = kNonSingleLine_SkipFillState;
135 }
sugoi@google.come2e81132013-03-05 18:35:55 +0000136 SkPoint lastMovePt = SkPoint::Make(0,0);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000137 SkDynamicMemoryWStream currentSegment;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000138 SkPoint args[4];
139 SkPath::Iter iter(path, false);
reed40c85e42015-01-05 10:01:25 -0800140 for (SkPath::Verb verb = iter.next(args); verb != SkPath::kDone_Verb; verb = iter.next(args)) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000141 // args gets all the points, even the implicit first point.
142 switch (verb) {
143 case SkPath::kMove_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000144 MoveTo(args[0].fX, args[0].fY, &currentSegment);
145 lastMovePt = args[0];
146 fillState = kEmpty_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000147 break;
148 case SkPath::kLine_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000149 AppendLine(args[1].fX, args[1].fY, &currentSegment);
150 if (fillState == kEmpty_SkipFillState) {
151 if (args[0] != lastMovePt) {
152 fillState = kSingleLine_SkipFillState;
153 }
154 } else if (fillState == kSingleLine_SkipFillState) {
155 fillState = kNonSingleLine_SkipFillState;
156 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000157 break;
reed40c85e42015-01-05 10:01:25 -0800158 case SkPath::kQuad_Verb:
159 append_quad(args, &currentSegment);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000160 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000161 break;
reed40c85e42015-01-05 10:01:25 -0800162 case SkPath::kConic_Verb: {
163 const SkScalar tol = SK_Scalar1 / 4;
164 SkAutoConicToQuads converter;
165 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tol);
166 for (int i = 0; i < converter.countQuads(); ++i) {
167 append_quad(&quads[i * 2], &currentSegment);
168 }
169 } break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000170 case SkPath::kCubic_Verb:
171 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000172 args[3].fX, args[3].fY, &currentSegment);
173 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000174 break;
175 case SkPath::kClose_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000176 if (fillState != kSingleLine_SkipFillState) {
177 ClosePath(&currentSegment);
178 SkData* data = currentSegment.copyToData();
179 content->write(data->data(), data->size());
180 data->unref();
181 }
182 currentSegment.reset();
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000183 break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000184 default:
185 SkASSERT(false);
186 break;
187 }
188 }
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000189 if (currentSegment.bytesWritten() > 0) {
190 SkData* data = currentSegment.copyToData();
191 content->write(data->data(), data->size());
192 data->unref();
193 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000194}
195
196// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000197void SkPDFUtils::ClosePath(SkWStream* content) {
198 content->writeText("h\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000199}
200
201// static
202void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000203 SkWStream* content) {
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000204 if (style == SkPaint::kFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000205 content->writeText("f");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000206 } else if (style == SkPaint::kStrokeAndFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000207 content->writeText("B");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000208 } else if (style == SkPaint::kStroke_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000209 content->writeText("S");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000210 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000211
212 if (style != SkPaint::kStroke_Style) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000213 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
214 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000215 if (fill == SkPath::kEvenOdd_FillType) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000216 content->writeText("*");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000217 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000218 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000219 content->writeText("\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000220}
221
222// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000223void SkPDFUtils::StrokePath(SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000224 SkPDFUtils::PaintPath(
225 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
226}
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000227
228// static
229void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000230 content->writeText("/");
231 content->writeText(SkPDFResourceDict::getResourceName(
232 SkPDFResourceDict::kXObject_ResourceType,
233 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000234 content->writeText(" Do\n");
235}
236
237// static
238void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000239 content->writeText("/");
240 content->writeText(SkPDFResourceDict::getResourceName(
241 SkPDFResourceDict::kExtGState_ResourceType,
242 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000243 content->writeText(" gs\n");
244}
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000245
246// static
247void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
248 // Select Pattern color space (CS, cs) and set pattern object as current
249 // color (SCN, scn)
250 SkString resourceName = SkPDFResourceDict::getResourceName(
251 SkPDFResourceDict::kPattern_ResourceType,
252 objectIndex);
253 content->writeText("/Pattern CS/Pattern cs/");
254 content->writeText(resourceName.c_str());
255 content->writeText(" SCN/");
256 content->writeText(resourceName.c_str());
257 content->writeText(" scn\n");
258}