blob: 13ffd5b0d4b324a5e0a35b4e5fc0697c6b0e0afc [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00002 * Copyright (C) 2011 Google Inc.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00003 *
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 "SkPDFDevice.h"
18
19#include "SkColor.h"
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +000020#include "SkDraw.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000021#include "SkGlyphCache.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000022#include "SkPaint.h"
vandebo@chromium.orga5180862010-10-26 19:48:49 +000023#include "SkPath.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000024#include "SkPDFFont.h"
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +000025#include "SkPDFFormXObject.h"
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +000026#include "SkPDFGraphicState.h"
27#include "SkPDFImage.h"
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000028#include "SkPDFShader.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000029#include "SkPDFStream.h"
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +000030#include "SkPDFTypes.h"
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +000031#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000032#include "SkRect.h"
33#include "SkString.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000034#include "SkTextFormatParams.h"
35#include "SkTypeface.h"
36#include "SkTypes.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000037
38// Utility functions
39
40namespace {
41
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000042void emitPDFColor(SkColor color, SkWStream* result) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000043 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
44 SkScalar colorMax = SkIntToScalar(0xFF);
vandebo@chromium.org094316b2011-03-04 03:15:13 +000045 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000046 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
47 result->writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000048 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000049 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
50 result->writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000051 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000052 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
53 result->writeText(" ");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000054}
55
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000056SkPaint calculateTextPaint(const SkPaint& paint) {
57 SkPaint result = paint;
58 if (result.isFakeBoldText()) {
59 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
60 kStdFakeBoldInterpKeys,
61 kStdFakeBoldInterpValues,
62 kStdFakeBoldInterpLength);
63 SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
64 if (result.getStyle() == SkPaint::kFill_Style)
65 result.setStyle(SkPaint::kStrokeAndFill_Style);
66 else
67 width += result.getStrokeWidth();
68 result.setStrokeWidth(width);
69 }
70 return result;
71}
72
73// Stolen from measure_text in SkDraw.cpp and then tweaked.
74void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
75 const uint16_t* glyphs, size_t len, SkScalar* x, SkScalar* y,
76 SkScalar* width) {
77 if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
78 return;
79
80 SkMatrix ident;
81 ident.reset();
82 SkAutoGlyphCache autoCache(paint, &ident);
83 SkGlyphCache* cache = autoCache.getCache();
84
85 const char* start = (char*)glyphs;
86 const char* stop = (char*)(glyphs + len);
87 SkFixed xAdv = 0, yAdv = 0;
88
89 // TODO(vandebo) This probably needs to take kerning into account.
90 while (start < stop) {
91 const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
92 xAdv += glyph.fAdvanceX;
93 yAdv += glyph.fAdvanceY;
94 };
95 if (width)
96 *width = SkFixedToScalar(xAdv);
97 if (paint.getTextAlign() == SkPaint::kLeft_Align)
98 return;
99
100 SkScalar xAdj = SkFixedToScalar(xAdv);
101 SkScalar yAdj = SkFixedToScalar(yAdv);
102 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
103 xAdj = SkScalarHalf(xAdj);
104 yAdj = SkScalarHalf(yAdj);
105 }
106 *x = *x - xAdj;
107 *y = *y - yAdj;
108}
109
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000110} // namespace
111
112////////////////////////////////////////////////////////////////////////////////
113
reed@android.comf2b98d62010-12-20 18:26:13 +0000114SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
115 int width, int height, bool isOpaque,
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000116 bool isForLayer) {
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000117 SkMatrix initialTransform;
118 initialTransform.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000119 if (isForLayer) {
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000120 initialTransform.setTranslate(0, height);
121 initialTransform.preScale(1, -1);
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000122 }
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000123 return SkNEW_ARGS(SkPDFDevice, (width, height, initialTransform));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000124}
125
reed@android.comf2b98d62010-12-20 18:26:13 +0000126static inline SkBitmap makeABitmap(int width, int height) {
127 SkBitmap bitmap;
128 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
129 return bitmap;
130}
131
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000132SkPDFDevice::SkPDFDevice(int width, int height,
133 const SkMatrix& initialTransform)
reed@google.com07700442010-12-20 19:46:07 +0000134 : SkDevice(NULL, makeABitmap(width, height), false),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000135 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000136 fHeight(height),
reed@google.com07700442010-12-20 19:46:07 +0000137 fGraphicStackIndex(0) {
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000138 // Skia generally uses the top left as the origin but PDF natively has the
139 // origin at the bottom left. This matrix corrects for that. When layering,
140 // we specify an inverse correction to cancel this out.
141 fInitialTransform.setTranslate(0, height);
142 fInitialTransform.preScale(1, -1);
143 fInitialTransform.preConcat(initialTransform);
144
145 this->init();
146}
147
148SkPDFDevice::~SkPDFDevice() {
149 this->cleanUp();
150}
151
152void SkPDFDevice::init() {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000153 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000154 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000155 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000156 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000157 fGraphicStack[0].fFont = NULL;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000158 fGraphicStack[0].fShader = NULL;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000159 fGraphicStack[0].fGraphicState = NULL;
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000160 fGraphicStack[0].fClip.setRect(0,0, fWidth, fHeight);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000161 fGraphicStack[0].fTransform.reset();
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000162 fGraphicStackIndex = 0;
163 fResourceDict = NULL;
164 fContent.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000165
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000166 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
167 SkPDFUtils::AppendTransform(fInitialTransform, &fContent);
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000168 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000169}
170
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000171SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() {
172 return SkNEW(SkPDFDeviceFactory);
173}
174
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000175void SkPDFDevice::cleanUp() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000176 fGraphicStateResources.unrefAll();
177 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000178 fFontResources.unrefAll();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000179 fShaderResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000180}
181
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000182void SkPDFDevice::clear(SkColor color) {
183 SkMatrix curTransform = fGraphicStack[fGraphicStackIndex].fTransform;
184 SkRegion curClip = fGraphicStack[fGraphicStackIndex].fClip;
185
186 this->cleanUp();
187 this->init();
188
189 SkPaint paint;
190 paint.setColor(color);
191 paint.setStyle(SkPaint::kFill_Style);
192 updateGSFromPaint(paint, false);
193 internalDrawPaint(paint);
194
195 SkClipStack clipStack;
196 setMatrixClip(curTransform, curClip, clipStack);
197}
198
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000199void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000200 const SkRegion& region,
201 const SkClipStack&) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000202 if (region.isEmpty()) {
203 return;
204 }
205
206 // TODO(vandebo) SkCanvas may not draw anything after doing this call, we
207 // should defer writing anything out to fContent until we actually get
208 // a draw command. In fact SkDraw contains the clip and transform, so
209 // this method should do nothing and we should have an update
210 // transform-and-clip method, like update paint.
211
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000212 // See the comment in the header file above GraphicStackEntry.
213 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
214 while (fGraphicStackIndex > 0)
215 popGS();
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000216
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000217 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
218 pushGS();
219
220 SkPath clipPath;
221 SkAssertResult(region.getBoundaryPath(&clipPath));
222
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000223 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000224 SkPath::FillType clipFill = clipPath.getFillType();
225 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
226 false);
227 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
228 false);
229 if (clipFill == SkPath::kEvenOdd_FillType)
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000230 fContent.writeText("W* n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000231 else
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000232 fContent.writeText("W n ");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000233
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000234 fGraphicStack[fGraphicStackIndex].fClip = region;
235 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000236 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000237 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000238}
239
240void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000241 if (d.fClip->isEmpty()) {
242 return;
243 }
244
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000245 SkPaint newPaint = paint;
246 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000247 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000248
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000249 internalDrawPaint(newPaint);
250}
251
252void SkPDFDevice::internalDrawPaint(const SkPaint& paint) {
253 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
254 SkIntToScalar(this->height()));
255 SkMatrix totalTransform = fInitialTransform;
256 totalTransform.preConcat(fGraphicStack[fGraphicStackIndex].fTransform);
257 SkMatrix inverse;
258 inverse.reset();
259 totalTransform.invert(&inverse);
260 inverse.mapRect(&bbox);
261
262 internalDrawRect(bbox, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000263}
264
265void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
266 size_t count, const SkPoint* points,
267 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000268 if (count == 0 || d.fClip->isEmpty()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000269 return;
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000270 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000271
272 switch (mode) {
273 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000274 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000275 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
276 for (size_t i = 1; i < count; i++) {
277 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
278 }
279 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000280 break;
281 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000282 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000283 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000284 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
285 &fContent);
286 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
287 points[i * 2 + 1].fY, &fContent);
288 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000289 }
290 break;
291 case SkCanvas::kPoints_PointMode:
292 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000293 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000294 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000295 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
296 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000297 }
298 } else {
299 // PDF won't draw a single point with square/butt caps because
300 // the orientation is ambiguous. Draw a rectangle instead.
301 SkPaint newPaint = paint;
302 newPaint.setStyle(SkPaint::kFill_Style);
303 SkScalar strokeWidth = paint.getStrokeWidth();
304 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
305 for (size_t i = 0; i < count; i++) {
306 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
307 0, 0);
308 r.inset(-halfStroke, -halfStroke);
309 drawRect(d, r, newPaint);
310 }
311 }
312 break;
313 default:
314 SkASSERT(false);
315 }
316}
317
318void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
319 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000320 if (d.fClip->isEmpty()) {
321 return;
322 }
323
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000324 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000325 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000326 SkPath path;
327 path.addRect(r);
328 paint.getFillPath(path, &path);
329
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000330 SkPaint noEffectPaint(paint);
331 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000332 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000333 return;
334 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000335 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000336
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000337 internalDrawRect(r, paint);
338}
339
340void SkPDFDevice::internalDrawRect(const SkRect& r, const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000341 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
342 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000343 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
344 &fContent);
345 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
346 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000347}
348
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000349void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000350 const SkPaint& paint, const SkMatrix* prePathMatrix,
351 bool pathIsMutable) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000352 if (d.fClip->isEmpty()) {
353 return;
354 }
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000355 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000356
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000357 if (paint.getPathEffect()) {
358 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000359 SkPath noEffectPath;
360 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000361
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000362 SkPaint noEffectPaint(paint);
363 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000364 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000365 return;
366 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000367 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000368
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000369 SkPDFUtils::EmitPath(path, &fContent);
370 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000371}
372
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000373void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000374 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000375 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000376 if (d.fClip->isEmpty()) {
377 return;
378 }
379
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000380 SkMatrix transform = matrix;
381 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000382 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000383}
384
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000385void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000386 int x, int y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000387 if (d.fClip->isEmpty()) {
388 return;
389 }
390
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000391 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000392 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000393 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000394}
395
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000396void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000397 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000398 if (d.fClip->isEmpty()) {
399 return;
400 }
401
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000402 SkPaint textPaint = calculateTextPaint(paint);
403 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000404
vandebo@chromium.org01294102011-02-28 19:52:18 +0000405 // We want the text in glyph id encoding and a writable buffer, so we end
406 // up making a copy either way.
407 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
408 uint16_t* glyphIDs =
409 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
410 SK_MALLOC_TEMP | SK_MALLOC_THROW);
411 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000412 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000413 paint.textToGlyphs(text, len, glyphIDs);
414 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
415 } else {
416 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000417 SkASSERT(len / 2 == numGlyphs);
418 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000419 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000420
421 SkScalar width;
422 SkScalar* widthPtr = NULL;
423 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
424 widthPtr = &width;
425
426 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000427 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000428 fContent.writeText("BT\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000429 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000430 size_t consumedGlyphCount = 0;
431 while (numGlyphs > consumedGlyphCount) {
432 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
433 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000434 size_t availableGlyphs =
435 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
436 numGlyphs - consumedGlyphCount);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000437 SkString encodedString =
438 SkPDFString::formatString(glyphIDs + consumedGlyphCount,
439 availableGlyphs, font->multiByteGlyphs());
440 fContent.writeText(encodedString.c_str());
vandebo@chromium.org01294102011-02-28 19:52:18 +0000441 consumedGlyphCount += availableGlyphs;
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000442 fContent.writeText(" Tj\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000443 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000444 fContent.writeText("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000445
446 // Draw underline and/or strikethrough if the paint has them.
447 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
448 // because the raster versions don't. Use paint instead of textPaint
449 // because we may have changed strokeWidth to do fakeBold text.
450 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
451 SkScalar textSize = paint.getTextSize();
452 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
453
454 if (paint.isUnderlineText()) {
455 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
456 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
457 drawRect(d, r, paint);
458 }
459 if (paint.isStrikeThruText()) {
460 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
461 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
462 drawRect(d, r, paint);
463 }
464 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000465}
466
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000467void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000468 const SkScalar pos[], SkScalar constY,
469 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000470 if (d.fClip->isEmpty()) {
471 return;
472 }
473
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000474 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
475 SkPaint textPaint = calculateTextPaint(paint);
476 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000477
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000478 // Make sure we have a glyph id encoding.
479 SkAutoFree glyphStorage;
480 uint16_t* glyphIDs;
481 size_t numGlyphs;
482 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
483 numGlyphs = paint.textToGlyphs(text, len, NULL);
484 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
485 SK_MALLOC_TEMP | SK_MALLOC_THROW);
486 glyphStorage.set(glyphIDs);
487 paint.textToGlyphs(text, len, glyphIDs);
488 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
489 } else {
490 SkASSERT((len & 1) == 0);
491 numGlyphs = len / 2;
492 glyphIDs = (uint16_t*)text;
493 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000494
495 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000496 fContent.writeText("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000497 updateFont(textPaint, glyphIDs[0]);
498 for (size_t i = 0; i < numGlyphs; i++) {
499 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000500 uint16_t encodedValue = glyphIDs[i];
501 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000502 updateFont(textPaint, glyphIDs[i]);
503 i--;
504 continue;
505 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000506 SkScalar x = pos[i * scalarsPerPos];
507 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000508 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000509 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000510 SkString encodedString =
511 SkPDFString::formatString(&encodedValue, 1,
512 font->multiByteGlyphs());
513 fContent.writeText(encodedString.c_str());
514 fContent.writeText(" Tj\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000515 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000516 fContent.writeText("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000517}
518
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000519void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000520 const SkPath& path, const SkMatrix* matrix,
521 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000522 if (d.fClip->isEmpty()) {
523 return;
524 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000525 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000526}
527
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000528void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000529 int vertexCount, const SkPoint verts[],
530 const SkPoint texs[], const SkColor colors[],
531 SkXfermode* xmode, const uint16_t indices[],
532 int indexCount, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000533 if (d.fClip->isEmpty()) {
534 return;
535 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000536 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000537}
538
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000539void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
540 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000541 if (d.fClip->isEmpty()) {
542 return;
543 }
544
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000545 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
546 // If we somehow get a raster device, do what our parent would do.
547 SkDevice::drawDevice(d, device, x, y, paint);
548 return;
549 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000550 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000551 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
552
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000553 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000554 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000555 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000556 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000557
558 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000559 fXObjectResources.push(xobject); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000560 fContent.writeText("/X");
561 fContent.writeDecAsText(fXObjectResources.count() - 1);
562 fContent.writeText(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000563 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000564}
565
566const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
567 if (fResourceDict.get() == NULL) {
568 fResourceDict = new SkPDFDict;
569 fResourceDict->unref(); // SkRefPtr and new both took a reference.
570
571 if (fGraphicStateResources.count()) {
572 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
573 extGState->unref(); // SkRefPtr and new both took a reference.
574 for (int i = 0; i < fGraphicStateResources.count(); i++) {
575 SkString nameString("G");
576 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000577 extGState->insert(
578 nameString.c_str(),
579 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000580 }
581 fResourceDict->insert("ExtGState", extGState.get());
582 }
583
584 if (fXObjectResources.count()) {
585 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
586 xObjects->unref(); // SkRefPtr and new both took a reference.
587 for (int i = 0; i < fXObjectResources.count(); i++) {
588 SkString nameString("X");
589 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000590 xObjects->insert(
591 nameString.c_str(),
592 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000593 }
594 fResourceDict->insert("XObject", xObjects.get());
595 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000596
597 if (fFontResources.count()) {
598 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
599 fonts->unref(); // SkRefPtr and new both took a reference.
600 for (int i = 0; i < fFontResources.count(); i++) {
601 SkString nameString("F");
602 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000603 fonts->insert(nameString.c_str(),
604 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000605 }
606 fResourceDict->insert("Font", fonts.get());
607 }
608
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000609 if (fShaderResources.count()) {
610 SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
611 patterns->unref(); // SkRefPtr and new both took a reference.
612 for (int i = 0; i < fShaderResources.count(); i++) {
613 SkString nameString("P");
614 nameString.appendS32(i);
615 patterns->insert(nameString.c_str(),
616 new SkPDFObjRef(fShaderResources[i]))->unref();
617 }
618 fResourceDict->insert("Pattern", patterns.get());
619 }
620
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000621 // For compatibility, add all proc sets (only used for output to PS
622 // devices).
623 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
624 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
625 procSets->unref(); // SkRefPtr and new both took a reference.
626 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000627 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
628 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000629 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000630 }
631 return fResourceDict;
632}
633
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000634void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
635 resourceList->setReserve(resourceList->count() +
636 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000637 fXObjectResources.count() +
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000638 fFontResources.count() +
639 fShaderResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000640 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000641 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000642 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000643 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000644 }
645 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000646 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000647 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000648 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000649 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000650 for (int i = 0; i < fFontResources.count(); i++) {
651 resourceList->push(fFontResources[i]);
652 fFontResources[i]->ref();
653 fFontResources[i]->getResources(resourceList);
654 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000655 for (int i = 0; i < fShaderResources.count(); i++) {
656 resourceList->push(fShaderResources[i]);
657 fShaderResources[i]->ref();
658 fShaderResources[i]->getResources(resourceList);
659 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000660}
661
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000662SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000663 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
664 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000665
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000666 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
667 mediaBox->unref(); // SkRefPtr and new both took a reference.
668 mediaBox->reserve(4);
669 mediaBox->append(zero.get());
670 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000671 mediaBox->append(new SkPDFInt(fWidth))->unref();
672 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000673 return mediaBox;
674}
675
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000676SkStream* SkPDFDevice::content() const {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000677 size_t offset = fContent.getOffset();
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000678 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000679 fContent.copyTo(data);
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000680 for (int i = 0; i < fGraphicStackIndex; i++) {
681 data[offset++] = 'Q';
682 data[offset++] = '\n';
683 }
684 SkMemoryStream* result = new SkMemoryStream;
685 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000686 return result;
687}
688
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000689void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
690 SkASSERT(paint.getPathEffect() == NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000691
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000692 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
693 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000694
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000695 SkPaint newPaint = paint;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000696
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000697 // PDF treats a shader as a color, so we only set one or the other.
698 SkRefPtr<SkPDFShader> pdfShader;
699 const SkShader* shader = newPaint.getShader();
700 if (shader) {
701 // PDF positions patterns relative to the initial transform, so
702 // we need to apply the current transform to the shader parameters.
703 SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000704 transform.postConcat(fInitialTransform);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000705
706 // PDF doesn't support kClamp_TileMode, so we simulate it by making
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000707 // a pattern the size of the drawing surface.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000708 SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
709 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
710 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
711
712 // A color shader is treated as an invalid shader so we don't have
713 // to set a shader just for a color.
714 if (pdfShader.get() == NULL) {
715 newPaint.setColor(0);
716
717 // Check for a color shader.
718 SkShader::GradientInfo gradientInfo;
719 SkColor gradientColor;
720 gradientInfo.fColors = &gradientColor;
721 gradientInfo.fColorOffsets = NULL;
722 gradientInfo.fColorCount = 1;
723 if (shader->asAGradient(&gradientInfo) ==
724 SkShader::kColor_GradientType) {
725 newPaint.setColor(gradientColor);
726 }
727 }
728 }
729
730 if (pdfShader) {
731 // pdfShader has been canonicalized so we can directly compare
732 // pointers.
733 if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
734 int resourceIndex = fShaderResources.find(pdfShader.get());
735 if (resourceIndex < 0) {
736 resourceIndex = fShaderResources.count();
737 fShaderResources.push(pdfShader.get());
738 pdfShader->ref();
739 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000740 fContent.writeText("/Pattern CS /Pattern cs /P");
741 fContent.writeDecAsText(resourceIndex);
742 fContent.writeText(" SCN /P");
743 fContent.writeDecAsText(resourceIndex);
744 fContent.writeText(" scn\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000745 fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
746 }
747 } else {
748 SkColor newColor = newPaint.getColor();
749 newColor = SkColorSetA(newColor, 0xFF);
750 if (fGraphicStack[fGraphicStackIndex].fShader ||
751 fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000752 emitPDFColor(newColor, &fContent);
753 fContent.writeText("RG ");
754 emitPDFColor(newColor, &fContent);
755 fContent.writeText("rg\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000756 fGraphicStack[fGraphicStackIndex].fColor = newColor;
757 fGraphicStack[fGraphicStackIndex].fShader = NULL;
758 }
759 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000760
761 SkRefPtr<SkPDFGraphicState> newGraphicState =
762 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
763 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
764 // newGraphicState has been canonicalized so we can directly compare
765 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000766 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000767 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000768 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
769 if (resourceIndex < 0) {
770 resourceIndex = fGraphicStateResources.count();
771 fGraphicStateResources.push(newGraphicState.get());
772 newGraphicState->ref();
773 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000774 fContent.writeText("/G");
775 fContent.writeDecAsText(resourceIndex);
776 fContent.writeText(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000777 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000778 }
779
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000780 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000781 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
782 newPaint.getTextScaleX()) {
783 SkScalar scale = newPaint.getTextScaleX();
784 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000785 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000786 fContent.writeText(" Tz\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000787 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
788 }
789
790 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
791 newPaint.getStyle()) {
792 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
793 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
794 enum_must_match_value);
795 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
796 enum_must_match_value);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000797 fContent.writeDecAsText(newPaint.getStyle());
798 fContent.writeText(" Tr\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000799 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
800 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000801 }
802}
803
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000804void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000805 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000806 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
807 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000808 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000809 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000810 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000811 fContent.writeText("/F");
812 fContent.writeDecAsText(fontIndex);
813 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000814 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000815 fContent.writeText(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000816 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
817 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
818 }
819}
820
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000821int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
822 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000823 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000824 int resourceIndex = fFontResources.find(newFont.get());
825 if (resourceIndex < 0) {
826 resourceIndex = fFontResources.count();
827 fFontResources.push(newFont.get());
828 newFont->ref();
829 }
830 return resourceIndex;
831}
832
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000833void SkPDFDevice::pushGS() {
834 SkASSERT(fGraphicStackIndex < 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000835 fContent.writeText("q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000836 fGraphicStackIndex++;
837 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
838}
839
840void SkPDFDevice::popGS() {
841 SkASSERT(fGraphicStackIndex > 0);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000842 fContent.writeText("Q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000843 fGraphicStackIndex--;
844}
845
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000846void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
847 // Flip the text about the x-axis to account for origin swap and include
848 // the passed parameters.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000849 fContent.writeText("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000850 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000851 fContent.writeText(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000852 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000853 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000854 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000855 fContent.writeText(" Tm\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000856}
857
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000858void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
859 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000860 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000861 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000862 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
863 if (srcRect && !subset.intersect(*srcRect))
864 return;
865
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000866 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
867 if (!image)
868 return;
869
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000870 SkMatrix scaled;
871 // Adjust for origin flip.
872 scaled.setScale(1, -1);
873 scaled.postTranslate(0, 1);
874 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000875 scaled.postScale(SkIntToScalar(subset.width()),
876 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000877 scaled.postConcat(matrix);
878 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000879 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000880
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000881 fXObjectResources.push(image); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000882 fContent.writeText("/X");
883 fContent.writeDecAsText(fXObjectResources.count() - 1);
884 fContent.writeText(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000885 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000886}
887
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000888SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
889 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
890 if (old == m)
891 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000892
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000893 if (old.getType() != SkMatrix::kIdentity_Mask) {
894 SkASSERT(fGraphicStackIndex > 0);
895 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
896 SkMatrix::kIdentity_Mask);
897 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
898 fGraphicStack[fGraphicStackIndex - 1].fClip);
899 popGS();
900 }
901 if (m.getType() == SkMatrix::kIdentity_Mask)
902 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000903
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000904 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
905 fGraphicStack[fGraphicStackIndex - 1].fClip)
906 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000907
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000908 SkPDFUtils::AppendTransform(m, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000909 fGraphicStack[fGraphicStackIndex].fTransform = m;
910
911 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000912}