blob: ef06c60bb4ee8676509a34c60346e1942ea84468 [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 }
ctguil@chromium.org15261292011-04-29 17:54:16 +0000123 SkISize size = SkISize::Make(width, height);
124 return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000125}
126
ctguil@chromium.org15261292011-04-29 17:54:16 +0000127static inline SkBitmap makeContentBitmap(const SkISize& contentSize,
128 const SkMatrix& initialTransform) {
129 // Compute the size of the drawing area.
130 SkVector drawingSize;
131 SkMatrix inverse;
132 drawingSize.set(contentSize.fWidth, contentSize.fHeight);
133 initialTransform.invert(&inverse);
134 inverse.mapVectors(&drawingSize, 1);
135 SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
136
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 SkBitmap bitmap;
ctguil@chromium.org15261292011-04-29 17:54:16 +0000138 bitmap.setConfig(SkBitmap::kNo_Config, size.fWidth, size.fHeight);
reed@android.comf2b98d62010-12-20 18:26:13 +0000139 return bitmap;
140}
141
ctguil@chromium.org15261292011-04-29 17:54:16 +0000142SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000143 const SkMatrix& initialTransform)
ctguil@chromium.org15261292011-04-29 17:54:16 +0000144 : SkDevice(NULL, makeContentBitmap(contentSize, initialTransform), false),
145 fPageSize(pageSize),
reed@google.com07700442010-12-20 19:46:07 +0000146 fGraphicStackIndex(0) {
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000147 // Skia generally uses the top left as the origin but PDF natively has the
148 // origin at the bottom left. This matrix corrects for that. When layering,
149 // we specify an inverse correction to cancel this out.
ctguil@chromium.org15261292011-04-29 17:54:16 +0000150 fInitialTransform.setTranslate(0, pageSize.fHeight);
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000151 fInitialTransform.preScale(1, -1);
152 fInitialTransform.preConcat(initialTransform);
153
154 this->init();
155}
156
157SkPDFDevice::~SkPDFDevice() {
158 this->cleanUp();
159}
160
161void SkPDFDevice::init() {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000162 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000163 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000164 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000165 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000166 fGraphicStack[0].fFont = NULL;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000167 fGraphicStack[0].fShader = NULL;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000168 fGraphicStack[0].fGraphicState = NULL;
ctguil@chromium.org15261292011-04-29 17:54:16 +0000169 fGraphicStack[0].fClip.setRect(0, 0, this->width(), this->height());
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000170 fGraphicStack[0].fTransform.reset();
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000171 fGraphicStackIndex = 0;
172 fResourceDict = NULL;
173 fContent.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000174
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000175 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
176 SkPDFUtils::AppendTransform(fInitialTransform, &fContent);
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000177 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000178}
179
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000180SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() {
181 return SkNEW(SkPDFDeviceFactory);
182}
183
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000184void SkPDFDevice::cleanUp() {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000185 fGraphicStateResources.unrefAll();
186 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000187 fFontResources.unrefAll();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000188 fShaderResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000189}
190
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000191void SkPDFDevice::clear(SkColor color) {
192 SkMatrix curTransform = fGraphicStack[fGraphicStackIndex].fTransform;
193 SkRegion curClip = fGraphicStack[fGraphicStackIndex].fClip;
194
195 this->cleanUp();
196 this->init();
197
198 SkPaint paint;
199 paint.setColor(color);
200 paint.setStyle(SkPaint::kFill_Style);
201 updateGSFromPaint(paint, false);
202 internalDrawPaint(paint);
203
204 SkClipStack clipStack;
205 setMatrixClip(curTransform, curClip, clipStack);
206}
207
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000208void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000209 const SkRegion& region,
210 const SkClipStack&) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000211 if (region.isEmpty()) {
212 return;
213 }
214
215 // TODO(vandebo) SkCanvas may not draw anything after doing this call, we
216 // should defer writing anything out to fContent until we actually get
217 // a draw command. In fact SkDraw contains the clip and transform, so
218 // this method should do nothing and we should have an update
219 // transform-and-clip method, like update paint.
220
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000221 // See the comment in the header file above GraphicStackEntry.
222 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
223 while (fGraphicStackIndex > 0)
224 popGS();
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000225
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000226 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
227 pushGS();
228
229 SkPath clipPath;
230 SkAssertResult(region.getBoundaryPath(&clipPath));
231
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000232 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000233 SkPath::FillType clipFill = clipPath.getFillType();
234 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
235 false);
236 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
237 false);
238 if (clipFill == SkPath::kEvenOdd_FillType)
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000239 fContent.writeText("W* n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000240 else
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000241 fContent.writeText("W n ");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000242
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000243 fGraphicStack[fGraphicStackIndex].fClip = region;
244 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000245 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000246 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000247}
248
249void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000250 if (d.fClip->isEmpty()) {
251 return;
252 }
253
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000254 SkPaint newPaint = paint;
255 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000256 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000257
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000258 internalDrawPaint(newPaint);
259}
260
261void SkPDFDevice::internalDrawPaint(const SkPaint& paint) {
262 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
263 SkIntToScalar(this->height()));
264 SkMatrix totalTransform = fInitialTransform;
265 totalTransform.preConcat(fGraphicStack[fGraphicStackIndex].fTransform);
266 SkMatrix inverse;
267 inverse.reset();
268 totalTransform.invert(&inverse);
269 inverse.mapRect(&bbox);
270
271 internalDrawRect(bbox, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000272}
273
274void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
275 size_t count, const SkPoint* points,
276 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000277 if (count == 0 || d.fClip->isEmpty()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000278 return;
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000279 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000280
281 switch (mode) {
282 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000283 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000284 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
285 for (size_t i = 1; i < count; i++) {
286 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
287 }
288 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000289 break;
290 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000291 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000292 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000293 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
294 &fContent);
295 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
296 points[i * 2 + 1].fY, &fContent);
297 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000298 }
299 break;
300 case SkCanvas::kPoints_PointMode:
301 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000302 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000303 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000304 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
305 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000306 }
307 } else {
308 // PDF won't draw a single point with square/butt caps because
309 // the orientation is ambiguous. Draw a rectangle instead.
310 SkPaint newPaint = paint;
311 newPaint.setStyle(SkPaint::kFill_Style);
312 SkScalar strokeWidth = paint.getStrokeWidth();
313 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
314 for (size_t i = 0; i < count; i++) {
315 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
316 0, 0);
317 r.inset(-halfStroke, -halfStroke);
318 drawRect(d, r, newPaint);
319 }
320 }
321 break;
322 default:
323 SkASSERT(false);
324 }
325}
326
327void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
328 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000329 if (d.fClip->isEmpty()) {
330 return;
331 }
332
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000333 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000334 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000335 SkPath path;
336 path.addRect(r);
337 paint.getFillPath(path, &path);
338
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000339 SkPaint noEffectPaint(paint);
340 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000341 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000342 return;
343 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000344 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000345
vandebo@chromium.org77bcaa32011-04-15 20:57:37 +0000346 internalDrawRect(r, paint);
347}
348
349void SkPDFDevice::internalDrawRect(const SkRect& r, const SkPaint& paint) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000350 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
351 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000352 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
353 &fContent);
354 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
355 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000356}
357
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000358void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000359 const SkPaint& paint, const SkMatrix* prePathMatrix,
360 bool pathIsMutable) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000361 if (d.fClip->isEmpty()) {
362 return;
363 }
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000364 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000365
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000366 if (paint.getPathEffect()) {
367 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000368 SkPath noEffectPath;
369 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000370
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000371 SkPaint noEffectPaint(paint);
372 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000373 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000374 return;
375 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000376 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000377
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000378 SkPDFUtils::EmitPath(path, &fContent);
379 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000380}
381
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000382void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000383 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000384 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000385 if (d.fClip->isEmpty()) {
386 return;
387 }
388
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000389 SkMatrix transform = matrix;
390 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000391 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000392}
393
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000394void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000395 int x, int y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000396 if (d.fClip->isEmpty()) {
397 return;
398 }
399
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000400 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000401 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000402 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000403}
404
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000405void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000406 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000407 if (d.fClip->isEmpty()) {
408 return;
409 }
410
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000411 SkPaint textPaint = calculateTextPaint(paint);
412 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000413
vandebo@chromium.org01294102011-02-28 19:52:18 +0000414 // We want the text in glyph id encoding and a writable buffer, so we end
415 // up making a copy either way.
416 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
417 uint16_t* glyphIDs =
418 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
419 SK_MALLOC_TEMP | SK_MALLOC_THROW);
420 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000421 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000422 paint.textToGlyphs(text, len, glyphIDs);
423 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
424 } else {
425 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000426 SkASSERT(len / 2 == numGlyphs);
427 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000428 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000429
430 SkScalar width;
431 SkScalar* widthPtr = NULL;
432 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
433 widthPtr = &width;
434
435 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000436 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000437 fContent.writeText("BT\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000438 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000439 size_t consumedGlyphCount = 0;
440 while (numGlyphs > consumedGlyphCount) {
441 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
442 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000443 size_t availableGlyphs =
444 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
445 numGlyphs - consumedGlyphCount);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000446 SkString encodedString =
447 SkPDFString::formatString(glyphIDs + consumedGlyphCount,
448 availableGlyphs, font->multiByteGlyphs());
449 fContent.writeText(encodedString.c_str());
vandebo@chromium.org01294102011-02-28 19:52:18 +0000450 consumedGlyphCount += availableGlyphs;
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000451 fContent.writeText(" Tj\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000452 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000453 fContent.writeText("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000454
455 // Draw underline and/or strikethrough if the paint has them.
456 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
457 // because the raster versions don't. Use paint instead of textPaint
458 // because we may have changed strokeWidth to do fakeBold text.
459 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
460 SkScalar textSize = paint.getTextSize();
461 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
462
463 if (paint.isUnderlineText()) {
464 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
465 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
466 drawRect(d, r, paint);
467 }
468 if (paint.isStrikeThruText()) {
469 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
470 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
471 drawRect(d, r, paint);
472 }
473 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000474}
475
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000476void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000477 const SkScalar pos[], SkScalar constY,
478 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000479 if (d.fClip->isEmpty()) {
480 return;
481 }
482
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000483 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
484 SkPaint textPaint = calculateTextPaint(paint);
485 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000486
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000487 // Make sure we have a glyph id encoding.
488 SkAutoFree glyphStorage;
489 uint16_t* glyphIDs;
490 size_t numGlyphs;
491 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
492 numGlyphs = paint.textToGlyphs(text, len, NULL);
493 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
494 SK_MALLOC_TEMP | SK_MALLOC_THROW);
495 glyphStorage.set(glyphIDs);
496 paint.textToGlyphs(text, len, glyphIDs);
497 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
498 } else {
499 SkASSERT((len & 1) == 0);
500 numGlyphs = len / 2;
501 glyphIDs = (uint16_t*)text;
502 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000503
504 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000505 fContent.writeText("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000506 updateFont(textPaint, glyphIDs[0]);
507 for (size_t i = 0; i < numGlyphs; i++) {
508 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000509 uint16_t encodedValue = glyphIDs[i];
510 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000511 updateFont(textPaint, glyphIDs[i]);
512 i--;
513 continue;
514 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000515 SkScalar x = pos[i * scalarsPerPos];
516 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000517 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000518 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000519 SkString encodedString =
520 SkPDFString::formatString(&encodedValue, 1,
521 font->multiByteGlyphs());
522 fContent.writeText(encodedString.c_str());
523 fContent.writeText(" Tj\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000524 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000525 fContent.writeText("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000526}
527
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000528void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000529 const SkPath& path, const SkMatrix* matrix,
530 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000531 if (d.fClip->isEmpty()) {
532 return;
533 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000534 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000535}
536
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000537void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000538 int vertexCount, const SkPoint verts[],
539 const SkPoint texs[], const SkColor colors[],
540 SkXfermode* xmode, const uint16_t indices[],
541 int indexCount, const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000542 if (d.fClip->isEmpty()) {
543 return;
544 }
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000545 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000546}
547
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000548void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
549 const SkPaint& paint) {
vandebo@chromium.orgfb0b0ed2011-04-15 20:01:17 +0000550 if (d.fClip->isEmpty()) {
551 return;
552 }
553
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000554 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
555 // If we somehow get a raster device, do what our parent would do.
556 SkDevice::drawDevice(d, device, x, y, paint);
557 return;
558 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000559 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000560 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
561
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000562 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000563 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000564 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000565 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000566
567 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000568 fXObjectResources.push(xobject); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000569 fContent.writeText("/X");
570 fContent.writeDecAsText(fXObjectResources.count() - 1);
571 fContent.writeText(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000572 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000573}
574
575const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
576 if (fResourceDict.get() == NULL) {
577 fResourceDict = new SkPDFDict;
578 fResourceDict->unref(); // SkRefPtr and new both took a reference.
579
580 if (fGraphicStateResources.count()) {
581 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
582 extGState->unref(); // SkRefPtr and new both took a reference.
583 for (int i = 0; i < fGraphicStateResources.count(); i++) {
584 SkString nameString("G");
585 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000586 extGState->insert(
587 nameString.c_str(),
588 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000589 }
590 fResourceDict->insert("ExtGState", extGState.get());
591 }
592
593 if (fXObjectResources.count()) {
594 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
595 xObjects->unref(); // SkRefPtr and new both took a reference.
596 for (int i = 0; i < fXObjectResources.count(); i++) {
597 SkString nameString("X");
598 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000599 xObjects->insert(
600 nameString.c_str(),
601 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000602 }
603 fResourceDict->insert("XObject", xObjects.get());
604 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000605
606 if (fFontResources.count()) {
607 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
608 fonts->unref(); // SkRefPtr and new both took a reference.
609 for (int i = 0; i < fFontResources.count(); i++) {
610 SkString nameString("F");
611 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000612 fonts->insert(nameString.c_str(),
613 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000614 }
615 fResourceDict->insert("Font", fonts.get());
616 }
617
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000618 if (fShaderResources.count()) {
619 SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
620 patterns->unref(); // SkRefPtr and new both took a reference.
621 for (int i = 0; i < fShaderResources.count(); i++) {
622 SkString nameString("P");
623 nameString.appendS32(i);
624 patterns->insert(nameString.c_str(),
625 new SkPDFObjRef(fShaderResources[i]))->unref();
626 }
627 fResourceDict->insert("Pattern", patterns.get());
628 }
629
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000630 // For compatibility, add all proc sets (only used for output to PS
631 // devices).
632 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
633 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
634 procSets->unref(); // SkRefPtr and new both took a reference.
635 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000636 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
637 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000638 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000639 }
640 return fResourceDict;
641}
642
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000643void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
644 resourceList->setReserve(resourceList->count() +
645 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000646 fXObjectResources.count() +
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000647 fFontResources.count() +
648 fShaderResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000649 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000650 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000651 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000652 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000653 }
654 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000655 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000656 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000657 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000658 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000659 for (int i = 0; i < fFontResources.count(); i++) {
660 resourceList->push(fFontResources[i]);
661 fFontResources[i]->ref();
662 fFontResources[i]->getResources(resourceList);
663 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000664 for (int i = 0; i < fShaderResources.count(); i++) {
665 resourceList->push(fShaderResources[i]);
666 fShaderResources[i]->ref();
667 fShaderResources[i]->getResources(resourceList);
668 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000669}
670
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000671SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000672 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
673 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000674
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000675 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
676 mediaBox->unref(); // SkRefPtr and new both took a reference.
677 mediaBox->reserve(4);
678 mediaBox->append(zero.get());
679 mediaBox->append(zero.get());
ctguil@chromium.org15261292011-04-29 17:54:16 +0000680 mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref();
681 mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000682 return mediaBox;
683}
684
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000685SkStream* SkPDFDevice::content() const {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000686 size_t offset = fContent.getOffset();
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000687 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000688 fContent.copyTo(data);
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000689 for (int i = 0; i < fGraphicStackIndex; i++) {
690 data[offset++] = 'Q';
691 data[offset++] = '\n';
692 }
693 SkMemoryStream* result = new SkMemoryStream;
694 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000695 return result;
696}
697
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000698void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
699 SkASSERT(paint.getPathEffect() == NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000700
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000701 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
702 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000703
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000704 SkPaint newPaint = paint;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000705
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000706 // PDF treats a shader as a color, so we only set one or the other.
707 SkRefPtr<SkPDFShader> pdfShader;
708 const SkShader* shader = newPaint.getShader();
709 if (shader) {
710 // PDF positions patterns relative to the initial transform, so
711 // we need to apply the current transform to the shader parameters.
712 SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000713 transform.postConcat(fInitialTransform);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000714
715 // PDF doesn't support kClamp_TileMode, so we simulate it by making
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000716 // a pattern the size of the drawing surface.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000717 SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
718 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
719 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
720
721 // A color shader is treated as an invalid shader so we don't have
722 // to set a shader just for a color.
723 if (pdfShader.get() == NULL) {
724 newPaint.setColor(0);
725
726 // Check for a color shader.
727 SkShader::GradientInfo gradientInfo;
728 SkColor gradientColor;
729 gradientInfo.fColors = &gradientColor;
730 gradientInfo.fColorOffsets = NULL;
731 gradientInfo.fColorCount = 1;
732 if (shader->asAGradient(&gradientInfo) ==
733 SkShader::kColor_GradientType) {
734 newPaint.setColor(gradientColor);
735 }
736 }
737 }
738
739 if (pdfShader) {
740 // pdfShader has been canonicalized so we can directly compare
741 // pointers.
742 if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
743 int resourceIndex = fShaderResources.find(pdfShader.get());
744 if (resourceIndex < 0) {
745 resourceIndex = fShaderResources.count();
746 fShaderResources.push(pdfShader.get());
747 pdfShader->ref();
748 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000749 fContent.writeText("/Pattern CS /Pattern cs /P");
750 fContent.writeDecAsText(resourceIndex);
751 fContent.writeText(" SCN /P");
752 fContent.writeDecAsText(resourceIndex);
753 fContent.writeText(" scn\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000754 fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
755 }
756 } else {
757 SkColor newColor = newPaint.getColor();
758 newColor = SkColorSetA(newColor, 0xFF);
759 if (fGraphicStack[fGraphicStackIndex].fShader ||
760 fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000761 emitPDFColor(newColor, &fContent);
762 fContent.writeText("RG ");
763 emitPDFColor(newColor, &fContent);
764 fContent.writeText("rg\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000765 fGraphicStack[fGraphicStackIndex].fColor = newColor;
766 fGraphicStack[fGraphicStackIndex].fShader = NULL;
767 }
768 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000769
770 SkRefPtr<SkPDFGraphicState> newGraphicState =
771 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
772 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
773 // newGraphicState has been canonicalized so we can directly compare
774 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000775 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000776 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000777 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
778 if (resourceIndex < 0) {
779 resourceIndex = fGraphicStateResources.count();
780 fGraphicStateResources.push(newGraphicState.get());
781 newGraphicState->ref();
782 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000783 fContent.writeText("/G");
784 fContent.writeDecAsText(resourceIndex);
785 fContent.writeText(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000786 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000787 }
788
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000789 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000790 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
791 newPaint.getTextScaleX()) {
792 SkScalar scale = newPaint.getTextScaleX();
793 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000794 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000795 fContent.writeText(" Tz\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000796 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
797 }
798
799 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
800 newPaint.getStyle()) {
801 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
802 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
803 enum_must_match_value);
804 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
805 enum_must_match_value);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000806 fContent.writeDecAsText(newPaint.getStyle());
807 fContent.writeText(" Tr\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000808 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
809 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000810 }
811}
812
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000813void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000814 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000815 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
816 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000817 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000818 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000819 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000820 fContent.writeText("/F");
821 fContent.writeDecAsText(fontIndex);
822 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000823 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000824 fContent.writeText(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000825 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
826 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
827 }
828}
829
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000830int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
831 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000832 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000833 int resourceIndex = fFontResources.find(newFont.get());
834 if (resourceIndex < 0) {
835 resourceIndex = fFontResources.count();
836 fFontResources.push(newFont.get());
837 newFont->ref();
838 }
839 return resourceIndex;
840}
841
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000842void SkPDFDevice::pushGS() {
843 SkASSERT(fGraphicStackIndex < 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000844 fContent.writeText("q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000845 fGraphicStackIndex++;
846 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
847}
848
849void SkPDFDevice::popGS() {
850 SkASSERT(fGraphicStackIndex > 0);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000851 fContent.writeText("Q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000852 fGraphicStackIndex--;
853}
854
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000855void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
856 // Flip the text about the x-axis to account for origin swap and include
857 // the passed parameters.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000858 fContent.writeText("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000859 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000860 fContent.writeText(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000861 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000862 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000863 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000864 fContent.writeText(" Tm\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000865}
866
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000867void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
868 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000869 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000870 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000871 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
872 if (srcRect && !subset.intersect(*srcRect))
873 return;
874
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000875 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
876 if (!image)
877 return;
878
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000879 SkMatrix scaled;
880 // Adjust for origin flip.
881 scaled.setScale(1, -1);
882 scaled.postTranslate(0, 1);
883 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000884 scaled.postScale(SkIntToScalar(subset.width()),
885 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000886 scaled.postConcat(matrix);
887 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000888 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000889
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000890 fXObjectResources.push(image); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000891 fContent.writeText("/X");
892 fContent.writeDecAsText(fXObjectResources.count() - 1);
893 fContent.writeText(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000894 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000895}
896
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000897SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
898 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
899 if (old == m)
900 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000901
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000902 if (old.getType() != SkMatrix::kIdentity_Mask) {
903 SkASSERT(fGraphicStackIndex > 0);
904 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
905 SkMatrix::kIdentity_Mask);
906 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
907 fGraphicStack[fGraphicStackIndex - 1].fClip);
908 popGS();
909 }
910 if (m.getType() == SkMatrix::kIdentity_Mask)
911 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000912
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000913 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
914 fGraphicStack[fGraphicStackIndex - 1].fClip)
915 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000916
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000917 SkPDFUtils::AppendTransform(m, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000918 fGraphicStack[fGraphicStackIndex].fTransform = m;
919
920 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000921}