blob: 4a255112fcf78551671574464d491e147621d240 [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.org28be72b2010-11-11 21:37:00 +000020#include "SkGlyphCache.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000021#include "SkPaint.h"
vandebo@chromium.orga5180862010-10-26 19:48:49 +000022#include "SkPath.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000023#include "SkPDFImage.h"
24#include "SkPDFGraphicState.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000025#include "SkPDFFont.h"
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +000026#include "SkPDFFormXObject.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000027#include "SkPDFTypes.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"
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +000030#include "SkPDFUtils.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000031#include "SkRect.h"
32#include "SkString.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000033#include "SkTextFormatParams.h"
34#include "SkTypeface.h"
35#include "SkTypes.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000036
37// Utility functions
38
39namespace {
40
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000041void emitPDFColor(SkColor color, SkWStream* result) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000042 SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
43 SkScalar colorMax = SkIntToScalar(0xFF);
vandebo@chromium.org094316b2011-03-04 03:15:13 +000044 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000045 SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
46 result->writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000047 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000048 SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
49 result->writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +000050 SkPDFScalar::Append(
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +000051 SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
52 result->writeText(" ");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000053}
54
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000055SkPaint calculateTextPaint(const SkPaint& paint) {
56 SkPaint result = paint;
57 if (result.isFakeBoldText()) {
58 SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
59 kStdFakeBoldInterpKeys,
60 kStdFakeBoldInterpValues,
61 kStdFakeBoldInterpLength);
62 SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
63 if (result.getStyle() == SkPaint::kFill_Style)
64 result.setStyle(SkPaint::kStrokeAndFill_Style);
65 else
66 width += result.getStrokeWidth();
67 result.setStrokeWidth(width);
68 }
69 return result;
70}
71
72// Stolen from measure_text in SkDraw.cpp and then tweaked.
73void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
74 const uint16_t* glyphs, size_t len, SkScalar* x, SkScalar* y,
75 SkScalar* width) {
76 if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
77 return;
78
79 SkMatrix ident;
80 ident.reset();
81 SkAutoGlyphCache autoCache(paint, &ident);
82 SkGlyphCache* cache = autoCache.getCache();
83
84 const char* start = (char*)glyphs;
85 const char* stop = (char*)(glyphs + len);
86 SkFixed xAdv = 0, yAdv = 0;
87
88 // TODO(vandebo) This probably needs to take kerning into account.
89 while (start < stop) {
90 const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
91 xAdv += glyph.fAdvanceX;
92 yAdv += glyph.fAdvanceY;
93 };
94 if (width)
95 *width = SkFixedToScalar(xAdv);
96 if (paint.getTextAlign() == SkPaint::kLeft_Align)
97 return;
98
99 SkScalar xAdj = SkFixedToScalar(xAdv);
100 SkScalar yAdj = SkFixedToScalar(yAdv);
101 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
102 xAdj = SkScalarHalf(xAdj);
103 yAdj = SkScalarHalf(yAdj);
104 }
105 *x = *x - xAdj;
106 *y = *y - yAdj;
107}
108
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000109} // namespace
110
111////////////////////////////////////////////////////////////////////////////////
112
reed@android.comf2b98d62010-12-20 18:26:13 +0000113SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
114 int width, int height, bool isOpaque,
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000115 bool isForLayer) {
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000116 SkMatrix initialTransform;
117 initialTransform.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000118 if (isForLayer) {
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000119 initialTransform.setTranslate(0, height);
120 initialTransform.preScale(1, -1);
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000121 }
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000122 return SkNEW_ARGS(SkPDFDevice, (width, height, initialTransform));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000123}
124
reed@android.comf2b98d62010-12-20 18:26:13 +0000125static inline SkBitmap makeABitmap(int width, int height) {
126 SkBitmap bitmap;
127 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
128 return bitmap;
129}
130
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000131SkPDFDevice::SkPDFDevice(int width, int height,
132 const SkMatrix& initialTransform)
reed@google.com07700442010-12-20 19:46:07 +0000133 : SkDevice(NULL, makeABitmap(width, height), false),
vandebo@chromium.org48543272011-02-08 19:28:07 +0000134 fWidth(width),
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000135 fHeight(height),
reed@google.com07700442010-12-20 19:46:07 +0000136 fGraphicStackIndex(0) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000137 fGraphicStack[0].fColor = SK_ColorBLACK;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000138 fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000139 fGraphicStack[0].fTextScaleX = SK_Scalar1;
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000140 fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000141 fGraphicStack[0].fFont = NULL;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000142 fGraphicStack[0].fShader = NULL;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000143 fGraphicStack[0].fGraphicState = NULL;
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000144 fGraphicStack[0].fClip.setRect(0,0, width, height);
145 fGraphicStack[0].fTransform.reset();
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000146
vandebo@chromium.org75f97e42011-04-11 23:24:18 +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.
150 fInitialTransform.setTranslate(0, height);
151 fInitialTransform.preScale(1, -1);
152 fInitialTransform.preConcat(initialTransform);
153 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
154 SkPDFUtils::AppendTransform(fInitialTransform, &fContent);
vandebo@chromium.orgf60a0012011-02-24 23:14:04 +0000155 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000156}
157
158SkPDFDevice::~SkPDFDevice() {
159 fGraphicStateResources.unrefAll();
160 fXObjectResources.unrefAll();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000161 fFontResources.unrefAll();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000162 fShaderResources.unrefAll();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000163}
164
165void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
reed@google.com46799cd2011-02-22 20:56:26 +0000166 const SkRegion& region,
167 const SkClipStack&) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000168 // See the comment in the header file above GraphicStackEntry.
169 if (region != fGraphicStack[fGraphicStackIndex].fClip) {
170 while (fGraphicStackIndex > 0)
171 popGS();
172 pushGS();
173
174 SkPath clipPath;
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000175 if (region.getBoundaryPath(&clipPath)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000176 SkPDFUtils::EmitPath(clipPath, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000177
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000178 SkPath::FillType clipFill = clipPath.getFillType();
179 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
180 false);
181 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
182 false);
183 if (clipFill == SkPath::kEvenOdd_FillType)
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000184 fContent.writeText("W* n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000185 else
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000186 fContent.writeText("W n ");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000187 }
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000188
189 fGraphicStack[fGraphicStackIndex].fClip = region;
190 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000191 setTransform(matrix);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000192}
193
194void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000195 SkMatrix identityTransform;
196 identityTransform.reset();
197 SkMatrix curTransform = setTransform(identityTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000198
199 SkPaint newPaint = paint;
200 newPaint.setStyle(SkPaint::kFill_Style);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000201 updateGSFromPaint(newPaint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000202
reed@google.coma6d59f62011-03-07 21:29:21 +0000203 SkRect all = SkRect::MakeWH(SkIntToScalar(this->width()),
204 SkIntToScalar(this->height()));
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000205 drawRect(d, all, newPaint);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000206 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000207}
208
209void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
210 size_t count, const SkPoint* points,
211 const SkPaint& paint) {
212 if (count == 0)
213 return;
214
215 switch (mode) {
216 case SkCanvas::kPolygon_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000217 updateGSFromPaint(paint, false);
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000218 SkPDFUtils::MoveTo(points[0].fX, points[0].fY, &fContent);
219 for (size_t i = 1; i < count; i++) {
220 SkPDFUtils::AppendLine(points[i].fX, points[i].fY, &fContent);
221 }
222 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000223 break;
224 case SkCanvas::kLines_PointMode:
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000225 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000226 for (size_t i = 0; i < count/2; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000227 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
228 &fContent);
229 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
230 points[i * 2 + 1].fY, &fContent);
231 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000232 }
233 break;
234 case SkCanvas::kPoints_PointMode:
235 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000236 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000237 for (size_t i = 0; i < count; i++) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000238 SkPDFUtils::MoveTo(points[i].fX, points[i].fY, &fContent);
239 SkPDFUtils::StrokePath(&fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000240 }
241 } else {
242 // PDF won't draw a single point with square/butt caps because
243 // the orientation is ambiguous. Draw a rectangle instead.
244 SkPaint newPaint = paint;
245 newPaint.setStyle(SkPaint::kFill_Style);
246 SkScalar strokeWidth = paint.getStrokeWidth();
247 SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
248 for (size_t i = 0; i < count; i++) {
249 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
250 0, 0);
251 r.inset(-halfStroke, -halfStroke);
252 drawRect(d, r, newPaint);
253 }
254 }
255 break;
256 default:
257 SkASSERT(false);
258 }
259}
260
261void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
262 const SkPaint& paint) {
263 if (paint.getPathEffect()) {
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000264 // Create a path for the rectangle and apply the path effect to it.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000265 SkPath path;
266 path.addRect(r);
267 paint.getFillPath(path, &path);
268
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000269 SkPaint noEffectPaint(paint);
270 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000271 drawPath(d, path, noEffectPaint, NULL, true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000272 return;
273 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000274 updateGSFromPaint(paint, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000275
276 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
277 SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000278 SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(),
279 &fContent);
280 SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
281 &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000282}
283
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000284void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000285 const SkPaint& paint, const SkMatrix* prePathMatrix,
286 bool pathIsMutable) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000287 NOT_IMPLEMENTED(prePathMatrix != NULL, true);
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000288
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000289 if (paint.getPathEffect()) {
290 // Apply the path effect to path and draw it that way.
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000291 SkPath noEffectPath;
292 paint.getFillPath(path, &noEffectPath);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000293
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000294 SkPaint noEffectPaint(paint);
295 SkSafeUnref(noEffectPaint.setPathEffect(NULL));
vandebo@chromium.org02cc5aa2011-01-25 22:06:29 +0000296 drawPath(d, noEffectPath, noEffectPaint, NULL, true);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000297 return;
298 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000299 updateGSFromPaint(paint, false);
vandebo@chromium.org7d71f7f2010-10-26 19:51:44 +0000300
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000301 SkPDFUtils::EmitPath(path, &fContent);
302 SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(), &fContent);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000303}
304
305void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
reed@android.comf2b98d62010-12-20 18:26:13 +0000306 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000307 const SkMatrix& matrix, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000308 SkMatrix transform = matrix;
309 transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000310 internalDrawBitmap(transform, bitmap, srcRect, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000311}
312
313void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
314 int x, int y, const SkPaint& paint) {
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000315 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000316 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000317 internalDrawBitmap(matrix, bitmap, NULL, paint);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000318}
319
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000320void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000321 SkScalar x, SkScalar y, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000322 SkPaint textPaint = calculateTextPaint(paint);
323 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000324
vandebo@chromium.org01294102011-02-28 19:52:18 +0000325 // We want the text in glyph id encoding and a writable buffer, so we end
326 // up making a copy either way.
327 size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
328 uint16_t* glyphIDs =
329 (uint16_t*)sk_malloc_flags(numGlyphs * 2,
330 SK_MALLOC_TEMP | SK_MALLOC_THROW);
331 SkAutoFree autoFreeGlyphIDs(glyphIDs);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000332 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000333 paint.textToGlyphs(text, len, glyphIDs);
334 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
335 } else {
336 SkASSERT((len & 1) == 0);
vandebo@chromium.org01294102011-02-28 19:52:18 +0000337 SkASSERT(len / 2 == numGlyphs);
338 memcpy(glyphIDs, text, len);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000339 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000340
341 SkScalar width;
342 SkScalar* widthPtr = NULL;
343 if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
344 widthPtr = &width;
345
346 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000347 alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000348 fContent.writeText("BT\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000349 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000350 size_t consumedGlyphCount = 0;
351 while (numGlyphs > consumedGlyphCount) {
352 updateFont(textPaint, glyphIDs[consumedGlyphCount]);
353 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000354 size_t availableGlyphs =
355 font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
356 numGlyphs - consumedGlyphCount);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000357 SkString encodedString =
358 SkPDFString::formatString(glyphIDs + consumedGlyphCount,
359 availableGlyphs, font->multiByteGlyphs());
360 fContent.writeText(encodedString.c_str());
vandebo@chromium.org01294102011-02-28 19:52:18 +0000361 consumedGlyphCount += availableGlyphs;
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000362 fContent.writeText(" Tj\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000363 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000364 fContent.writeText("ET\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000365
366 // Draw underline and/or strikethrough if the paint has them.
367 // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
368 // because the raster versions don't. Use paint instead of textPaint
369 // because we may have changed strokeWidth to do fakeBold text.
370 if (paint.isUnderlineText() || paint.isStrikeThruText()) {
371 SkScalar textSize = paint.getTextSize();
372 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
373
374 if (paint.isUnderlineText()) {
375 SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
376 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
377 drawRect(d, r, paint);
378 }
379 if (paint.isStrikeThruText()) {
380 SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
381 SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
382 drawRect(d, r, paint);
383 }
384 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000385}
386
387void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
388 const SkScalar pos[], SkScalar constY,
389 int scalarsPerPos, const SkPaint& paint) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000390 SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
391 SkPaint textPaint = calculateTextPaint(paint);
392 updateGSFromPaint(textPaint, true);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000393
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000394 // Make sure we have a glyph id encoding.
395 SkAutoFree glyphStorage;
396 uint16_t* glyphIDs;
397 size_t numGlyphs;
398 if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
399 numGlyphs = paint.textToGlyphs(text, len, NULL);
400 glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
401 SK_MALLOC_TEMP | SK_MALLOC_THROW);
402 glyphStorage.set(glyphIDs);
403 paint.textToGlyphs(text, len, glyphIDs);
404 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
405 } else {
406 SkASSERT((len & 1) == 0);
407 numGlyphs = len / 2;
408 glyphIDs = (uint16_t*)text;
409 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000410
411 SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000412 fContent.writeText("BT\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000413 updateFont(textPaint, glyphIDs[0]);
414 for (size_t i = 0; i < numGlyphs; i++) {
415 SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
vandebo@chromium.org01294102011-02-28 19:52:18 +0000416 uint16_t encodedValue = glyphIDs[i];
417 if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000418 updateFont(textPaint, glyphIDs[i]);
419 i--;
420 continue;
421 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000422 SkScalar x = pos[i * scalarsPerPos];
423 SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000424 alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000425 setTextTransform(x, y, textPaint.getTextSkewX());
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000426 SkString encodedString =
427 SkPDFString::formatString(&encodedValue, 1,
428 font->multiByteGlyphs());
429 fContent.writeText(encodedString.c_str());
430 fContent.writeText(" Tj\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000431 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000432 fContent.writeText("ET\n");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000433}
434
435void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
436 const SkPath& path, const SkMatrix* matrix,
437 const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000438 NOT_IMPLEMENTED("drawTextOnPath", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000439}
440
441void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
442 int vertexCount, const SkPoint verts[],
443 const SkPoint texs[], const SkColor colors[],
444 SkXfermode* xmode, const uint16_t indices[],
445 int indexCount, const SkPaint& paint) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000446 NOT_IMPLEMENTED("drawVerticies", true);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000447}
448
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000449void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
450 const SkPaint& paint) {
451 if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
452 // If we somehow get a raster device, do what our parent would do.
453 SkDevice::drawDevice(d, device, x, y, paint);
454 return;
455 }
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000456 // Assume that a vector capable device means that it's a PDF Device.
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000457 SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
458
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000459 SkMatrix matrix;
reed@google.coma6d59f62011-03-07 21:29:21 +0000460 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000461 SkMatrix curTransform = setTransform(matrix);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000462 updateGSFromPaint(paint, false);
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000463
464 SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
vandebo@chromium.orgeb6c7592010-10-26 19:54:45 +0000465 fXObjectResources.push(xobject); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000466 fContent.writeText("/X");
467 fContent.writeDecAsText(fXObjectResources.count() - 1);
468 fContent.writeText(" Do\n");
vandebo@chromium.org1aef2ed2011-02-03 21:46:10 +0000469 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000470}
471
472const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
473 if (fResourceDict.get() == NULL) {
474 fResourceDict = new SkPDFDict;
475 fResourceDict->unref(); // SkRefPtr and new both took a reference.
476
477 if (fGraphicStateResources.count()) {
478 SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
479 extGState->unref(); // SkRefPtr and new both took a reference.
480 for (int i = 0; i < fGraphicStateResources.count(); i++) {
481 SkString nameString("G");
482 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000483 extGState->insert(
484 nameString.c_str(),
485 new SkPDFObjRef(fGraphicStateResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000486 }
487 fResourceDict->insert("ExtGState", extGState.get());
488 }
489
490 if (fXObjectResources.count()) {
491 SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
492 xObjects->unref(); // SkRefPtr and new both took a reference.
493 for (int i = 0; i < fXObjectResources.count(); i++) {
494 SkString nameString("X");
495 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000496 xObjects->insert(
497 nameString.c_str(),
498 new SkPDFObjRef(fXObjectResources[i]))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000499 }
500 fResourceDict->insert("XObject", xObjects.get());
501 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000502
503 if (fFontResources.count()) {
504 SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
505 fonts->unref(); // SkRefPtr and new both took a reference.
506 for (int i = 0; i < fFontResources.count(); i++) {
507 SkString nameString("F");
508 nameString.appendS32(i);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000509 fonts->insert(nameString.c_str(),
510 new SkPDFObjRef(fFontResources[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000511 }
512 fResourceDict->insert("Font", fonts.get());
513 }
514
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000515 if (fShaderResources.count()) {
516 SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
517 patterns->unref(); // SkRefPtr and new both took a reference.
518 for (int i = 0; i < fShaderResources.count(); i++) {
519 SkString nameString("P");
520 nameString.appendS32(i);
521 patterns->insert(nameString.c_str(),
522 new SkPDFObjRef(fShaderResources[i]))->unref();
523 }
524 fResourceDict->insert("Pattern", patterns.get());
525 }
526
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000527 // For compatibility, add all proc sets (only used for output to PS
528 // devices).
529 const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
530 SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
531 procSets->unref(); // SkRefPtr and new both took a reference.
532 procSets->reserve(SK_ARRAY_COUNT(procs));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000533 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
534 procSets->append(new SkPDFName(procs[i]))->unref();
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000535 fResourceDict->insert("ProcSet", procSets.get());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000536 }
537 return fResourceDict;
538}
539
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000540void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
541 resourceList->setReserve(resourceList->count() +
542 fGraphicStateResources.count() +
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000543 fXObjectResources.count() +
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000544 fFontResources.count() +
545 fShaderResources.count());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000546 for (int i = 0; i < fGraphicStateResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000547 resourceList->push(fGraphicStateResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000548 fGraphicStateResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000549 fGraphicStateResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000550 }
551 for (int i = 0; i < fXObjectResources.count(); i++) {
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000552 resourceList->push(fXObjectResources[i]);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000553 fXObjectResources[i]->ref();
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000554 fXObjectResources[i]->getResources(resourceList);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000555 }
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000556 for (int i = 0; i < fFontResources.count(); i++) {
557 resourceList->push(fFontResources[i]);
558 fFontResources[i]->ref();
559 fFontResources[i]->getResources(resourceList);
560 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000561 for (int i = 0; i < fShaderResources.count(); i++) {
562 resourceList->push(fShaderResources[i]);
563 fShaderResources[i]->ref();
564 fShaderResources[i]->getResources(resourceList);
565 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000566}
567
vandebo@chromium.orga5180862010-10-26 19:48:49 +0000568SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000569 SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
570 zero->unref(); // SkRefPtr and new both took a reference.
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000571
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000572 SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
573 mediaBox->unref(); // SkRefPtr and new both took a reference.
574 mediaBox->reserve(4);
575 mediaBox->append(zero.get());
576 mediaBox->append(zero.get());
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000577 mediaBox->append(new SkPDFInt(fWidth))->unref();
578 mediaBox->append(new SkPDFInt(fHeight))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000579 return mediaBox;
580}
581
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000582SkStream* SkPDFDevice::content() const {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000583 size_t offset = fContent.getOffset();
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000584 char* data = (char*)sk_malloc_throw(offset + fGraphicStackIndex * 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000585 fContent.copyTo(data);
vandebo@chromium.orgc2a9b7f2011-02-24 23:22:30 +0000586 for (int i = 0; i < fGraphicStackIndex; i++) {
587 data[offset++] = 'Q';
588 data[offset++] = '\n';
589 }
590 SkMemoryStream* result = new SkMemoryStream;
591 result->setMemoryOwned(data, offset);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000592 return result;
593}
594
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000595void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
596 SkASSERT(paint.getPathEffect() == NULL);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000597
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000598 NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
599 NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000600
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000601 SkPaint newPaint = paint;
vandebo@chromium.org48543272011-02-08 19:28:07 +0000602
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000603 // PDF treats a shader as a color, so we only set one or the other.
604 SkRefPtr<SkPDFShader> pdfShader;
605 const SkShader* shader = newPaint.getShader();
606 if (shader) {
607 // PDF positions patterns relative to the initial transform, so
608 // we need to apply the current transform to the shader parameters.
609 SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000610 transform.postConcat(fInitialTransform);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000611
612 // PDF doesn't support kClamp_TileMode, so we simulate it by making
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000613 // a pattern the size of the drawing surface.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000614 SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
615 pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
616 SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
617
618 // A color shader is treated as an invalid shader so we don't have
619 // to set a shader just for a color.
620 if (pdfShader.get() == NULL) {
621 newPaint.setColor(0);
622
623 // Check for a color shader.
624 SkShader::GradientInfo gradientInfo;
625 SkColor gradientColor;
626 gradientInfo.fColors = &gradientColor;
627 gradientInfo.fColorOffsets = NULL;
628 gradientInfo.fColorCount = 1;
629 if (shader->asAGradient(&gradientInfo) ==
630 SkShader::kColor_GradientType) {
631 newPaint.setColor(gradientColor);
632 }
633 }
634 }
635
636 if (pdfShader) {
637 // pdfShader has been canonicalized so we can directly compare
638 // pointers.
639 if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
640 int resourceIndex = fShaderResources.find(pdfShader.get());
641 if (resourceIndex < 0) {
642 resourceIndex = fShaderResources.count();
643 fShaderResources.push(pdfShader.get());
644 pdfShader->ref();
645 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000646 fContent.writeText("/Pattern CS /Pattern cs /P");
647 fContent.writeDecAsText(resourceIndex);
648 fContent.writeText(" SCN /P");
649 fContent.writeDecAsText(resourceIndex);
650 fContent.writeText(" scn\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000651 fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
652 }
653 } else {
654 SkColor newColor = newPaint.getColor();
655 newColor = SkColorSetA(newColor, 0xFF);
656 if (fGraphicStack[fGraphicStackIndex].fShader ||
657 fGraphicStack[fGraphicStackIndex].fColor != newColor) {
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000658 emitPDFColor(newColor, &fContent);
659 fContent.writeText("RG ");
660 emitPDFColor(newColor, &fContent);
661 fContent.writeText("rg\n");
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000662 fGraphicStack[fGraphicStackIndex].fColor = newColor;
663 fGraphicStack[fGraphicStackIndex].fShader = NULL;
664 }
665 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000666
667 SkRefPtr<SkPDFGraphicState> newGraphicState =
668 SkPDFGraphicState::getGraphicStateForPaint(newPaint);
669 newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
670 // newGraphicState has been canonicalized so we can directly compare
671 // pointers.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000672 if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000673 newGraphicState.get()) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000674 int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
675 if (resourceIndex < 0) {
676 resourceIndex = fGraphicStateResources.count();
677 fGraphicStateResources.push(newGraphicState.get());
678 newGraphicState->ref();
679 }
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000680 fContent.writeText("/G");
681 fContent.writeDecAsText(resourceIndex);
682 fContent.writeText(" gs\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000683 fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000684 }
685
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000686 if (forText) {
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000687 if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
688 newPaint.getTextScaleX()) {
689 SkScalar scale = newPaint.getTextScaleX();
690 SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000691 SkPDFScalar::Append(pdfScale, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000692 fContent.writeText(" Tz\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000693 fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
694 }
695
696 if (fGraphicStack[fGraphicStackIndex].fTextFill !=
697 newPaint.getStyle()) {
698 SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
699 SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
700 enum_must_match_value);
701 SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
702 enum_must_match_value);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000703 fContent.writeDecAsText(newPaint.getStyle());
704 fContent.writeText(" Tr\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000705 fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
706 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000707 }
708}
709
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000710void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000711 SkTypeface* typeface = paint.getTypeface();
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000712 if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
713 fGraphicStack[fGraphicStackIndex].fFont == NULL ||
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000714 fGraphicStack[fGraphicStackIndex].fFont->typeface() != typeface ||
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000715 !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000716 int fontIndex = getFontResourceIndex(typeface, glyphID);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000717 fContent.writeText("/F");
718 fContent.writeDecAsText(fontIndex);
719 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000720 SkPDFScalar::Append(paint.getTextSize(), &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000721 fContent.writeText(" Tf\n");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000722 fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
723 fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
724 }
725}
726
ctguil@chromium.org9db86bb2011-03-04 21:43:27 +0000727int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
728 SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000729 newFont->unref(); // getFontResource and SkRefPtr both took a ref.
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000730 int resourceIndex = fFontResources.find(newFont.get());
731 if (resourceIndex < 0) {
732 resourceIndex = fFontResources.count();
733 fFontResources.push(newFont.get());
734 newFont->ref();
735 }
736 return resourceIndex;
737}
738
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000739void SkPDFDevice::pushGS() {
740 SkASSERT(fGraphicStackIndex < 2);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000741 fContent.writeText("q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000742 fGraphicStackIndex++;
743 fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
744}
745
746void SkPDFDevice::popGS() {
747 SkASSERT(fGraphicStackIndex > 0);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000748 fContent.writeText("Q\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000749 fGraphicStackIndex--;
750}
751
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000752void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) {
753 // Flip the text about the x-axis to account for origin swap and include
754 // the passed parameters.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000755 fContent.writeText("1 0 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000756 SkPDFScalar::Append(0 - textSkewX, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000757 fContent.writeText(" -1 ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000758 SkPDFScalar::Append(x, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000759 fContent.writeText(" ");
vandebo@chromium.org094316b2011-03-04 03:15:13 +0000760 SkPDFScalar::Append(y, &fContent);
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000761 fContent.writeText(" Tm\n");
vandebo@chromium.org28be72b2010-11-11 21:37:00 +0000762}
763
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000764void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
765 const SkBitmap& bitmap,
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000766 const SkIRect* srcRect,
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000767 const SkPaint& paint) {
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000768 SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
769 if (srcRect && !subset.intersect(*srcRect))
770 return;
771
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000772 SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
773 if (!image)
774 return;
775
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000776 SkMatrix scaled;
777 // Adjust for origin flip.
778 scaled.setScale(1, -1);
779 scaled.postTranslate(0, 1);
780 // Scale the image up from 1x1 to WxH.
reed@google.coma6d59f62011-03-07 21:29:21 +0000781 scaled.postScale(SkIntToScalar(subset.width()),
782 SkIntToScalar(subset.height()));
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000783 scaled.postConcat(matrix);
784 SkMatrix curTransform = setTransform(scaled);
vandebo@chromium.org48543272011-02-08 19:28:07 +0000785 updateGSFromPaint(paint, false);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000786
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000787 fXObjectResources.push(image); // Transfer reference.
vandebo@chromium.orgcae5fba2011-03-28 19:03:50 +0000788 fContent.writeText("/X");
789 fContent.writeDecAsText(fXObjectResources.count() - 1);
790 fContent.writeText(" Do\n");
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000791 setTransform(curTransform);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000792}
793
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000794SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
795 SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
796 if (old == m)
797 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000798
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000799 if (old.getType() != SkMatrix::kIdentity_Mask) {
800 SkASSERT(fGraphicStackIndex > 0);
801 SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
802 SkMatrix::kIdentity_Mask);
803 SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
804 fGraphicStack[fGraphicStackIndex - 1].fClip);
805 popGS();
806 }
807 if (m.getType() == SkMatrix::kIdentity_Mask)
808 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000809
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000810 if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
811 fGraphicStack[fGraphicStackIndex - 1].fClip)
812 pushGS();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000813
vandebo@chromium.org75f97e42011-04-11 23:24:18 +0000814 SkPDFUtils::AppendTransform(m, &fContent);
vandebo@chromium.org7e2ff7c2010-11-03 23:55:28 +0000815 fGraphicStack[fGraphicStackIndex].fTransform = m;
816
817 return old;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000818}