blob: 16abc2f9f252b381a26a4d8c016361018d71c712 [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
20// static
21SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
22 SkScalar values[6];
bungeman@google.com1ddd7c32011-07-13 19:41:55 +000023 if (!matrix.asAffine(values)) {
24 SkMatrix::SetAffineIdentity(values);
25 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000026
27 SkPDFArray* result = new SkPDFArray;
28 result->reserve(6);
29 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
reed@google.comc789cf12011-07-20 12:14:33 +000030 result->appendScalar(values[i]);
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000031 }
32 return result;
33}
34
35// static
vandebo@chromium.org75f97e42011-04-11 23:24:18 +000036void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
37 SkScalar values[6];
bungeman@google.com1ddd7c32011-07-13 19:41:55 +000038 if (!matrix.asAffine(values)) {
39 SkMatrix::SetAffineIdentity(values);
40 }
vandebo@chromium.org75f97e42011-04-11 23:24:18 +000041 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
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000049void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000050 SkPDFScalar::Append(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000051 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000052 SkPDFScalar::Append(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000053 content->writeText(" m\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000054}
55
56// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000057void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000058 SkPDFScalar::Append(x, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000059 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000060 SkPDFScalar::Append(y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000061 content->writeText(" l\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000062}
63
64// static
65void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
66 SkScalar ctl2X, SkScalar ctl2Y,
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000067 SkScalar dstX, SkScalar dstY, SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000068 SkString cmd("y\n");
69 SkPDFScalar::Append(ctl1X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000070 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000071 SkPDFScalar::Append(ctl1Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000072 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000073 if (ctl2X != dstX || ctl2Y != dstY) {
74 cmd.set("c\n");
75 SkPDFScalar::Append(ctl2X, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000076 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000077 SkPDFScalar::Append(ctl2Y, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000078 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000079 }
80 SkPDFScalar::Append(dstX, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000081 content->writeText(" ");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000082 SkPDFScalar::Append(dstY, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000083 content->writeText(" ");
84 content->writeText(cmd.c_str());
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +000085}
86
87// static
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000088void 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);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000093 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000094 SkPDFScalar::Append(bottom, content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000095 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000096 SkPDFScalar::Append(rect.width(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000097 content->writeText(" ");
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +000098 SkPDFScalar::Append(rect.height(), content);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000099 content->writeText(" re\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000100}
101
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000102// static
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000103void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
104 SkWStream* content) {
105 // Filling a path with no area results in a drawing in PDF renderers but
106 // Chrome expects to be able to draw some such entities with no visible
107 // result, so we detect those cases and discard the drawing for them.
108 // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
109 enum SkipFillState {
110 kEmpty_SkipFillState = 0,
111 kSingleLine_SkipFillState = 1,
112 kNonSingleLine_SkipFillState = 2,
113 };
114 SkipFillState fillState = kEmpty_SkipFillState;
115 if (paintStyle != SkPaint::kFill_Style) {
116 fillState = kNonSingleLine_SkipFillState;
117 }
sugoi@google.come2e81132013-03-05 18:35:55 +0000118 SkPoint lastMovePt = SkPoint::Make(0,0);
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000119 SkDynamicMemoryWStream currentSegment;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000120 SkPoint args[4];
121 SkPath::Iter iter(path, false);
122 for (SkPath::Verb verb = iter.next(args);
123 verb != SkPath::kDone_Verb;
124 verb = iter.next(args)) {
125 // args gets all the points, even the implicit first point.
126 switch (verb) {
127 case SkPath::kMove_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000128 MoveTo(args[0].fX, args[0].fY, &currentSegment);
129 lastMovePt = args[0];
130 fillState = kEmpty_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000131 break;
132 case SkPath::kLine_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000133 AppendLine(args[1].fX, args[1].fY, &currentSegment);
134 if (fillState == kEmpty_SkipFillState) {
135 if (args[0] != lastMovePt) {
136 fillState = kSingleLine_SkipFillState;
137 }
138 } else if (fillState == kSingleLine_SkipFillState) {
139 fillState = kNonSingleLine_SkipFillState;
140 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000141 break;
142 case SkPath::kQuad_Verb: {
reed@google.coma44ea512011-07-27 18:24:25 +0000143 SkPoint cubic[4];
144 SkConvertQuadToCubic(args, cubic);
145 AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000146 cubic[3].fX, cubic[3].fY, &currentSegment);
147 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000148 break;
149 }
150 case SkPath::kCubic_Verb:
151 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000152 args[3].fX, args[3].fY, &currentSegment);
153 fillState = kNonSingleLine_SkipFillState;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000154 break;
155 case SkPath::kClose_Verb:
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000156 if (fillState != kSingleLine_SkipFillState) {
157 ClosePath(&currentSegment);
158 SkData* data = currentSegment.copyToData();
159 content->write(data->data(), data->size());
160 data->unref();
161 }
162 currentSegment.reset();
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000163 break;
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000164 default:
165 SkASSERT(false);
166 break;
167 }
168 }
vandebo@chromium.org683001c2012-05-09 17:17:51 +0000169 if (currentSegment.bytesWritten() > 0) {
170 SkData* data = currentSegment.copyToData();
171 content->write(data->data(), data->size());
172 data->unref();
173 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000174}
175
176// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000177void SkPDFUtils::ClosePath(SkWStream* content) {
178 content->writeText("h\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000179}
180
181// static
182void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000183 SkWStream* content) {
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000184 if (style == SkPaint::kFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000185 content->writeText("f");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000186 } else if (style == SkPaint::kStrokeAndFill_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000187 content->writeText("B");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000188 } else if (style == SkPaint::kStroke_Style) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000189 content->writeText("S");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000190 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000191
192 if (style != SkPaint::kStroke_Style) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000193 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
194 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000195 if (fill == SkPath::kEvenOdd_FillType) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000196 content->writeText("*");
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000197 }
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000198 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000199 content->writeText("\n");
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000200}
201
202// static
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000203void SkPDFUtils::StrokePath(SkWStream* content) {
ctguil@chromium.orgf966fd32011-03-04 21:47:04 +0000204 SkPDFUtils::PaintPath(
205 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
206}
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000207
208// static
209void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000210 content->writeText("/");
211 content->writeText(SkPDFResourceDict::getResourceName(
212 SkPDFResourceDict::kXObject_ResourceType,
213 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000214 content->writeText(" Do\n");
215}
216
217// static
218void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
commit-bot@chromium.org47401352013-07-23 21:49:29 +0000219 content->writeText("/");
220 content->writeText(SkPDFResourceDict::getResourceName(
221 SkPDFResourceDict::kExtGState_ResourceType,
222 objectIndex).c_str());
vandebo@chromium.org6112c212011-05-13 03:50:38 +0000223 content->writeText(" gs\n");
224}