blob: efe725770478473b8cf4a50a0e770b0a38fed0b6 [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
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000171void SkPDFDevice::cleanUp() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000172 fGraphicStateResources.unrefAll();
173 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000174 fFontResources.unrefAll();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000175 fShaderResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000176}
177
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000178void SkPDFDevice::clear(SkColor color) {
179 SkMatrix curTransform = fGraphicStack[fGraphicStackIndex].fTransform;
180 SkRegion curClip = fGraphicStack[fGraphicStackIndex].fClip;
181
182 this->cleanUp();
183 this->init();
184
185 SkPaint paint;
186 paint.setColor(color);
187 paint.setStyle(SkPaint::kFill_Style);
188 updateGSFromPaint(paint, false);
189 internalDrawPaint(paint);
190
191 SkClipStack clipStack;
192 setMatrixClip(curTransform, curClip, clipStack);
193}
194
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000195void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000196 const SkRegion& region,
197 const SkClipStack&) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000198 if (region.isEmpty()) {
199 return;
200 }
201
202 // TODO(vandebo) SkCanvas may not draw anything after doing this call, we
203 // should defer writing anything out to fContent until we actually get
204 // a draw command. In fact SkDraw contains the clip and transform, so
205 // this method should do nothing and we should have an update
206 // transform-and-clip method, like update paint.
207
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000208 // See the comment in the header file above GraphicStackEntry.
209 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
210 while (fGraphicStackIndex > 0)
211 popGS();
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000212
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000213 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
214 pushGS();
215
216 SkPath clipPath;
217 SkAssertResult(region.getBoundaryPath(&clipPath));
218
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000219 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000220 SkPath::FillType clipFill = clipPath.getFillType();
221 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
222 false);
223 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
224 false);
225 if (clipFill == SkPath::kEvenOdd_FillType)
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000226 fContent.writeText("W* n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000227 else
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000228 fContent.writeText("W n ");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000229
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000230 fGraphicStack[fGraphicStackIndex].fClip = region;
231 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000232 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000233 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000234}
235
236void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000237 if (d.fClip->isEmpty()) {
238 return;
239 }
240
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000241 SkPaint newPaint = paint;
242 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000243 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000244
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000245 internalDrawPaint(newPaint);
246}
247
248void SkPDFDevice::internalDrawPaint(const SkPaint& paint) {
249 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
250 SkIntToScalar(this->height()));
251 SkMatrix totalTransform = fInitialTransform;
252 totalTransform.preConcat(fGraphicStack[fGraphicStackIndex].fTransform);
253 SkMatrix inverse;
254 inverse.reset();
255 totalTransform.invert(&inverse);
256 inverse.mapRect(&bbox);
257
258 internalDrawRect(bbox, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000259}
260
261void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
262 size_t count, const SkPoint* points,
263 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000264 if (count == 0 || d.fClip->isEmpty()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000265 return;
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000266 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000267
268 switch (mode) {
269 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000270 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000271 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
272 for (size_t i = 1; i < count; i++) {
273 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
274 }
275 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000276 break;
277 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000278 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000279 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000280 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
281 &fContent);
282 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
283 points[i * 2 + 1].fY, &fContent);
284 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000285 }
286 break;
287 case SkCanvas::kPoints_PointMode:
288 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000289 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000290 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000291 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
292 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000293 }
294 } else {
295 // PDF won't draw a single point with square/butt caps because
296 // the orientation is ambiguous. Draw a rectangle instead.
297 SkPaint newPaint = paint;
298 newPaint.setStyle(SkPaint::kFill_Style);
299 SkScalar strokeWidth = paint.getStrokeWidth();
300 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
301 for (size_t i = 0; i < count; i++) {
302 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
303 0, 0);
304 r.inset(-halfStroke, -halfStroke);
305 drawRect(d, r, newPaint);
306 }
307 }
308 break;
309 default:
310 SkASSERT(false);
311 }
312}
313
314void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
315 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000316 if (d.fClip->isEmpty()) {
317 return;
318 }
319
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000320 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000321 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000322 SkPath path;
323 path.addRect(r);
324 paint.getFillPath(path, &path);
325
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000326 SkPaint noEffectPaint(paint);
327 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000328 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000329 return;
330 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000331 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000332
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000333 internalDrawRect(r, paint);
334}
335
336void SkPDFDevice::internalDrawRect(const SkRect& r, const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000337 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
338 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000339 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
340 &fContent);
341 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
342 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000343}
344
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000345void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000346 const SkPaint& paint, const SkMatrix* prePathMatrix,
347 bool pathIsMutable) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000348 if (d.fClip->isEmpty()) {
349 return;
350 }
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000351 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000352
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000353 if (paint.getPathEffect()) {
354 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000355 SkPath noEffectPath;
356 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000357
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000358 SkPaint noEffectPaint(paint);
359 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000360 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000361 return;
362 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000363 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000364
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000365 SkPDFUtils::EmitPath(path, &fContent);
366 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000367}
368
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000369void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000370 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000371 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000372 if (d.fClip->isEmpty()) {
373 return;
374 }
375
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000376 SkMatrix transform = matrix;
377 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000378 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000379}
380
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000381void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000382 int x, int y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000383 if (d.fClip->isEmpty()) {
384 return;
385 }
386
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000387 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000388 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000389 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000390}
391
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000392void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000393 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000394 if (d.fClip->isEmpty()) {
395 return;
396 }
397
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000398 SkPaint textPaint = calculateTextPaint(paint);
399 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000400
vandebo@chromium.org01294102011-02-28 19:52:18 +0000401 // We want the text in glyph id encoding and a writable buffer, so we end
402 // up making a copy either way.
403 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
404 uint16_t* glyphIDs =
405 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
406 SK_MALLOC_TEMP | SK_MALLOC_THROW);
407 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000408 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000409 paint.textToGlyphs(text, len, glyphIDs);
410 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
411 } else {
412 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000413 SkASSERT(len / 2 == numGlyphs);
414 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000415 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000416
417 SkScalar width;
418 SkScalar* widthPtr = NULL;
419 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
420 widthPtr = &width;
421
422 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000423 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000424 fContent.writeText("BT\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000425 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000426 size_t consumedGlyphCount = 0;
427 while (numGlyphs > consumedGlyphCount) {
428 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
429 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000430 size_t availableGlyphs =
431 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
432 numGlyphs - consumedGlyphCount);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000433 SkString encodedString =
434 SkPDFString::formatString(glyphIDs + consumedGlyphCount,
435 availableGlyphs, font->multiByteGlyphs());
436 fContent.writeText(encodedString.c_str());
vandebo@chromium.org01294102011-02-28 19:52:18 +0000437 consumedGlyphCount += availableGlyphs;
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000438 fContent.writeText(" Tj\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000439 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000440 fContent.writeText("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000441
442 // Draw underline and/or strikethrough if the paint has them.
443 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
444 // because the raster versions don't. Use paint instead of textPaint
445 // because we may have changed strokeWidth to do fakeBold text.
446 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
447 SkScalar textSize = paint.getTextSize();
448 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
449
450 if (paint.isUnderlineText()) {
451 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
452 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
453 drawRect(d, r, paint);
454 }
455 if (paint.isStrikeThruText()) {
456 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
457 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
458 drawRect(d, r, paint);
459 }
460 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000461}
462
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000463void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000464 const SkScalar pos[], SkScalar constY,
465 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000466 if (d.fClip->isEmpty()) {
467 return;
468 }
469
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000470 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
471 SkPaint textPaint = calculateTextPaint(paint);
472 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000473
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000474 // Make sure we have a glyph id encoding.
475 SkAutoFree glyphStorage;
476 uint16_t* glyphIDs;
477 size_t numGlyphs;
478 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
479 numGlyphs = paint.textToGlyphs(text, len, NULL);
480 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
481 SK_MALLOC_TEMP | SK_MALLOC_THROW);
482 glyphStorage.set(glyphIDs);
483 paint.textToGlyphs(text, len, glyphIDs);
484 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
485 } else {
486 SkASSERT((len & 1) == 0);
487 numGlyphs = len / 2;
488 glyphIDs = (uint16_t*)text;
489 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000490
491 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000492 fContent.writeText("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000493 updateFont(textPaint, glyphIDs[0]);
494 for (size_t i = 0; i < numGlyphs; i++) {
495 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000496 uint16_t encodedValue = glyphIDs[i];
497 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000498 updateFont(textPaint, glyphIDs[i]);
499 i--;
500 continue;
501 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000502 SkScalar x = pos[i * scalarsPerPos];
503 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000504 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000505 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000506 SkString encodedString =
507 SkPDFString::formatString(&encodedValue, 1,
508 font->multiByteGlyphs());
509 fContent.writeText(encodedString.c_str());
510 fContent.writeText(" Tj\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000511 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000512 fContent.writeText("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000513}
514
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000515void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000516 const SkPath& path, const SkMatrix* matrix,
517 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000518 if (d.fClip->isEmpty()) {
519 return;
520 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000521 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000522}
523
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000524void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000525 int vertexCount, const SkPoint verts[],
526 const SkPoint texs[], const SkColor colors[],
527 SkXfermode* xmode, const uint16_t indices[],
528 int indexCount, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000529 if (d.fClip->isEmpty()) {
530 return;
531 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000532 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000533}
534
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000535void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
536 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000537 if (d.fClip->isEmpty()) {
538 return;
539 }
540
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000541 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
542 // If we somehow get a raster device, do what our parent would do.
543 SkDevice::drawDevice(d, device, x, y, paint);
544 return;
545 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000546 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000547 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
548
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000549 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000550 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000551 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000552 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000553
554 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000555 fXObjectResources.push(xobject); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000556 fContent.writeText("/X");
557 fContent.writeDecAsText(fXObjectResources.count() - 1);
558 fContent.writeText(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000559 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000560}
561
562const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
563 if (fResourceDict.get() == NULL) {
564 fResourceDict = new SkPDFDict;
565 fResourceDict->unref(); // SkRefPtr and new both took a reference.
566
567 if (fGraphicStateResources.count()) {
568 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
569 extGState->unref(); // SkRefPtr and new both took a reference.
570 for (int i = 0; i < fGraphicStateResources.count(); i++) {
571 SkString nameString("G");
572 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000573 extGState->insert(
574 nameString.c_str(),
575 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000576 }
577 fResourceDict->insert("ExtGState", extGState.get());
578 }
579
580 if (fXObjectResources.count()) {
581 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
582 xObjects->unref(); // SkRefPtr and new both took a reference.
583 for (int i = 0; i < fXObjectResources.count(); i++) {
584 SkString nameString("X");
585 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000586 xObjects->insert(
587 nameString.c_str(),
588 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000589 }
590 fResourceDict->insert("XObject", xObjects.get());
591 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000592
593 if (fFontResources.count()) {
594 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
595 fonts->unref(); // SkRefPtr and new both took a reference.
596 for (int i = 0; i < fFontResources.count(); i++) {
597 SkString nameString("F");
598 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000599 fonts->insert(nameString.c_str(),
600 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000601 }
602 fResourceDict->insert("Font", fonts.get());
603 }
604
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000605 if (fShaderResources.count()) {
606 SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
607 patterns->unref(); // SkRefPtr and new both took a reference.
608 for (int i = 0; i < fShaderResources.count(); i++) {
609 SkString nameString("P");
610 nameString.appendS32(i);
611 patterns->insert(nameString.c_str(),
612 new SkPDFObjRef(fShaderResources[i]))->unref();
613 }
614 fResourceDict->insert("Pattern", patterns.get());
615 }
616
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000617 // For compatibility, add all proc sets (only used for output to PS
618 // devices).
619 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
620 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
621 procSets->unref(); // SkRefPtr and new both took a reference.
622 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000623 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
624 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000625 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000626 }
627 return fResourceDict;
628}
629
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000630void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
631 resourceList->setReserve(resourceList->count() +
632 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000633 fXObjectResources.count() +
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000634 fFontResources.count() +
635 fShaderResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000636 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000637 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000638 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000639 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000640 }
641 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000642 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000643 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000644 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000645 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000646 for (int i = 0; i < fFontResources.count(); i++) {
647 resourceList->push(fFontResources[i]);
648 fFontResources[i]->ref();
649 fFontResources[i]->getResources(resourceList);
650 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000651 for (int i = 0; i < fShaderResources.count(); i++) {
652 resourceList->push(fShaderResources[i]);
653 fShaderResources[i]->ref();
654 fShaderResources[i]->getResources(resourceList);
655 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000656}
657
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000658SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000659 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
660 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000661
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000662 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
663 mediaBox->unref(); // SkRefPtr and new both took a reference.
664 mediaBox->reserve(4);
665 mediaBox->append(zero.get());
666 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000667 mediaBox->append(new SkPDFInt(fWidth))->unref();
668 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000669 return mediaBox;
670}
671
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000672SkStream* SkPDFDevice::content() const {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000673 size_t offset = fContent.getOffset();
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000674 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000675 fContent.copyTo(data);
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000676 for (int i = 0; i < fGraphicStackIndex; i++) {
677 data[offset++] = 'Q';
678 data[offset++] = '\n';
679 }
680 SkMemoryStream* result = new SkMemoryStream;
681 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000682 return result;
683}
684
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000685void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
686 SkASSERT(paint.getPathEffect() == NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000687
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000688 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
689 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000690
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000691 SkPaint newPaint = paint;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000692
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000693 // PDF treats a shader as a color, so we only set one or the other.
694 SkRefPtr<SkPDFShader> pdfShader;
695 const SkShader* shader = newPaint.getShader();
696 if (shader) {
697 // PDF positions patterns relative to the initial transform, so
698 // we need to apply the current transform to the shader parameters.
699 SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000700 transform.postConcat(fInitialTransform);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000701
702 // PDF doesn't support kClamp_TileMode, so we simulate it by making
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000703 // a pattern the size of the drawing surface.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000704 SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
705 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
706 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
707
708 // A color shader is treated as an invalid shader so we don't have
709 // to set a shader just for a color.
710 if (pdfShader.get() == NULL) {
711 newPaint.setColor(0);
712
713 // Check for a color shader.
714 SkShader::GradientInfo gradientInfo;
715 SkColor gradientColor;
716 gradientInfo.fColors = &gradientColor;
717 gradientInfo.fColorOffsets = NULL;
718 gradientInfo.fColorCount = 1;
719 if (shader->asAGradient(&gradientInfo) ==
720 SkShader::kColor_GradientType) {
721 newPaint.setColor(gradientColor);
722 }
723 }
724 }
725
726 if (pdfShader) {
727 // pdfShader has been canonicalized so we can directly compare
728 // pointers.
729 if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
730 int resourceIndex = fShaderResources.find(pdfShader.get());
731 if (resourceIndex < 0) {
732 resourceIndex = fShaderResources.count();
733 fShaderResources.push(pdfShader.get());
734 pdfShader->ref();
735 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000736 fContent.writeText("/Pattern CS /Pattern cs /P");
737 fContent.writeDecAsText(resourceIndex);
738 fContent.writeText(" SCN /P");
739 fContent.writeDecAsText(resourceIndex);
740 fContent.writeText(" scn\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000741 fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
742 }
743 } else {
744 SkColor newColor = newPaint.getColor();
745 newColor = SkColorSetA(newColor, 0xFF);
746 if (fGraphicStack[fGraphicStackIndex].fShader ||
747 fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000748 emitPDFColor(newColor, &fContent);
749 fContent.writeText("RG ");
750 emitPDFColor(newColor, &fContent);
751 fContent.writeText("rg\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000752 fGraphicStack[fGraphicStackIndex].fColor = newColor;
753 fGraphicStack[fGraphicStackIndex].fShader = NULL;
754 }
755 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000756
757 SkRefPtr<SkPDFGraphicState> newGraphicState =
758 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
759 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
760 // newGraphicState has been canonicalized so we can directly compare
761 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000762 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000763 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000764 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
765 if (resourceIndex < 0) {
766 resourceIndex = fGraphicStateResources.count();
767 fGraphicStateResources.push(newGraphicState.get());
768 newGraphicState->ref();
769 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000770 fContent.writeText("/G");
771 fContent.writeDecAsText(resourceIndex);
772 fContent.writeText(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000773 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000774 }
775
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000776 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000777 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
778 newPaint.getTextScaleX()) {
779 SkScalar scale = newPaint.getTextScaleX();
780 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000781 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000782 fContent.writeText(" Tz\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000783 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
784 }
785
786 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
787 newPaint.getStyle()) {
788 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
789 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
790 enum_must_match_value);
791 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
792 enum_must_match_value);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000793 fContent.writeDecAsText(newPaint.getStyle());
794 fContent.writeText(" Tr\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000795 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
796 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000797 }
798}
799
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000800void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000801 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000802 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
803 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000804 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000805 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000806 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000807 fContent.writeText("/F");
808 fContent.writeDecAsText(fontIndex);
809 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000810 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000811 fContent.writeText(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000812 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
813 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
814 }
815}
816
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000817int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
818 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000819 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000820 int resourceIndex = fFontResources.find(newFont.get());
821 if (resourceIndex < 0) {
822 resourceIndex = fFontResources.count();
823 fFontResources.push(newFont.get());
824 newFont->ref();
825 }
826 return resourceIndex;
827}
828
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000829void SkPDFDevice::pushGS() {
830 SkASSERT(fGraphicStackIndex < 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000831 fContent.writeText("q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000832 fGraphicStackIndex++;
833 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
834}
835
836void SkPDFDevice::popGS() {
837 SkASSERT(fGraphicStackIndex > 0);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000838 fContent.writeText("Q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000839 fGraphicStackIndex--;
840}
841
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000842void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
843 // Flip the text about the x-axis to account for origin swap and include
844 // the passed parameters.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000845 fContent.writeText("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000846 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000847 fContent.writeText(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000848 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000849 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000850 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000851 fContent.writeText(" Tm\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000852}
853
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000854void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
855 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000856 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000857 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000858 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
859 if (srcRect && !subset.intersect(*srcRect))
860 return;
861
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000862 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
863 if (!image)
864 return;
865
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000866 SkMatrix scaled;
867 // Adjust for origin flip.
868 scaled.setScale(1, -1);
869 scaled.postTranslate(0, 1);
870 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000871 scaled.postScale(SkIntToScalar(subset.width()),
872 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000873 scaled.postConcat(matrix);
874 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000875 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000876
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000877 fXObjectResources.push(image); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000878 fContent.writeText("/X");
879 fContent.writeDecAsText(fXObjectResources.count() - 1);
880 fContent.writeText(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000881 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000882}
883
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000884SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
885 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
886 if (old == m)
887 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000888
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000889 if (old.getType() != SkMatrix::kIdentity_Mask) {
890 SkASSERT(fGraphicStackIndex > 0);
891 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
892 SkMatrix::kIdentity_Mask);
893 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
894 fGraphicStack[fGraphicStackIndex - 1].fClip);
895 popGS();
896 }
897 if (m.getType() == SkMatrix::kIdentity_Mask)
898 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000899
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000900 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
901 fGraphicStack[fGraphicStackIndex - 1].fClip)
902 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000903
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000904 SkPDFUtils::AppendTransform(m, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000905 fGraphicStack[fGraphicStackIndex].fTransform = m;
906
907 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000908}